From 89579b4317a7f7ab6ee706399bee4b8f25a12c3a Mon Sep 17 00:00:00 2001 From: Harald Welte Date: Fri, 4 Mar 2011 13:18:30 +0100 Subject: prefix sub-directories containing libraries with 'lib' ... and make sure tests work again after restructuring --- openbsc/configure.in | 14 +- openbsc/src/Makefile.am | 2 +- openbsc/src/abis/Makefile.am | 13 - openbsc/src/abis/e1_input.c | 649 ------ openbsc/src/abis/e1_input_vty.c | 102 - openbsc/src/abis/input/dahdi.c | 494 ----- openbsc/src/abis/input/ipaccess.c | 797 ------- openbsc/src/abis/input/lapd.c | 710 ------ openbsc/src/abis/input/lapd.h | 46 - openbsc/src/abis/input/misdn.c | 542 ----- openbsc/src/bsc/Makefile.am | 21 - openbsc/src/bsc/abis_nm.c | 3126 --------------------------- openbsc/src/bsc/abis_nm_ipaccess.c | 87 - openbsc/src/bsc/abis_nm_vty.c | 197 -- openbsc/src/bsc/abis_om2000.c | 878 -------- openbsc/src/bsc/abis_om2000_vty.c | 456 ---- openbsc/src/bsc/abis_rsl.c | 1960 ----------------- openbsc/src/bsc/bsc_api.c | 675 ------ openbsc/src/bsc/bsc_init.c | 440 ---- openbsc/src/bsc/bsc_msc.c | 259 --- openbsc/src/bsc/bsc_rll.c | 141 -- openbsc/src/bsc/bsc_vty.c | 2773 ------------------------ openbsc/src/bsc/bts_ericsson_rbs2000.c | 164 -- openbsc/src/bsc/bts_ipaccess_nanobts.c | 447 ---- openbsc/src/bsc/bts_siemens_bs11.c | 591 ----- openbsc/src/bsc/bts_unknown.c | 41 - openbsc/src/bsc/chan_alloc.c | 507 ----- openbsc/src/bsc/e1_config.c | 296 --- openbsc/src/bsc/gsm_04_08_utils.c | 655 ------ openbsc/src/bsc/gsm_subscriber_base.c | 149 -- openbsc/src/bsc/handover_decision.c | 297 --- openbsc/src/bsc/handover_logic.c | 393 ---- openbsc/src/bsc/meas_proc.c | 84 - openbsc/src/bsc/meas_rep.c | 116 - openbsc/src/bsc/paging.c | 395 ---- openbsc/src/bsc/rest_octets.c | 432 ---- openbsc/src/bsc/system_information.c | 606 ------ openbsc/src/bsc/transaction.c | 152 -- openbsc/src/common/Makefile.am | 7 - openbsc/src/common/bsc_version.c | 30 - openbsc/src/common/common_vty.c | 225 -- openbsc/src/common/debug.c | 249 --- openbsc/src/common/gsm_data.c | 589 ----- openbsc/src/common/socket.c | 108 - openbsc/src/common/talloc_ctx.c | 38 - openbsc/src/gb/Makefile.am | 9 - openbsc/src/gb/gprs_bssgp.c | 856 -------- openbsc/src/gb/gprs_bssgp_util.c | 119 - openbsc/src/gb/gprs_bssgp_vty.c | 176 -- openbsc/src/gb/gprs_ns.c | 992 --------- openbsc/src/gb/gprs_ns_frgre.c | 304 --- openbsc/src/gb/gprs_ns_vty.c | 569 ----- openbsc/src/gprs/Makefile.am | 7 +- openbsc/src/ipaccess/Makefile.am | 12 +- openbsc/src/libabis/Makefile.am | 13 + openbsc/src/libabis/e1_input.c | 649 ++++++ openbsc/src/libabis/e1_input_vty.c | 102 + openbsc/src/libabis/input/dahdi.c | 494 +++++ openbsc/src/libabis/input/ipaccess.c | 797 +++++++ openbsc/src/libabis/input/lapd.c | 710 ++++++ openbsc/src/libabis/input/lapd.h | 46 + openbsc/src/libabis/input/misdn.c | 542 +++++ openbsc/src/libbsc/Makefile.am | 21 + openbsc/src/libbsc/abis_nm.c | 3126 +++++++++++++++++++++++++++ openbsc/src/libbsc/abis_nm_ipaccess.c | 87 + openbsc/src/libbsc/abis_nm_vty.c | 197 ++ openbsc/src/libbsc/abis_om2000.c | 878 ++++++++ openbsc/src/libbsc/abis_om2000_vty.c | 456 ++++ openbsc/src/libbsc/abis_rsl.c | 1960 +++++++++++++++++ openbsc/src/libbsc/bsc_api.c | 675 ++++++ openbsc/src/libbsc/bsc_init.c | 440 ++++ openbsc/src/libbsc/bsc_msc.c | 259 +++ openbsc/src/libbsc/bsc_rll.c | 141 ++ openbsc/src/libbsc/bsc_vty.c | 2773 ++++++++++++++++++++++++ openbsc/src/libbsc/bts_ericsson_rbs2000.c | 164 ++ openbsc/src/libbsc/bts_ipaccess_nanobts.c | 447 ++++ openbsc/src/libbsc/bts_siemens_bs11.c | 591 +++++ openbsc/src/libbsc/bts_unknown.c | 41 + openbsc/src/libbsc/chan_alloc.c | 507 +++++ openbsc/src/libbsc/e1_config.c | 296 +++ openbsc/src/libbsc/gsm_04_08_utils.c | 655 ++++++ openbsc/src/libbsc/gsm_subscriber_base.c | 149 ++ openbsc/src/libbsc/handover_decision.c | 297 +++ openbsc/src/libbsc/handover_logic.c | 393 ++++ openbsc/src/libbsc/meas_proc.c | 84 + openbsc/src/libbsc/meas_rep.c | 116 + openbsc/src/libbsc/paging.c | 395 ++++ openbsc/src/libbsc/rest_octets.c | 432 ++++ openbsc/src/libbsc/system_information.c | 606 ++++++ openbsc/src/libbsc/transaction.c | 152 ++ openbsc/src/libcommon/Makefile.am | 7 + openbsc/src/libcommon/bsc_version.c | 30 + openbsc/src/libcommon/common_vty.c | 225 ++ openbsc/src/libcommon/debug.c | 249 +++ openbsc/src/libcommon/gsm_data.c | 589 +++++ openbsc/src/libcommon/socket.c | 108 + openbsc/src/libcommon/talloc_ctx.c | 38 + openbsc/src/libgb/Makefile.am | 9 + openbsc/src/libgb/gprs_bssgp.c | 856 ++++++++ openbsc/src/libgb/gprs_bssgp_util.c | 119 + openbsc/src/libgb/gprs_bssgp_vty.c | 176 ++ openbsc/src/libgb/gprs_ns.c | 992 +++++++++ openbsc/src/libgb/gprs_ns_frgre.c | 304 +++ openbsc/src/libgb/gprs_ns_vty.c | 569 +++++ openbsc/src/libmgcp/Makefile.am | 7 + openbsc/src/libmgcp/mgcp_network.c | 579 +++++ openbsc/src/libmgcp/mgcp_protocol.c | 1102 ++++++++++ openbsc/src/libmgcp/mgcp_vty.c | 742 +++++++ openbsc/src/libmsc/Makefile.am | 19 + openbsc/src/libmsc/auth.c | 132 ++ openbsc/src/libmsc/db.c | 1303 +++++++++++ openbsc/src/libmsc/gsm_04_08.c | 3345 +++++++++++++++++++++++++++++ openbsc/src/libmsc/gsm_04_11.c | 1240 +++++++++++ openbsc/src/libmsc/gsm_04_80.c | 175 ++ openbsc/src/libmsc/gsm_subscriber.c | 410 ++++ openbsc/src/libmsc/mncc.c | 110 + openbsc/src/libmsc/mncc_builtin.c | 411 ++++ openbsc/src/libmsc/mncc_sock.c | 337 +++ openbsc/src/libmsc/osmo_msc.c | 104 + openbsc/src/libmsc/rrlp.c | 105 + openbsc/src/libmsc/silent_call.c | 143 ++ openbsc/src/libmsc/sms_queue.c | 479 +++++ openbsc/src/libmsc/token_auth.c | 153 ++ openbsc/src/libmsc/ussd.c | 79 + openbsc/src/libmsc/vty_interface_layer3.c | 790 +++++++ openbsc/src/libtrau/Makefile.am | 7 + openbsc/src/libtrau/rtp_proxy.c | 728 +++++++ openbsc/src/libtrau/subchan_demux.c | 321 +++ openbsc/src/libtrau/trau_frame.c | 260 +++ openbsc/src/libtrau/trau_mux.c | 312 +++ openbsc/src/libtrau/trau_upqueue.c | 27 + openbsc/src/mgcp/Makefile.am | 7 - openbsc/src/mgcp/mgcp_network.c | 579 ----- openbsc/src/mgcp/mgcp_protocol.c | 1102 ---------- openbsc/src/mgcp/mgcp_vty.c | 742 ------- openbsc/src/msc/Makefile.am | 19 - openbsc/src/msc/auth.c | 132 -- openbsc/src/msc/db.c | 1303 ----------- openbsc/src/msc/gsm_04_08.c | 3345 ----------------------------- openbsc/src/msc/gsm_04_11.c | 1240 ----------- openbsc/src/msc/gsm_04_80.c | 175 -- openbsc/src/msc/gsm_subscriber.c | 410 ---- openbsc/src/msc/mncc.c | 110 - openbsc/src/msc/mncc_builtin.c | 411 ---- openbsc/src/msc/mncc_sock.c | 337 --- openbsc/src/msc/osmo_msc.c | 104 - openbsc/src/msc/rrlp.c | 105 - openbsc/src/msc/silent_call.c | 143 -- openbsc/src/msc/sms_queue.c | 479 ----- openbsc/src/msc/token_auth.c | 153 -- openbsc/src/msc/ussd.c | 79 - openbsc/src/msc/vty_interface_layer3.c | 790 ------- openbsc/src/osmo-bsc/Makefile.am | 8 +- openbsc/src/osmo-bsc_mgcp/Makefile.am | 5 +- openbsc/src/osmo-bsc_nat/Makefile.am | 8 +- openbsc/src/osmo-nitb/Makefile.am | 8 +- openbsc/src/trau/Makefile.am | 7 - openbsc/src/trau/rtp_proxy.c | 728 ------- openbsc/src/trau/subchan_demux.c | 321 --- openbsc/src/trau/trau_frame.c | 260 --- openbsc/src/trau/trau_mux.c | 312 --- openbsc/src/trau/trau_upqueue.c | 27 - openbsc/src/utils/Makefile.am | 4 +- openbsc/tests/channel/Makefile.am | 16 +- openbsc/tests/db/Makefile.am | 8 +- openbsc/tests/debug/Makefile.am | 5 +- openbsc/tests/gsm0408/Makefile.am | 5 +- openbsc/tests/mgcp/Makefile.am | 10 +- 168 files changed, 37450 insertions(+), 37426 deletions(-) delete mode 100644 openbsc/src/abis/Makefile.am delete mode 100644 openbsc/src/abis/e1_input.c delete mode 100644 openbsc/src/abis/e1_input_vty.c delete mode 100644 openbsc/src/abis/input/dahdi.c delete mode 100644 openbsc/src/abis/input/ipaccess.c delete mode 100644 openbsc/src/abis/input/lapd.c delete mode 100644 openbsc/src/abis/input/lapd.h delete mode 100644 openbsc/src/abis/input/misdn.c delete mode 100644 openbsc/src/bsc/Makefile.am delete mode 100644 openbsc/src/bsc/abis_nm.c delete mode 100644 openbsc/src/bsc/abis_nm_ipaccess.c delete mode 100644 openbsc/src/bsc/abis_nm_vty.c delete mode 100644 openbsc/src/bsc/abis_om2000.c delete mode 100644 openbsc/src/bsc/abis_om2000_vty.c delete mode 100644 openbsc/src/bsc/abis_rsl.c delete mode 100644 openbsc/src/bsc/bsc_api.c delete mode 100644 openbsc/src/bsc/bsc_init.c delete mode 100644 openbsc/src/bsc/bsc_msc.c delete mode 100644 openbsc/src/bsc/bsc_rll.c delete mode 100644 openbsc/src/bsc/bsc_vty.c delete mode 100644 openbsc/src/bsc/bts_ericsson_rbs2000.c delete mode 100644 openbsc/src/bsc/bts_ipaccess_nanobts.c delete mode 100644 openbsc/src/bsc/bts_siemens_bs11.c delete mode 100644 openbsc/src/bsc/bts_unknown.c delete mode 100644 openbsc/src/bsc/chan_alloc.c delete mode 100644 openbsc/src/bsc/e1_config.c delete mode 100644 openbsc/src/bsc/gsm_04_08_utils.c delete mode 100644 openbsc/src/bsc/gsm_subscriber_base.c delete mode 100644 openbsc/src/bsc/handover_decision.c delete mode 100644 openbsc/src/bsc/handover_logic.c delete mode 100644 openbsc/src/bsc/meas_proc.c delete mode 100644 openbsc/src/bsc/meas_rep.c delete mode 100644 openbsc/src/bsc/paging.c delete mode 100644 openbsc/src/bsc/rest_octets.c delete mode 100644 openbsc/src/bsc/system_information.c delete mode 100644 openbsc/src/bsc/transaction.c delete mode 100644 openbsc/src/common/Makefile.am delete mode 100644 openbsc/src/common/bsc_version.c delete mode 100644 openbsc/src/common/common_vty.c delete mode 100644 openbsc/src/common/debug.c delete mode 100644 openbsc/src/common/gsm_data.c delete mode 100644 openbsc/src/common/socket.c delete mode 100644 openbsc/src/common/talloc_ctx.c delete mode 100644 openbsc/src/gb/Makefile.am delete mode 100644 openbsc/src/gb/gprs_bssgp.c delete mode 100644 openbsc/src/gb/gprs_bssgp_util.c delete mode 100644 openbsc/src/gb/gprs_bssgp_vty.c delete mode 100644 openbsc/src/gb/gprs_ns.c delete mode 100644 openbsc/src/gb/gprs_ns_frgre.c delete mode 100644 openbsc/src/gb/gprs_ns_vty.c create mode 100644 openbsc/src/libabis/Makefile.am create mode 100644 openbsc/src/libabis/e1_input.c create mode 100644 openbsc/src/libabis/e1_input_vty.c create mode 100644 openbsc/src/libabis/input/dahdi.c create mode 100644 openbsc/src/libabis/input/ipaccess.c create mode 100644 openbsc/src/libabis/input/lapd.c create mode 100644 openbsc/src/libabis/input/lapd.h create mode 100644 openbsc/src/libabis/input/misdn.c create mode 100644 openbsc/src/libbsc/Makefile.am create mode 100644 openbsc/src/libbsc/abis_nm.c create mode 100644 openbsc/src/libbsc/abis_nm_ipaccess.c create mode 100644 openbsc/src/libbsc/abis_nm_vty.c create mode 100644 openbsc/src/libbsc/abis_om2000.c create mode 100644 openbsc/src/libbsc/abis_om2000_vty.c create mode 100644 openbsc/src/libbsc/abis_rsl.c create mode 100644 openbsc/src/libbsc/bsc_api.c create mode 100644 openbsc/src/libbsc/bsc_init.c create mode 100644 openbsc/src/libbsc/bsc_msc.c create mode 100644 openbsc/src/libbsc/bsc_rll.c create mode 100644 openbsc/src/libbsc/bsc_vty.c create mode 100644 openbsc/src/libbsc/bts_ericsson_rbs2000.c create mode 100644 openbsc/src/libbsc/bts_ipaccess_nanobts.c create mode 100644 openbsc/src/libbsc/bts_siemens_bs11.c create mode 100644 openbsc/src/libbsc/bts_unknown.c create mode 100644 openbsc/src/libbsc/chan_alloc.c create mode 100644 openbsc/src/libbsc/e1_config.c create mode 100644 openbsc/src/libbsc/gsm_04_08_utils.c create mode 100644 openbsc/src/libbsc/gsm_subscriber_base.c create mode 100644 openbsc/src/libbsc/handover_decision.c create mode 100644 openbsc/src/libbsc/handover_logic.c create mode 100644 openbsc/src/libbsc/meas_proc.c create mode 100644 openbsc/src/libbsc/meas_rep.c create mode 100644 openbsc/src/libbsc/paging.c create mode 100644 openbsc/src/libbsc/rest_octets.c create mode 100644 openbsc/src/libbsc/system_information.c create mode 100644 openbsc/src/libbsc/transaction.c create mode 100644 openbsc/src/libcommon/Makefile.am create mode 100644 openbsc/src/libcommon/bsc_version.c create mode 100644 openbsc/src/libcommon/common_vty.c create mode 100644 openbsc/src/libcommon/debug.c create mode 100644 openbsc/src/libcommon/gsm_data.c create mode 100644 openbsc/src/libcommon/socket.c create mode 100644 openbsc/src/libcommon/talloc_ctx.c create mode 100644 openbsc/src/libgb/Makefile.am create mode 100644 openbsc/src/libgb/gprs_bssgp.c create mode 100644 openbsc/src/libgb/gprs_bssgp_util.c create mode 100644 openbsc/src/libgb/gprs_bssgp_vty.c create mode 100644 openbsc/src/libgb/gprs_ns.c create mode 100644 openbsc/src/libgb/gprs_ns_frgre.c create mode 100644 openbsc/src/libgb/gprs_ns_vty.c create mode 100644 openbsc/src/libmgcp/Makefile.am create mode 100644 openbsc/src/libmgcp/mgcp_network.c create mode 100644 openbsc/src/libmgcp/mgcp_protocol.c create mode 100644 openbsc/src/libmgcp/mgcp_vty.c create mode 100644 openbsc/src/libmsc/Makefile.am create mode 100644 openbsc/src/libmsc/auth.c create mode 100644 openbsc/src/libmsc/db.c create mode 100644 openbsc/src/libmsc/gsm_04_08.c create mode 100644 openbsc/src/libmsc/gsm_04_11.c create mode 100644 openbsc/src/libmsc/gsm_04_80.c create mode 100644 openbsc/src/libmsc/gsm_subscriber.c create mode 100644 openbsc/src/libmsc/mncc.c create mode 100644 openbsc/src/libmsc/mncc_builtin.c create mode 100644 openbsc/src/libmsc/mncc_sock.c create mode 100644 openbsc/src/libmsc/osmo_msc.c create mode 100644 openbsc/src/libmsc/rrlp.c create mode 100644 openbsc/src/libmsc/silent_call.c create mode 100644 openbsc/src/libmsc/sms_queue.c create mode 100644 openbsc/src/libmsc/token_auth.c create mode 100644 openbsc/src/libmsc/ussd.c create mode 100644 openbsc/src/libmsc/vty_interface_layer3.c create mode 100644 openbsc/src/libtrau/Makefile.am create mode 100644 openbsc/src/libtrau/rtp_proxy.c create mode 100644 openbsc/src/libtrau/subchan_demux.c create mode 100644 openbsc/src/libtrau/trau_frame.c create mode 100644 openbsc/src/libtrau/trau_mux.c create mode 100644 openbsc/src/libtrau/trau_upqueue.c delete mode 100644 openbsc/src/mgcp/Makefile.am delete mode 100644 openbsc/src/mgcp/mgcp_network.c delete mode 100644 openbsc/src/mgcp/mgcp_protocol.c delete mode 100644 openbsc/src/mgcp/mgcp_vty.c delete mode 100644 openbsc/src/msc/Makefile.am delete mode 100644 openbsc/src/msc/auth.c delete mode 100644 openbsc/src/msc/db.c delete mode 100644 openbsc/src/msc/gsm_04_08.c delete mode 100644 openbsc/src/msc/gsm_04_11.c delete mode 100644 openbsc/src/msc/gsm_04_80.c delete mode 100644 openbsc/src/msc/gsm_subscriber.c delete mode 100644 openbsc/src/msc/mncc.c delete mode 100644 openbsc/src/msc/mncc_builtin.c delete mode 100644 openbsc/src/msc/mncc_sock.c delete mode 100644 openbsc/src/msc/osmo_msc.c delete mode 100644 openbsc/src/msc/rrlp.c delete mode 100644 openbsc/src/msc/silent_call.c delete mode 100644 openbsc/src/msc/sms_queue.c delete mode 100644 openbsc/src/msc/token_auth.c delete mode 100644 openbsc/src/msc/ussd.c delete mode 100644 openbsc/src/msc/vty_interface_layer3.c delete mode 100644 openbsc/src/trau/Makefile.am delete mode 100644 openbsc/src/trau/rtp_proxy.c delete mode 100644 openbsc/src/trau/subchan_demux.c delete mode 100644 openbsc/src/trau/trau_frame.c delete mode 100644 openbsc/src/trau/trau_mux.c delete mode 100644 openbsc/src/trau/trau_upqueue.c diff --git a/openbsc/configure.in b/openbsc/configure.in index aec490d51..8c0c728c1 100644 --- a/openbsc/configure.in +++ b/openbsc/configure.in @@ -87,19 +87,19 @@ AC_OUTPUT( include/openbsc/Makefile include/Makefile src/Makefile - src/trau/Makefile - src/abis/Makefile - src/bsc/Makefile - src/msc/Makefile - src/mgcp/Makefile - src/common/Makefile + src/libtrau/Makefile + src/libabis/Makefile + src/libbsc/Makefile + src/libmsc/Makefile + src/libmgcp/Makefile + src/libcommon/Makefile src/osmo-nitb/Makefile src/osmo-bsc/Makefile src/osmo-bsc_nat/Makefile src/osmo-bsc_mgcp/Makefile src/ipaccess/Makefile src/utils/Makefile - src/gb/Makefile + src/libgb/Makefile src/gprs/Makefile tests/Makefile tests/debug/Makefile diff --git a/openbsc/src/Makefile.am b/openbsc/src/Makefile.am index 489dc58b9..005ce6bb4 100644 --- a/openbsc/src/Makefile.am +++ b/openbsc/src/Makefile.am @@ -2,7 +2,7 @@ INCLUDES = $(all_includes) -I$(top_srcdir)/include -I$(top_builddir) AM_CFLAGS=-Wall $(LIBOSMOCORE_CFLAGS) $(LIBOSMOVTY_CFLAGS) $(COVERAGE_CFLAGS) AM_LDFLAGS = $(LIBOSMOCORE_LIBS) $(COVERAGE_LDFLAGS) -SUBDIRS = common abis mgcp bsc trau osmo-nitb osmo-bsc_mgcp utils ipaccess gb gprs +SUBDIRS = libcommon libabis libmgcp libbsc libtrau osmo-nitb osmo-bsc_mgcp utils ipaccess libgb gprs # Conditional modules if BUILD_NAT diff --git a/openbsc/src/abis/Makefile.am b/openbsc/src/abis/Makefile.am deleted file mode 100644 index 7f5ac47cc..000000000 --- a/openbsc/src/abis/Makefile.am +++ /dev/null @@ -1,13 +0,0 @@ -INCLUDES = $(all_includes) -I$(top_srcdir)/include -I$(top_builddir) -AM_CFLAGS=-Wall $(LIBOSMOCORE_CFLAGS) $(LIBOSMOVTY_CFLAGS) $(COVERAGE_CFLAGS) -AM_LDFLAGS = $(LIBOSMOCORE_LIBS) $(COVERAGE_LDFLAGS) - -noinst_LIBRARIES = libabis.a - -libabis_a_SOURCES = e1_input.c e1_input_vty.c \ - input/misdn.c \ - input/ipaccess.c \ - input/dahdi.c \ - input/lapd.c - -EXTRA_DIST = input/lapd.h diff --git a/openbsc/src/abis/e1_input.c b/openbsc/src/abis/e1_input.c deleted file mode 100644 index e5dd11b94..000000000 --- a/openbsc/src/abis/e1_input.c +++ /dev/null @@ -1,649 +0,0 @@ -/* OpenBSC Abis interface to E1 */ - -/* (C) 2008-2009 by Harald Welte - * - * All Rights Reserved - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation; either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - * - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -//#define AF_COMPATIBILITY_FUNC -//#include -#ifndef AF_ISDN -#define AF_ISDN 34 -#define PF_ISDN AF_ISDN -#endif - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "../bscconfig.h" - -#define NUM_E1_TS 32 - -/* list of all E1 drivers */ -LLIST_HEAD(e1inp_driver_list); - -/* list of all E1 lines */ -LLIST_HEAD(e1inp_line_list); - -static void *tall_sigl_ctx; - -/* - * pcap writing of the misdn load - * pcap format is from http://wiki.wireshark.org/Development/LibpcapFileFormat - */ -#define DLT_LINUX_LAPD 177 -#define PCAP_INPUT 0 -#define PCAP_OUTPUT 1 - -struct pcap_hdr { - u_int32_t magic_number; - u_int16_t version_major; - u_int16_t version_minor; - int32_t thiszone; - u_int32_t sigfigs; - u_int32_t snaplen; - u_int32_t network; -} __attribute__((packed)); - -struct pcaprec_hdr { - u_int32_t ts_sec; - u_int32_t ts_usec; - u_int32_t incl_len; - u_int32_t orig_len; -} __attribute__((packed)); - -struct fake_linux_lapd_header { - u_int16_t pkttype; - u_int16_t hatype; - u_int16_t halen; - u_int64_t addr; - int16_t protocol; -} __attribute__((packed)); - -struct lapd_header { - u_int8_t ea1 : 1; - u_int8_t cr : 1; - u_int8_t sapi : 6; - u_int8_t ea2 : 1; - u_int8_t tei : 7; - u_int8_t control_foo; /* fake UM's ... */ -} __attribute__((packed)); - -static_assert(offsetof(struct fake_linux_lapd_header, hatype) == 2, hatype_offset); -static_assert(offsetof(struct fake_linux_lapd_header, halen) == 4, halen_offset); -static_assert(offsetof(struct fake_linux_lapd_header, addr) == 6, addr_offset); -static_assert(offsetof(struct fake_linux_lapd_header, protocol) == 14, proto_offset); -static_assert(sizeof(struct fake_linux_lapd_header) == 16, lapd_header_size); - - -static int pcap_fd = -1; - -void e1_set_pcap_fd(int fd) -{ - int ret; - struct pcap_hdr header = { - .magic_number = 0xa1b2c3d4, - .version_major = 2, - .version_minor = 4, - .thiszone = 0, - .sigfigs = 0, - .snaplen = 65535, - .network = DLT_LINUX_LAPD, - }; - - pcap_fd = fd; - ret = write(pcap_fd, &header, sizeof(header)); -} - -/* This currently only works for the D-Channel */ -static void write_pcap_packet(int direction, int sapi, int tei, - struct msgb *msg) { - if (pcap_fd < 0) - return; - - int ret; - time_t cur_time; - struct tm *tm; - - struct fake_linux_lapd_header header = { - .pkttype = 4, - .hatype = 0, - .halen = 0, - .addr = direction == PCAP_OUTPUT ? 0x0 : 0x1, - .protocol = ntohs(48), - }; - - struct lapd_header lapd_header = { - .ea1 = 0, - .cr = direction == PCAP_OUTPUT ? 1 : 0, - .sapi = sapi & 0x3F, - .ea2 = 1, - .tei = tei & 0x7F, - .control_foo = 0x03 /* UI */, - }; - - struct pcaprec_hdr payload_header = { - .ts_sec = 0, - .ts_usec = 0, - .incl_len = msgb_l2len(msg) + sizeof(struct fake_linux_lapd_header) - + sizeof(struct lapd_header), - .orig_len = msgb_l2len(msg) + sizeof(struct fake_linux_lapd_header) - + sizeof(struct lapd_header), - }; - - - cur_time = time(NULL); - tm = localtime(&cur_time); - payload_header.ts_sec = mktime(tm); - - ret = write(pcap_fd, &payload_header, sizeof(payload_header)); - ret = write(pcap_fd, &header, sizeof(header)); - ret = write(pcap_fd, &lapd_header, sizeof(lapd_header)); - ret = write(pcap_fd, msg->l2h, msgb_l2len(msg)); -} - -static const char *sign_types[] = { - [E1INP_SIGN_NONE] = "None", - [E1INP_SIGN_OML] = "OML", - [E1INP_SIGN_RSL] = "RSL", -}; -const char *e1inp_signtype_name(enum e1inp_sign_type tp) -{ - if (tp >= ARRAY_SIZE(sign_types)) - return "undefined"; - return sign_types[tp]; -} - -static const char *ts_types[] = { - [E1INP_TS_TYPE_NONE] = "None", - [E1INP_TS_TYPE_SIGN] = "Signalling", - [E1INP_TS_TYPE_TRAU] = "TRAU", -}; - -const char *e1inp_tstype_name(enum e1inp_ts_type tp) -{ - if (tp >= ARRAY_SIZE(ts_types)) - return "undefined"; - return ts_types[tp]; -} - -/* callback when a TRAU frame was received */ -static int subch_cb(struct subch_demux *dmx, int ch, u_int8_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); -} - -int abis_rsl_sendmsg(struct msgb *msg) -{ - struct e1inp_sign_link *sign_link; - struct e1inp_driver *e1inp_driver; - struct e1inp_ts *e1i_ts; - - msg->l2h = msg->data; - - if (!msg->trx) { - LOGP(DRSL, LOGL_ERROR, "rsl_sendmsg: msg->trx == NULL: %s\n", - hexdump(msg->data, msg->len)); - talloc_free(msg); - return -EINVAL; - } else if (!msg->trx->rsl_link) { - LOGP(DRSL, LOGL_ERROR, "rsl_sendmsg: msg->trx->rsl_link == NULL: %s\n", - hexdump(msg->data, msg->len)); - talloc_free(msg); - return -EIO; - } - - sign_link = msg->trx->rsl_link; - e1i_ts = sign_link->ts; - if (!bsc_timer_pending(&e1i_ts->sign.tx_timer)) { - /* notify the driver we have something to write */ - e1inp_driver = sign_link->ts->line->driver; - e1inp_driver->want_write(e1i_ts); - } - msgb_enqueue(&sign_link->tx_list, msg); - - /* dump it */ - write_pcap_packet(PCAP_OUTPUT, sign_link->sapi, sign_link->tei, msg); - - return 0; -} - -int _abis_nm_sendmsg(struct msgb *msg, int to_trx_oml) -{ - struct e1inp_sign_link *sign_link; - struct e1inp_driver *e1inp_driver; - struct e1inp_ts *e1i_ts; - - msg->l2h = msg->data; - - if (!msg->trx || !msg->trx->bts || !msg->trx->bts->oml_link) { - LOGP(DNM, LOGL_ERROR, "nm_sendmsg: msg->trx == NULL\n"); - return -EINVAL; - } - - /* Check for TRX-specific OML link first */ - if (to_trx_oml) { - if (!msg->trx->oml_link) - return -ENODEV; - sign_link = msg->trx->oml_link; - } else - sign_link = msg->trx->bts->oml_link; - - e1i_ts = sign_link->ts; - if (!bsc_timer_pending(&e1i_ts->sign.tx_timer)) { - /* notify the driver we have something to write */ - e1inp_driver = sign_link->ts->line->driver; - e1inp_driver->want_write(e1i_ts); - } - msgb_enqueue(&sign_link->tx_list, msg); - - /* dump it */ - write_pcap_packet(PCAP_OUTPUT, sign_link->sapi, sign_link->tei, msg); - - return 0; -} - -/* Timeslot */ - -/* configure and initialize one e1inp_ts */ -int e1inp_ts_config(struct e1inp_ts *ts, struct e1inp_line *line, - enum e1inp_ts_type type) -{ - if (ts->type == type && ts->line && line) - return 0; - - ts->type = type; - ts->line = line; - - switch (type) { - case E1INP_TS_TYPE_SIGN: - if (line && line->driver) - ts->sign.delay = line->driver->default_delay; - else - ts->sign.delay = 100000; - INIT_LLIST_HEAD(&ts->sign.sign_links); - break; - case E1INP_TS_TYPE_TRAU: - subchan_mux_init(&ts->trau.mux); - ts->trau.demux.out_cb = subch_cb; - ts->trau.demux.data = ts; - subch_demux_init(&ts->trau.demux); - break; - default: - LOGP(DMI, LOGL_ERROR, "unsupported E1 timeslot type %u\n", - ts->type); - return -EINVAL; - } - return 0; -} - -struct e1inp_line *e1inp_line_get(u_int8_t e1_nr) -{ - struct e1inp_line *e1i_line; - - /* iterate over global list of e1 lines */ - llist_for_each_entry(e1i_line, &e1inp_line_list, list) { - if (e1i_line->num == e1_nr) - return e1i_line; - } - return NULL; -} - -struct e1inp_line *e1inp_line_create(u_int8_t e1_nr, const char *driver_name) -{ - struct e1inp_driver *driver; - struct e1inp_line *line; - int i; - - line = e1inp_line_get(e1_nr); - if (line) { - LOGP(DINP, LOGL_ERROR, "E1 Line %u already exists\n", - e1_nr); - return NULL; - } - - driver = e1inp_driver_find(driver_name); - if (!driver) { - LOGP(DINP, LOGL_ERROR, "No such E1 driver '%s'\n", - driver_name); - return NULL; - } - - line = talloc_zero(tall_bsc_ctx, struct e1inp_line); - if (!line) - return NULL; - - line->driver = driver; - - line->num = e1_nr; - for (i = 0; i < NUM_E1_TS; i++) { - line->ts[i].num = i+1; - line->ts[i].line = line; - } - llist_add_tail(&line->list, &e1inp_line_list); - - return line; -} - -#if 0 -struct e1inp_line *e1inp_line_get_create(u_int8_t e1_nr) -{ - struct e1inp_line *line; - int i; - - line = e1inp_line_get(e1_nr); - if (line) - return line; - - line = talloc_zero(tall_bsc_ctx, struct e1inp_line); - if (!line) - return NULL; - - line->num = e1_nr; - for (i = 0; i < NUM_E1_TS; i++) { - line->ts[i].num = i+1; - line->ts[i].line = line; - } - llist_add_tail(&line->list, &e1inp_line_list); - - return line; -} -#endif - -static struct e1inp_ts *e1inp_ts_get(u_int8_t e1_nr, u_int8_t ts_nr) -{ - struct e1inp_line *e1i_line; - - e1i_line = e1inp_line_get(e1_nr); - if (!e1i_line) - return NULL; - - return &e1i_line->ts[ts_nr-1]; -} - -struct subch_mux *e1inp_get_mux(u_int8_t e1_nr, u_int8_t ts_nr) -{ - struct e1inp_ts *e1i_ts = e1inp_ts_get(e1_nr, ts_nr); - - if (!e1i_ts) - return NULL; - - return &e1i_ts->trau.mux; -} - -/* Signalling Link */ - -struct e1inp_sign_link *e1inp_lookup_sign_link(struct e1inp_ts *e1i, - u_int8_t tei, u_int8_t sapi) -{ - struct e1inp_sign_link *link; - - llist_for_each_entry(link, &e1i->sign.sign_links, list) { - if (link->sapi == sapi && link->tei == tei) - return link; - } - - return NULL; -} - -/* create a new signalling link in a E1 timeslot */ - -struct e1inp_sign_link * -e1inp_sign_link_create(struct e1inp_ts *ts, enum e1inp_sign_type type, - struct gsm_bts_trx *trx, u_int8_t tei, - u_int8_t sapi) -{ - struct e1inp_sign_link *link; - - if (ts->type != E1INP_TS_TYPE_SIGN) - return NULL; - - link = talloc_zero(tall_sigl_ctx, struct e1inp_sign_link); - if (!link) - return NULL; - - link->ts = ts; - link->type = type; - INIT_LLIST_HEAD(&link->tx_list); - link->trx = trx; - link->tei = tei; - link->sapi = sapi; - - llist_add_tail(&link->list, &ts->sign.sign_links); - - return link; -} - -void e1inp_sign_link_destroy(struct e1inp_sign_link *link) -{ - struct msgb *msg; - - llist_del(&link->list); - while (!llist_empty(&link->tx_list)) { - msg = msgb_dequeue(&link->tx_list); - msgb_free(msg); - } - - if (link->ts->type == E1INP_TS_TYPE_SIGN) - bsc_del_timer(&link->ts->sign.tx_timer); - - talloc_free(link); -} - -/* the E1 driver tells us he has received something on a TS */ -int e1inp_rx_ts(struct e1inp_ts *ts, struct msgb *msg, - u_int8_t tei, u_int8_t sapi) -{ - struct e1inp_sign_link *link; - struct gsm_bts *bts; - int ret; - - switch (ts->type) { - case E1INP_TS_TYPE_SIGN: - /* consult the list of signalling links */ - write_pcap_packet(PCAP_INPUT, sapi, tei, msg); - link = e1inp_lookup_sign_link(ts, tei, sapi); - if (!link) { - LOGP(DMI, LOGL_ERROR, "didn't find signalling link for " - "tei %d, sapi %d\n", tei, sapi); - return -EINVAL; - } - - log_set_context(BSC_CTX_BTS, link->trx->bts); - switch (link->type) { - case E1INP_SIGN_OML: - msg->trx = link->trx; - bts = msg->trx->bts; - ret = bts->model->oml_rcvmsg(msg); - break; - case E1INP_SIGN_RSL: - msg->trx = link->trx; - ret = abis_rsl_rcvmsg(msg); - break; - default: - ret = -EINVAL; - LOGP(DMI, LOGL_ERROR, "unknown link type %u\n", link->type); - break; - } - break; - case E1INP_TS_TYPE_TRAU: - ret = subch_demux_in(&ts->trau.demux, msg->l2h, msgb_l2len(msg)); - break; - default: - ret = -EINVAL; - LOGP(DMI, LOGL_ERROR, "unknown TS type %u\n", ts->type); - break; - } - - return ret; -} - -#define TSX_ALLOC_SIZE 4096 - -/* called by driver if it wants to transmit on a given TS */ -struct msgb *e1inp_tx_ts(struct e1inp_ts *e1i_ts, - struct e1inp_sign_link **sign_link) -{ - struct e1inp_sign_link *link; - struct msgb *msg = NULL; - int len; - - switch (e1i_ts->type) { - case E1INP_TS_TYPE_SIGN: - /* FIXME: implement this round robin */ - llist_for_each_entry(link, &e1i_ts->sign.sign_links, list) { - msg = msgb_dequeue(&link->tx_list); - if (msg) { - if (sign_link) - *sign_link = link; - break; - } - } - break; - case E1INP_TS_TYPE_TRAU: - msg = msgb_alloc(TSX_ALLOC_SIZE, "TRAU_TX"); - if (!msg) - return NULL; - len = subchan_mux_out(&e1i_ts->trau.mux, msg->data, 40); - msgb_put(msg, 40); - break; - default: - LOGP(DMI, LOGL_ERROR, "unsupported E1 TS type %u\n", e1i_ts->type); - return NULL; - } - return msg; -} - -/* called by driver in case some kind of link state event */ -int e1inp_event(struct e1inp_ts *ts, int evt, u_int8_t tei, u_int8_t sapi) -{ - struct e1inp_sign_link *link; - struct input_signal_data isd; - - link = e1inp_lookup_sign_link(ts, tei, sapi); - if (!link) - return -EINVAL; - - isd.link_type = link->type; - isd.trx = link->trx; - isd.tei = tei; - isd.sapi = sapi; - - /* report further upwards */ - dispatch_signal(SS_INPUT, evt, &isd); - return 0; -} - -/* register a driver with the E1 core */ -int e1inp_driver_register(struct e1inp_driver *drv) -{ - llist_add_tail(&drv->list, &e1inp_driver_list); - return 0; -} - -struct e1inp_driver *e1inp_driver_find(const char *name) -{ - struct e1inp_driver *drv; - - llist_for_each_entry(drv, &e1inp_driver_list, list) { - if (!strcasecmp(name, drv->name)) - return drv; - } - return NULL; -} - -int e1inp_line_update(struct e1inp_line *line) -{ - struct input_signal_data isd; - int rc; - - if (line->driver && line->driver->line_update) - rc = line->driver->line_update(line); - else - rc = 0; - - /* Send a signal to anyone who is interested in new lines being - * configured */ - memset(&isd, 0, sizeof(isd)); - isd.line = line; - dispatch_signal(SS_INPUT, S_INP_LINE_INIT, &isd); - - return rc; -} - -static int e1i_sig_cb(unsigned int subsys, unsigned int signal, - void *handler_data, void *signal_data) -{ - if (subsys != SS_GLOBAL || - signal != S_GLOBAL_SHUTDOWN) - return 0; - - if (pcap_fd) { - close(pcap_fd); - pcap_fd = -1; - } - - return 0; -} - -void e1inp_misdn_init(void); -void e1inp_dahdi_init(void); - -void e1inp_init(void) -{ - tall_sigl_ctx = talloc_named_const(tall_bsc_ctx, 1, - "e1inp_sign_link"); - register_signal_handler(SS_GLOBAL, e1i_sig_cb, NULL); - - e1inp_misdn_init(); -#ifdef HAVE_DAHDI_USER_H - e1inp_dahdi_init(); -#endif -} diff --git a/openbsc/src/abis/e1_input_vty.c b/openbsc/src/abis/e1_input_vty.c deleted file mode 100644 index 3909f652f..000000000 --- a/openbsc/src/abis/e1_input_vty.c +++ /dev/null @@ -1,102 +0,0 @@ -/* OpenBSC E1 vty interface */ -/* (C) 2011 by Harald Welte - * All Rights Reserved - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation; either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - * - */ - -#include -#include -#include - -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include - -#include "../bscconfig.h" - -#define E1_DRIVER_NAMES "(misdn|dahdi)" -#define E1_DRIVER_HELP "mISDN supported E1 Card\n" \ - "DAHDI supported E1/T1/J1 Card\n" - -DEFUN(cfg_e1line_driver, cfg_e1_line_driver_cmd, - "e1_line <0-255> driver " E1_DRIVER_NAMES, - "Configure E1/T1/J1 Line\n" "Line Number\n" "Set driver for this line\n" - E1_DRIVER_HELP) -{ - struct e1inp_line *line; - int e1_nr = atoi(argv[0]); - - line = e1inp_line_get(e1_nr); - if (line) { - vty_out(vty, "%% Line %d already exists%s", e1_nr, VTY_NEWLINE); - return CMD_WARNING; - } - line = e1inp_line_create(e1_nr, argv[1]); - if (!line) { - vty_out(vty, "%% Error creating line %d%s", e1_nr, VTY_NEWLINE); - return CMD_WARNING; - } - - return CMD_SUCCESS; -} - -DEFUN(cfg_e1inp, cfg_e1inp_cmd, - "e1_input", - "Configure E1/T1/J1 TDM input\n") -{ - vty->node = E1INP_NODE; - - return CMD_SUCCESS; -} - -static int e1inp_config_write(struct vty *vty) -{ - struct e1inp_line *line; - - vty_out(vty, "e1_input%s", VTY_NEWLINE); - - llist_for_each_entry(line, &e1inp_line_list, list) { - vty_out(vty, " e1_line %u driver %s%s", line->num, - line->driver->name, VTY_NEWLINE); - } - return CMD_SUCCESS; -} - -struct cmd_node e1inp_node = { - E1INP_NODE, - "%s(e1_input)#", - 1, -}; - -int e1inp_vty_init(void) -{ - install_element(CONFIG_NODE, &cfg_e1inp_cmd); - install_node(&e1inp_node, e1inp_config_write); - install_element(E1INP_NODE, &cfg_e1_line_driver_cmd); - - return 0; -} diff --git a/openbsc/src/abis/input/dahdi.c b/openbsc/src/abis/input/dahdi.c deleted file mode 100644 index 2fc99f58e..000000000 --- a/openbsc/src/abis/input/dahdi.c +++ /dev/null @@ -1,494 +0,0 @@ -/* OpenBSC Abis input driver for DAHDI */ - -/* (C) 2008-2011 by Harald Welte - * (C) 2009 by Holger Hans Peter Freyther - * (C) 2010 by Digium and Matthew Fredrickson - * - * 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 "../../bscconfig.h" - -#ifdef HAVE_DAHDI_USER_H - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "lapd.h" - -#define TS1_ALLOC_SIZE 300 - -/* Corresponds to dahdi/user.h, only PRI related events */ -static const struct value_string dahdi_evt_names[] = { - { DAHDI_EVENT_NONE, "NONE" }, - { DAHDI_EVENT_ALARM, "ALARM" }, - { DAHDI_EVENT_NOALARM, "NOALARM" }, - { DAHDI_EVENT_ABORT, "HDLC ABORT" }, - { DAHDI_EVENT_OVERRUN, "HDLC OVERRUN" }, - { DAHDI_EVENT_BADFCS, "HDLC BAD FCS" }, - { DAHDI_EVENT_REMOVED, "REMOVED" }, - { 0, NULL } -}; - -static void handle_dahdi_exception(struct e1inp_ts *ts) -{ - int rc, evt; - struct input_signal_data isd; - - rc = ioctl(ts->driver.dahdi.fd.fd, DAHDI_GETEVENT, &evt); - if (rc < 0) - return; - - LOGP(DMI, LOGL_NOTICE, "Line %u(%s) / TS %u DAHDI EVENT %s\n", - ts->line->num, ts->line->name, ts->num, - get_value_string(dahdi_evt_names, evt)); - - isd.line = ts->line; - - switch (evt) { - case DAHDI_EVENT_ALARM: - /* we should notify the code that the line is gone */ - dispatch_signal(SS_INPUT, S_INP_LINE_ALARM, &isd); - break; - case DAHDI_EVENT_NOALARM: - /* alarm has gone, we should re-start the SABM requests */ - dispatch_signal(SS_INPUT, S_INP_LINE_NOALARM, &isd); - break; - } -} - -static int handle_ts1_read(struct bsc_fd *bfd) -{ - struct e1inp_line *line = bfd->data; - unsigned int ts_nr = bfd->priv_nr; - struct e1inp_ts *e1i_ts = &line->ts[ts_nr-1]; - struct msgb *msg = msgb_alloc(TS1_ALLOC_SIZE, "DAHDI TS1"); - lapd_mph_type prim; - unsigned int sapi, tei; - int ilen, ret; - uint8_t *idata; - - if (!msg) - return -ENOMEM; - - ret = read(bfd->fd, msg->data, TS1_ALLOC_SIZE - 16); - if (ret == -1) - handle_dahdi_exception(e1i_ts); - else if (ret < 0) { - perror("read "); - } - msgb_put(msg, ret - 2); - if (ret <= 3) { - perror("read "); - } - - sapi = msg->data[0] >> 2; - tei = msg->data[1] >> 1; - - DEBUGP(DMI, "<= len = %d, sapi(%d) tei(%d)", ret, sapi, tei); - - idata = lapd_receive(e1i_ts->driver.dahdi.lapd, msg->data, msg->len, &ilen, &prim); - if (!idata && prim == 0) - return -EIO; - - msgb_pull(msg, 2); - - DEBUGP(DMI, "prim %08x\n", prim); - - switch (prim) { - case 0: - break; - case LAPD_MPH_ACTIVATE_IND: - DEBUGP(DMI, "MPH_ACTIVATE_IND: sapi(%d) tei(%d)\n", sapi, tei); - ret = e1inp_event(e1i_ts, S_INP_TEI_UP, tei, sapi); - break; - case LAPD_MPH_DEACTIVATE_IND: - DEBUGP(DMI, "MPH_DEACTIVATE_IND: sapi(%d) tei(%d)\n", sapi, tei); - ret = e1inp_event(e1i_ts, S_INP_TEI_DN, tei, sapi); - break; - case LAPD_DL_DATA_IND: - case LAPD_DL_UNITDATA_IND: - if (prim == LAPD_DL_DATA_IND) - msg->l2h = msg->data + 2; - else - msg->l2h = msg->data + 1; - DEBUGP(DMI, "RX: %s\n", hexdump(msgb_l2(msg), ret)); - ret = e1inp_rx_ts(e1i_ts, msg, tei, sapi); - break; - default: - printf("ERROR: unknown prim\n"); - break; - } - - DEBUGP(DMI, "Returned ok\n"); - return ret; -} - -static int ts_want_write(struct e1inp_ts *e1i_ts) -{ - /* We never include the DAHDI B-Channel FD into the - * writeset, since it doesn't support poll() based - * write flow control */ - if (e1i_ts->type == E1INP_TS_TYPE_TRAU) { - fprintf(stderr, "Trying to write TRAU ts\n"); - return 0; - } - - e1i_ts->driver.dahdi.fd.when |= BSC_FD_WRITE; - - return 0; -} - -static void timeout_ts1_write(void *data) -{ - struct e1inp_ts *e1i_ts = (struct e1inp_ts *)data; - - /* trigger write of ts1, due to tx delay timer */ - ts_want_write(e1i_ts); -} - -static void dahdi_write_msg(uint8_t *data, int len, void *cbdata) -{ - struct bsc_fd *bfd = cbdata; - struct e1inp_line *line = bfd->data; - unsigned int ts_nr = bfd->priv_nr; - struct e1inp_ts *e1i_ts = &line->ts[ts_nr-1]; - int ret; - - ret = write(bfd->fd, data, len + 2); - if (ret == -1) - handle_dahdi_exception(e1i_ts); - else if (ret < 0) - LOGP(DMI, LOGL_NOTICE, "%s write failed %d\n", __func__, ret); -} - -static int handle_ts1_write(struct bsc_fd *bfd) -{ - struct e1inp_line *line = bfd->data; - unsigned int ts_nr = bfd->priv_nr; - struct e1inp_ts *e1i_ts = &line->ts[ts_nr-1]; - struct e1inp_sign_link *sign_link; - struct msgb *msg; - - bfd->when &= ~BSC_FD_WRITE; - - /* get the next msg for this timeslot */ - msg = e1inp_tx_ts(e1i_ts, &sign_link); - if (!msg) { - /* no message after tx delay timer */ - return 0; - } - - DEBUGP(DMI, "TX: %s\n", hexdump(msg->data, msg->len)); - lapd_transmit(e1i_ts->driver.dahdi.lapd, sign_link->tei, - sign_link->sapi, msg->data, msg->len); - msgb_free(msg); - - /* set tx delay timer for next event */ - e1i_ts->sign.tx_timer.cb = timeout_ts1_write; - e1i_ts->sign.tx_timer.data = e1i_ts; - bsc_schedule_timer(&e1i_ts->sign.tx_timer, 0, 50000); - - return 0; -} - - -static int invertbits = 1; - -static u_int8_t flip_table[256]; - -static void init_flip_bits(void) -{ - int i,k; - - for (i = 0 ; i < 256 ; i++) { - u_int8_t sample = 0 ; - for (k = 0; k<8; k++) { - if ( i & 1 << k ) sample |= 0x80 >> k; - } - flip_table[i] = sample; - } -} - -static u_int8_t * flip_buf_bits ( u_int8_t * buf , int len) -{ - int i; - u_int8_t * start = buf; - - for (i = 0 ; i < len; i++) { - buf[i] = flip_table[(u_int8_t)buf[i]]; - } - - return start; -} - -#define D_BCHAN_TX_GRAN 160 -/* write to a B channel TS */ -static int handle_tsX_write(struct bsc_fd *bfd) -{ - struct e1inp_line *line = bfd->data; - unsigned int ts_nr = bfd->priv_nr; - struct e1inp_ts *e1i_ts = &line->ts[ts_nr-1]; - u_int8_t tx_buf[D_BCHAN_TX_GRAN]; - struct subch_mux *mx = &e1i_ts->trau.mux; - int ret; - - ret = subchan_mux_out(mx, tx_buf, D_BCHAN_TX_GRAN); - - if (ret != D_BCHAN_TX_GRAN) { - fprintf(stderr, "Huh, got ret of %d\n", ret); - if (ret < 0) - return ret; - } - - DEBUGP(DMIB, "BCHAN TX: %s\n", - hexdump(tx_buf, D_BCHAN_TX_GRAN)); - - if (invertbits) { - flip_buf_bits(tx_buf, ret); - } - - ret = write(bfd->fd, tx_buf, ret); - if (ret < D_BCHAN_TX_GRAN) - fprintf(stderr, "send returns %d instead of %d\n", ret, - D_BCHAN_TX_GRAN); - - return ret; -} - -#define D_TSX_ALLOC_SIZE (D_BCHAN_TX_GRAN) -/* FIXME: read from a B channel TS */ -static int handle_tsX_read(struct bsc_fd *bfd) -{ - struct e1inp_line *line = bfd->data; - unsigned int ts_nr = bfd->priv_nr; - struct e1inp_ts *e1i_ts = &line->ts[ts_nr-1]; - struct msgb *msg = msgb_alloc(D_TSX_ALLOC_SIZE, "DAHDI TSx"); - int ret; - - if (!msg) - return -ENOMEM; - - ret = read(bfd->fd, msg->data, D_TSX_ALLOC_SIZE); - if (ret < 0 || ret != D_TSX_ALLOC_SIZE) { - fprintf(stderr, "read error %d %s\n", ret, strerror(errno)); - return ret; - } - - if (invertbits) { - flip_buf_bits(msg->data, ret); - } - - msgb_put(msg, ret); - - msg->l2h = msg->data; - DEBUGP(DMIB, "BCHAN RX: %s\n", - hexdump(msgb_l2(msg), ret)); - ret = e1inp_rx_ts(e1i_ts, msg, 0, 0); - /* physical layer indicates that data has been sent, - * we thus can send some more data */ - ret = handle_tsX_write(bfd); - msgb_free(msg); - - return ret; -} - -/* callback from select.c in case one of the fd's can be read/written */ -static int dahdi_fd_cb(struct bsc_fd *bfd, unsigned int what) -{ - struct e1inp_line *line = bfd->data; - unsigned int ts_nr = bfd->priv_nr; - unsigned int idx = ts_nr-1; - struct e1inp_ts *e1i_ts = &line->ts[idx]; - int rc = 0; - - switch (e1i_ts->type) { - case E1INP_TS_TYPE_SIGN: - if (what & BSC_FD_EXCEPT) - handle_dahdi_exception(e1i_ts); - if (what & BSC_FD_READ) - rc = handle_ts1_read(bfd); - if (what & BSC_FD_WRITE) - rc = handle_ts1_write(bfd); - break; - case E1INP_TS_TYPE_TRAU: - if (what & BSC_FD_EXCEPT) - handle_dahdi_exception(e1i_ts); - if (what & BSC_FD_READ) - rc = handle_tsX_read(bfd); - if (what & BSC_FD_WRITE) - rc = handle_tsX_write(bfd); - /* We never include the DAHDI B-Channel FD into the - * writeset, since it doesn't support poll() based - * write flow control */ - break; - default: - fprintf(stderr, "unknown E1 TS type %u\n", e1i_ts->type); - break; - } - - return rc; -} - -static int dahdi_e1_line_update(struct e1inp_line *line); - -struct e1inp_driver dahdi_driver = { - .name = "dahdi", - .want_write = ts_want_write, - .line_update = &dahdi_e1_line_update, -}; - -void dahdi_set_bufinfo(int fd, int as_sigchan) -{ - struct dahdi_bufferinfo bi; - int x = 0; - - if (ioctl(fd, DAHDI_GET_BUFINFO, &bi)) { - fprintf(stderr, "Error getting bufinfo\n"); - exit(-1); - } - - if (as_sigchan) { - bi.numbufs = 4; - bi.bufsize = 512; - } else { - bi.numbufs = 8; - bi.bufsize = D_BCHAN_TX_GRAN; - bi.txbufpolicy = DAHDI_POLICY_WHEN_FULL; - } - - if (ioctl(fd, DAHDI_SET_BUFINFO, &bi)) { - fprintf(stderr, "Error setting bufinfo\n"); - exit(-1); - } - - if (!as_sigchan) { - if (ioctl(fd, DAHDI_AUDIOMODE, &x)) { - fprintf(stderr, "Error setting bufinfo\n"); - exit(-1); - } - } else { - int one = 1; - ioctl(fd, DAHDI_HDLCFCSMODE, &one); - /* we cannot reliably check for the ioctl return value here - * as this command will fail if the slot _already_ was a - * signalling slot before :( */ - } -} - -static int dahdi_e1_setup(struct e1inp_line *line) -{ - int ts, ret; - - /* TS0 is CRC4, don't need any fd for it */ - for (ts = 1; ts < NUM_E1_TS; ts++) { - unsigned int idx = ts-1; - char openstr[128]; - struct e1inp_ts *e1i_ts = &line->ts[idx]; - struct bsc_fd *bfd = &e1i_ts->driver.dahdi.fd; - - bfd->data = line; - bfd->priv_nr = ts; - bfd->cb = dahdi_fd_cb; - snprintf(openstr, sizeof(openstr), "/dev/dahdi/%d", ts); - - switch (e1i_ts->type) { - case E1INP_TS_TYPE_NONE: - continue; - break; - case E1INP_TS_TYPE_SIGN: - bfd->fd = open(openstr, O_RDWR | O_NONBLOCK); - if (bfd->fd == -1) { - fprintf(stderr, "%s could not open %s %s\n", - __func__, openstr, strerror(errno)); - exit(-1); - } - bfd->when = BSC_FD_READ | BSC_FD_EXCEPT; - dahdi_set_bufinfo(bfd->fd, 1); - e1i_ts->driver.dahdi.lapd = lapd_instance_alloc(1, dahdi_write_msg, bfd); - break; - case E1INP_TS_TYPE_TRAU: - bfd->fd = open(openstr, O_RDWR | O_NONBLOCK); - if (bfd->fd == -1) { - fprintf(stderr, "%s could not open %s %s\n", - __func__, openstr, strerror(errno)); - exit(-1); - } - dahdi_set_bufinfo(bfd->fd, 0); - /* We never include the DAHDI B-Channel FD into the - * writeset, since it doesn't support poll() based - * write flow control */ - bfd->when = BSC_FD_READ | BSC_FD_EXCEPT;// | BSC_FD_WRITE; - break; - } - - if (bfd->fd < 0) { - fprintf(stderr, "%s could not open %s %s\n", - __func__, openstr, strerror(errno)); - return bfd->fd; - } - - ret = bsc_register_fd(bfd); - if (ret < 0) { - fprintf(stderr, "could not register FD: %s\n", - strerror(ret)); - return ret; - } - } - - return 0; -} - -static int dahdi_e1_line_update(struct e1inp_line *line) -{ - if (line->driver != &dahdi_driver) - return -EINVAL; - - return dahdi_e1_setup(line); -} - -int e1inp_dahdi_init(void) -{ - init_flip_bits(); - - /* register the driver with the core */ - return e1inp_driver_register(&dahdi_driver); -} - -#endif /* HAVE_DAHDI_USER_H */ diff --git a/openbsc/src/abis/input/ipaccess.c b/openbsc/src/abis/input/ipaccess.c deleted file mode 100644 index dcf8d1a53..000000000 --- a/openbsc/src/abis/input/ipaccess.c +++ /dev/null @@ -1,797 +0,0 @@ -/* OpenBSC Abis input driver for ip.access */ - -/* (C) 2009 by Harald Welte - * (C) 2010 by Holger Hans Peter Freyther - * (C) 2010 by On-Waves - * - * All Rights Reserved - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation; either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - * - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#define PRIV_OML 1 -#define PRIV_RSL 2 - -/* data structure for one E1 interface with A-bis */ -struct ia_e1_handle { - struct bsc_fd listen_fd; - struct bsc_fd rsl_listen_fd; - struct gsm_network *gsmnet; -}; - -static struct ia_e1_handle *e1h; - - -#define TS1_ALLOC_SIZE 900 - -static const u_int8_t pong[] = { 0, 1, IPAC_PROTO_IPACCESS, IPAC_MSGT_PONG }; -static const u_int8_t id_ack[] = { 0, 1, IPAC_PROTO_IPACCESS, IPAC_MSGT_ID_ACK }; -static const u_int8_t id_req[] = { 0, 17, IPAC_PROTO_IPACCESS, 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, - }; - -static const char *idtag_names[] = { - [IPAC_IDTAG_SERNR] = "Serial_Number", - [IPAC_IDTAG_UNITNAME] = "Unit_Name", - [IPAC_IDTAG_LOCATION1] = "Location_1", - [IPAC_IDTAG_LOCATION2] = "Location_2", - [IPAC_IDTAG_EQUIPVERS] = "Equipment_Version", - [IPAC_IDTAG_SWVERSION] = "Software_Version", - [IPAC_IDTAG_IPADDR] = "IP_Address", - [IPAC_IDTAG_MACADDR] = "MAC_Address", - [IPAC_IDTAG_UNIT] = "Unit_ID", -}; - -static const char *ipac_idtag_name(int tag) -{ - if (tag >= ARRAY_SIZE(idtag_names)) - return "unknown"; - - return idtag_names[tag]; -} - -int ipaccess_idtag_parse(struct tlv_parsed *dec, unsigned char *buf, int len) -{ - u_int8_t t_len; - u_int8_t t_tag; - u_int8_t *cur = buf; - - memset(dec, 0, sizeof(*dec)); - - while (len >= 2) { - len -= 2; - t_len = *cur++; - t_tag = *cur++; - - if (t_len > len + 1) { - LOGP(DMI, LOGL_ERROR, "The tag does not fit: %d\n", t_len); - return -1; - } - - DEBUGPC(DMI, "%s='%s' ", ipac_idtag_name(t_tag), cur); - - dec->lv[t_tag].len = t_len; - dec->lv[t_tag].val = cur; - - cur += t_len; - len -= t_len; - } - return 0; -} - -struct gsm_bts *find_bts_by_unitid(struct gsm_network *net, - u_int16_t site_id, u_int16_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; -} - -static int parse_unitid(const char *str, u_int16_t *site_id, u_int16_t *bts_id, - u_int16_t *trx_id) -{ - unsigned long ul; - char *endptr; - const char *nptr; - - nptr = str; - ul = strtoul(nptr, &endptr, 10); - if (endptr <= nptr) - return -EINVAL; - if (site_id) - *site_id = ul & 0xffff; - - if (*endptr++ != '/') - return -EINVAL; - - nptr = endptr; - ul = strtoul(nptr, &endptr, 10); - if (endptr <= nptr) - return -EINVAL; - if (bts_id) - *bts_id = ul & 0xffff; - - if (*endptr++ != '/') - return -EINVAL; - - nptr = endptr; - ul = strtoul(nptr, &endptr, 10); - if (endptr <= nptr) - return -EINVAL; - if (trx_id) - *trx_id = ul & 0xffff; - - return 0; -} - -/* send the id ack */ -int ipaccess_send_id_ack(int fd) -{ - return write(fd, id_ack, sizeof(id_ack)); -} - -int ipaccess_send_id_req(int fd) -{ - return write(fd, id_req, sizeof(id_req)); -} - -/* base handling of the ip.access protocol */ -int ipaccess_rcvmsg_base(struct msgb *msg, - struct bsc_fd *bfd) -{ - u_int8_t msg_type = *(msg->l2h); - int ret = 0; - - switch (msg_type) { - case IPAC_MSGT_PING: - ret = write(bfd->fd, pong, sizeof(pong)); - break; - case IPAC_MSGT_PONG: - DEBUGP(DMI, "PONG!\n"); - break; - case IPAC_MSGT_ID_ACK: - DEBUGP(DMI, "ID_ACK? -> ACK!\n"); - ret = ipaccess_send_id_ack(bfd->fd); - break; - } - return 0; -} - -static int ipaccess_rcvmsg(struct e1inp_line *line, struct msgb *msg, - struct bsc_fd *bfd) -{ - struct tlv_parsed tlvp; - u_int8_t msg_type = *(msg->l2h); - u_int16_t site_id = 0, bts_id = 0, trx_id = 0; - struct gsm_bts *bts; - char *unitid; - int len; - - /* handle base messages */ - ipaccess_rcvmsg_base(msg, bfd); - - switch (msg_type) { - case IPAC_MSGT_ID_RESP: - DEBUGP(DMI, "ID_RESP "); - /* parse tags, search for Unit ID */ - ipaccess_idtag_parse(&tlvp, (u_int8_t *)msg->l2h + 2, - msgb_l2len(msg)-2); - DEBUGP(DMI, "\n"); - - if (!TLVP_PRESENT(&tlvp, IPAC_IDTAG_UNIT)) - break; - - len = TLVP_LEN(&tlvp, IPAC_IDTAG_UNIT); - if (len < 1) - break; - - /* lookup BTS, create sign_link, ... */ - unitid = (char *) TLVP_VAL(&tlvp, IPAC_IDTAG_UNIT); - unitid[len - 1] = '\0'; - parse_unitid(unitid, &site_id, &bts_id, &trx_id); - bts = find_bts_by_unitid(e1h->gsmnet, site_id, bts_id); - if (!bts) { - LOGP(DINP, LOGL_ERROR, "Unable to find BTS configuration for " - " %u/%u/%u, disconnecting\n", site_id, bts_id, - trx_id); - return -EIO; - } - DEBUGP(DINP, "Identified BTS %u/%u/%u\n", site_id, bts_id, trx_id); - if (bfd->priv_nr == PRIV_OML) { - /* drop any old oml connection */ - ipaccess_drop_oml(bts); - bts->oml_link = e1inp_sign_link_create(&line->ts[PRIV_OML - 1], - E1INP_SIGN_OML, bts->c0, - bts->oml_tei, 0); - } else if (bfd->priv_nr == PRIV_RSL) { - struct e1inp_ts *e1i_ts; - struct bsc_fd *newbfd; - struct gsm_bts_trx *trx = gsm_bts_trx_num(bts, trx_id); - - /* drop any old rsl connection */ - ipaccess_drop_rsl(trx); - - if (!bts->oml_link) { - bsc_unregister_fd(bfd); - close(bfd->fd); - bfd->fd = -1; - talloc_free(bfd); - return 0; - } - - bfd->data = line = bts->oml_link->ts->line; - e1i_ts = &line->ts[PRIV_RSL + trx_id - 1]; - newbfd = &e1i_ts->driver.ipaccess.fd; - e1inp_ts_config(e1i_ts, line, E1INP_TS_TYPE_SIGN); - - trx->rsl_link = e1inp_sign_link_create(e1i_ts, - E1INP_SIGN_RSL, trx, - trx->rsl_tei, 0); - trx->rsl_link->ts->sign.delay = 0; - - /* get rid of our old temporary bfd */ - memcpy(newbfd, bfd, sizeof(*newbfd)); - newbfd->priv_nr = PRIV_RSL + trx_id; - bsc_unregister_fd(bfd); - bfd->fd = -1; - talloc_free(bfd); - bsc_register_fd(newbfd); - } - break; - } - return 0; -} - -#define OML_UP 0x0001 -#define RSL_UP 0x0002 - -/* - * read one ipa message from the socket - * return NULL in case of error - */ -struct msgb *ipaccess_read_msg(struct bsc_fd *bfd, int *error) -{ - struct msgb *msg = msgb_alloc(TS1_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, sizeof(*hh), 0); - if (ret == 0) { - msgb_free(msg); - *error = ret; - return NULL; - } else if (ret != sizeof(*hh)) { - if (errno != EAGAIN) - LOGP(DINP, LOGL_ERROR, "recv error %d %s\n", ret, strerror(errno)); - 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); - - if (len < 0 || TS1_ALLOC_SIZE < len + sizeof(*hh)) { - LOGP(DINP, LOGL_ERROR, "Can not read this packet. %d avail\n", len); - msgb_free(msg); - *error = -EIO; - return NULL; - } - - ret = recv(bfd->fd, msg->l2h, len, 0); - if (ret < len) { - LOGP(DINP, LOGL_ERROR, "short read! Got %d from %d\n", ret, len); - msgb_free(msg); - *error = -EIO; - return NULL; - } - msgb_put(msg, ret); - - return msg; -} - -int ipaccess_drop_oml(struct gsm_bts *bts) -{ - struct gsm_bts_trx *trx; - struct e1inp_ts *ts; - struct e1inp_line *line; - struct bsc_fd *bfd; - - if (!bts || !bts->oml_link) - return -1; - - /* send OML down */ - ts = bts->oml_link->ts; - line = ts->line; - e1inp_event(ts, S_INP_TEI_DN, bts->oml_link->tei, bts->oml_link->sapi); - - bfd = &ts->driver.ipaccess.fd; - bsc_unregister_fd(bfd); - close(bfd->fd); - bfd->fd = -1; - - /* clean up OML and RSL */ - e1inp_sign_link_destroy(bts->oml_link); - bts->oml_link = NULL; - bts->ip_access.flags = 0; - - /* drop all RSL connections too */ - llist_for_each_entry(trx, &bts->trx_list, list) - ipaccess_drop_rsl(trx); - - /* kill the E1 line now... as we have no one left to use it */ - talloc_free(line); - - return -1; -} - -static int ipaccess_drop(struct e1inp_ts *ts, struct bsc_fd *bfd) -{ - struct e1inp_sign_link *link; - int bts_nr; - - if (!ts) { - /* - * If we don't have a TS this means that this is a RSL - * connection but we are not past the authentication - * handling yet. So we can safely delete this bfd and - * wait for a reconnect. - */ - bsc_unregister_fd(bfd); - close(bfd->fd); - bfd->fd = -1; - talloc_free(bfd); - return -1; - } - - /* attempt to find a signalling link */ - if (ts->type == E1INP_TS_TYPE_SIGN) { - llist_for_each_entry(link, &ts->sign.sign_links, list) { - bts_nr = link->trx->bts->bts_nr; - /* we have issues just reconnecting RLS so we drop OML */ - ipaccess_drop_oml(link->trx->bts); - return bts_nr; - } - } - - /* error case */ - LOGP(DINP, LOGL_ERROR, "Failed to find a signalling link for ts: %p\n", ts); - bsc_unregister_fd(bfd); - close(bfd->fd); - bfd->fd = -1; - return -1; -} - -int ipaccess_drop_rsl(struct gsm_bts_trx *trx) -{ - struct bsc_fd *bfd; - struct e1inp_ts *ts; - - if (!trx || !trx->rsl_link) - return -1; - - /* send RSL down */ - ts = trx->rsl_link->ts; - e1inp_event(ts, S_INP_TEI_DN, trx->rsl_link->tei, trx->rsl_link->sapi); - - /* close the socket */ - bfd = &ts->driver.ipaccess.fd; - bsc_unregister_fd(bfd); - close(bfd->fd); - bfd->fd = -1; - - /* destroy */ - e1inp_sign_link_destroy(trx->rsl_link); - trx->rsl_link = NULL; - - return -1; -} - -static int handle_ts1_read(struct bsc_fd *bfd) -{ - struct e1inp_line *line = bfd->data; - unsigned int ts_nr = bfd->priv_nr; - struct e1inp_ts *e1i_ts = &line->ts[ts_nr-1]; - struct e1inp_sign_link *link; - struct msgb *msg; - struct ipaccess_head *hh; - int ret = 0, error; - - msg = ipaccess_read_msg(bfd, &error); - if (!msg) { - if (error == 0) { - int ret = ipaccess_drop(e1i_ts, bfd); - if (ret >= 0) - LOGP(DINP, LOGL_NOTICE, "BTS %u disappeared, dead socket\n", - ret); - else - LOGP(DINP, LOGL_NOTICE, "unknown BTS disappeared, dead socket\n"); - } - return error; - } - - DEBUGP(DMI, "RX %u: %s\n", ts_nr, hexdump(msgb_l2(msg), msgb_l2len(msg))); - - hh = (struct ipaccess_head *) msg->data; - if (hh->proto == IPAC_PROTO_IPACCESS) { - ret = ipaccess_rcvmsg(line, msg, bfd); - if (ret < 0) - ipaccess_drop(e1i_ts, bfd); - msgb_free(msg); - return ret; - } - /* BIG FAT WARNING: bfd might no longer exist here, since ipaccess_rcvmsg() - * might have free'd it !!! */ - - link = e1inp_lookup_sign_link(e1i_ts, hh->proto, 0); - if (!link) { - LOGP(DINP, LOGL_ERROR, "no matching signalling link for " - "hh->proto=0x%02x\n", hh->proto); - msgb_free(msg); - return -EIO; - } - msg->trx = link->trx; - - switch (link->type) { - case E1INP_SIGN_RSL: - if (!(msg->trx->bts->ip_access.flags & (RSL_UP << msg->trx->nr))) { - e1inp_event(e1i_ts, S_INP_TEI_UP, link->tei, link->sapi); - msg->trx->bts->ip_access.flags |= (RSL_UP << msg->trx->nr); - } - ret = abis_rsl_rcvmsg(msg); - break; - case E1INP_SIGN_OML: - if (!(msg->trx->bts->ip_access.flags & OML_UP)) { - e1inp_event(e1i_ts, S_INP_TEI_UP, link->tei, link->sapi); - msg->trx->bts->ip_access.flags |= OML_UP; - } - ret = abis_nm_rcvmsg(msg); - break; - default: - LOGP(DINP, LOGL_NOTICE, "Unknown IP.access protocol proto=0x%02x\n", hh->proto); - msgb_free(msg); - break; - } - return ret; -} - -void ipaccess_prepend_header(struct msgb *msg, int proto) -{ - struct ipaccess_head *hh; - - /* prepend the ip.access header */ - hh = (struct ipaccess_head *) msgb_push(msg, sizeof(*hh)); - hh->len = htons(msg->len - sizeof(*hh)); - hh->proto = proto; -} - -static int ts_want_write(struct e1inp_ts *e1i_ts) -{ - e1i_ts->driver.ipaccess.fd.when |= BSC_FD_WRITE; - - return 0; -} - -static void timeout_ts1_write(void *data) -{ - struct e1inp_ts *e1i_ts = (struct e1inp_ts *)data; - - /* trigger write of ts1, due to tx delay timer */ - ts_want_write(e1i_ts); -} - -static int handle_ts1_write(struct bsc_fd *bfd) -{ - struct e1inp_line *line = bfd->data; - unsigned int ts_nr = bfd->priv_nr; - struct e1inp_ts *e1i_ts = &line->ts[ts_nr-1]; - struct e1inp_sign_link *sign_link; - struct msgb *msg; - u_int8_t proto; - int ret; - - bfd->when &= ~BSC_FD_WRITE; - - /* get the next msg for this timeslot */ - msg = e1inp_tx_ts(e1i_ts, &sign_link); - if (!msg) { - /* no message after tx delay timer */ - return 0; - } - - switch (sign_link->type) { - case E1INP_SIGN_OML: - proto = IPAC_PROTO_OML; - break; - case E1INP_SIGN_RSL: - proto = IPAC_PROTO_RSL; - break; - default: - msgb_free(msg); - bfd->when |= BSC_FD_WRITE; /* come back for more msg */ - return -EINVAL; - } - - msg->l2h = msg->data; - ipaccess_prepend_header(msg, sign_link->tei); - - DEBUGP(DMI, "TX %u: %s\n", ts_nr, hexdump(msg->l2h, msgb_l2len(msg))); - - ret = send(bfd->fd, msg->data, msg->len, 0); - msgb_free(msg); - - /* set tx delay timer for next event */ - e1i_ts->sign.tx_timer.cb = timeout_ts1_write; - e1i_ts->sign.tx_timer.data = e1i_ts; - - /* Reducing this might break the nanoBTS 900 init. */ - bsc_schedule_timer(&e1i_ts->sign.tx_timer, 0, e1i_ts->sign.delay); - - return ret; -} - -/* callback from select.c in case one of the fd's can be read/written */ -static int ipaccess_fd_cb(struct bsc_fd *bfd, unsigned int what) -{ - struct e1inp_line *line = bfd->data; - unsigned int ts_nr = bfd->priv_nr; - unsigned int idx = ts_nr-1; - struct e1inp_ts *e1i_ts; - int rc = 0; - - /* In case of early RSL we might not yet have a line */ - - if (line) - e1i_ts = &line->ts[idx]; - - if (!line || e1i_ts->type == E1INP_TS_TYPE_SIGN) { - if (what & BSC_FD_READ) - rc = handle_ts1_read(bfd); - if (what & BSC_FD_WRITE) - rc = handle_ts1_write(bfd); - } else - LOGP(DINP, LOGL_ERROR, "unknown E1 TS type %u\n", e1i_ts->type); - - return rc; -} - -struct e1inp_driver ipaccess_driver = { - .name = "ip.access", - .want_write = ts_want_write, - .default_delay = 0, -}; - -/* callback of the OML listening filedescriptor */ -static int listen_fd_cb(struct bsc_fd *listen_bfd, unsigned int what) -{ - int ret; - int idx = 0; - int i; - struct e1inp_line *line; - struct e1inp_ts *e1i_ts; - struct bsc_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; - } - LOGP(DINP, LOGL_NOTICE, "accept()ed new OML link from %s\n", - inet_ntoa(sa.sin_addr)); - - line = talloc_zero(tall_bsc_ctx, struct e1inp_line); - if (!line) { - close(ret); - return -ENOMEM; - } - line->driver = &ipaccess_driver; - //line->driver_data = e1h; - /* create virrtual E1 timeslots for signalling */ - e1inp_ts_config(&line->ts[1-1], line, E1INP_TS_TYPE_SIGN); - - /* initialize the fds */ - for (i = 0; i < ARRAY_SIZE(line->ts); ++i) - line->ts[i].driver.ipaccess.fd.fd = -1; - - e1i_ts = &line->ts[idx]; - - bfd = &e1i_ts->driver.ipaccess.fd; - bfd->fd = ret; - bfd->data = line; - bfd->priv_nr = PRIV_OML; - bfd->cb = ipaccess_fd_cb; - bfd->when = BSC_FD_READ; - ret = bsc_register_fd(bfd); - if (ret < 0) { - LOGP(DINP, LOGL_ERROR, "could not register FD\n"); - close(bfd->fd); - talloc_free(line); - return ret; - } - - /* Request ID. FIXME: request LOCATION, HW/SW VErsion, Unit Name, Serno */ - ret = ipaccess_send_id_req(bfd->fd); - - return ret; - //return e1inp_line_register(line); -} - -static int rsl_listen_fd_cb(struct bsc_fd *listen_bfd, unsigned int what) -{ - struct sockaddr_in sa; - socklen_t sa_len = sizeof(sa); - struct bsc_fd *bfd; - int ret; - - if (!(what & BSC_FD_READ)) - return 0; - - bfd = talloc_zero(tall_bsc_ctx, struct bsc_fd); - if (!bfd) - return -ENOMEM; - - /* Some BTS has connected to us, but we don't know yet which line - * (as created by the OML link) to associate it with. Thus, we - * allocate a temporary bfd until we have received ID from BTS */ - - bfd->fd = accept(listen_bfd->fd, (struct sockaddr *) &sa, &sa_len); - if (bfd->fd < 0) { - perror("accept"); - return bfd->fd; - } - LOGP(DINP, LOGL_NOTICE, "accept()ed new RSL link from %s\n", inet_ntoa(sa.sin_addr)); - bfd->priv_nr = PRIV_RSL; - bfd->cb = ipaccess_fd_cb; - bfd->when = BSC_FD_READ; - ret = bsc_register_fd(bfd); - if (ret < 0) { - LOGP(DINP, LOGL_ERROR, "could not register FD\n"); - close(bfd->fd); - talloc_free(bfd); - return ret; - } - /* Request ID. FIXME: request LOCATION, HW/SW VErsion, Unit Name, Serno */ - ret = write(bfd->fd, id_req, sizeof(id_req)); - - return 0; -} - -/* Actively connect to a BTS. Currently used by ipaccess-config.c */ -int ipaccess_connect(struct e1inp_line *line, struct sockaddr_in *sa) -{ - struct e1inp_ts *e1i_ts = &line->ts[0]; - struct bsc_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 = PRIV_OML; - - if (bfd->fd < 0) { - LOGP(DINP, LOGL_ERROR, "could not create TCP socket.\n"); - return -EIO; - } - - setsockopt(bfd->fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)); - - ret = connect(bfd->fd, (struct sockaddr *) sa, sizeof(*sa)); - if (ret < 0) { - LOGP(DINP, LOGL_ERROR, "could not connect socket\n"); - close(bfd->fd); - return ret; - } - - ret = bsc_register_fd(bfd); - if (ret < 0) { - close(bfd->fd); - return ret; - } - - line->driver = &ipaccess_driver; - - return ret; - //return e1inp_line_register(line); -} - -int ipaccess_setup(struct gsm_network *gsmnet) -{ - int ret; - - /* register the driver with the core */ - /* FIXME: do this in the plugin initializer function */ - ret = e1inp_driver_register(&ipaccess_driver); - if (ret) - return ret; - - e1h = talloc_zero(tall_bsc_ctx, struct ia_e1_handle); - if (!e1h) - return -ENOMEM; - - e1h->gsmnet = gsmnet; - - /* Listen for OML connections */ - ret = make_sock(&e1h->listen_fd, IPPROTO_TCP, 0, IPA_TCP_PORT_OML, - listen_fd_cb); - if (ret < 0) - return ret; - - /* Listen for RSL connections */ - ret = make_sock(&e1h->rsl_listen_fd, IPPROTO_TCP, 0, - IPA_TCP_PORT_RSL, rsl_listen_fd_cb); - if (ret < 0) - return ret; - - return ret; -} diff --git a/openbsc/src/abis/input/lapd.c b/openbsc/src/abis/input/lapd.c deleted file mode 100644 index 7bce6cc51..000000000 --- a/openbsc/src/abis/input/lapd.c +++ /dev/null @@ -1,710 +0,0 @@ -/* OpenBSC minimal LAPD implementation */ - -/* (C) 2009 by oystein@homelien.no - * (C) 2009 by Holger Hans Peter Freyther - * (C) 2010 by Digium and Matthew Fredrickson - * (C) 2011 by Harald Welte - * - * All Rights Reserved - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU 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. - * - */ - -/* TODO: - * detect RR timeout and set SAP state back to SABM_RETRANSMIT - * use of value_string - * further code cleanup (spaghetti) - */ - -#include -#include -#include -#include - -#include "lapd.h" - -#include -#include -#include -#include -#include - -#define SABM_INTERVAL 0, 300000 - -typedef enum { - LAPD_TEI_NONE = 0, - LAPD_TEI_ASSIGNED, - LAPD_TEI_ACTIVE, -} lapd_tei_state; - -const char *lapd_tei_states[] = { - "NONE", - "ASSIGNED", - "ACTIVE", -}; - -typedef enum { - LAPD_TYPE_NONE = 0, - - LAPD_TYPE_I, - LAPD_TYPE_S, - LAPD_TYPE_U, -} lapd_msg_type; - -typedef enum { - /* commands/responses */ - LAPD_CMD_NONE = 0, - - LAPD_CMD_I, - LAPD_CMD_RR, - LAPD_CMD_RNR, - LAPD_CMD_REJ, - - LAPD_CMD_SABME, - LAPD_CMD_DM, - LAPD_CMD_UI, - LAPD_CMD_DISC, - LAPD_CMD_UA, - LAPD_CMD_FRMR, - LAPD_CMD_XID, -} lapd_cmd_type; - -const char *lapd_cmd_types[] = { - "NONE", - - "I", - "RR", - "RNR", - "REJ", - - "SABME", - "DM", - "UI", - "DISC", - "UA", - "FRMR", - "XID", - -}; - -enum lapd_sap_state { - SAP_STATE_INACTIVE, - SAP_STATE_SABM_RETRANS, - SAP_STATE_ACTIVE, -}; - -const char *lapd_sap_states[] = { - "INACTIVE", - "SABM_RETRANS", - "ACTIVE", -}; - -const char *lapd_msg_types = "?ISU"; - -/* structure representing an allocated TEI within a LAPD instance */ -struct lapd_tei { - struct llist_head list; - struct lapd_instance *li; - uint8_t tei; - lapd_tei_state state; - - struct llist_head sap_list; -}; - -/* Structure representing a SAP within a TEI. We use this for TE-mode to - * re-transmit SABM */ -struct lapd_sap { - struct llist_head list; - struct lapd_tei *tei; - uint8_t sapi; - enum lapd_sap_state state; - - /* A valid N(R) value is one that is in the range V(A) ≤ N(R) ≤ V(S). */ - int vs; /* next to be transmitted */ - int va; /* last acked by peer */ - int vr; /* next expected to be received */ - - struct timer_list sabme_timer; /* timer to re-transmit SABM message */ -}; - -/* 3.5.2.2 Send state variable V(S) - * Each point-to-point data link connection endpoint shall have an associated V(S) when using I frame - * commands. V(S) denotes the sequence number of the next I frame to be transmitted. The V(S) can - * take on the value 0 through n minus 1. The value of V(S) shall be incremented by 1 with each - * successive I frame transmission, and shall not exceed V(A) by more than the maximum number of - * outstanding I frames k. The value of k may be in the range of 1 ≤ k ≤ 127. - * - * 3.5.2.3 Acknowledge state variable V(A) - * Each point-to-point data link connection endpoint shall have an associated V(A) when using I frame - * commands and supervisory frame commands/responses. V(A) identifies the last I frame that has been - * acknowledged by its peer [V(A) − 1 equals the N(S) of the last acknowledged I frame]. V(A) can - * take on the value 0 through n minus 1. The value of V(A) shall be updated by the valid N(R) values - * received from its peer (see 3.5.2.6). A valid N(R) value is one that is in the range V(A) ≤ N(R) ≤ - * V(S). - * - * 3.5.2.5 Receive state variable V(R) - * Each point-to-point data link connection endpoint shall have an associated V(R) when using I frame - * commands and supervisory frame commands/responses. V(R) denotes the sequence number of the - * next in-sequence I frame expected to be received. V(R) can take on the value 0 through n minus 1. - * The value of V(R) shall be incremented by one with the receipt of an error-free, in-sequence I frame - * whose N(S) equals V(R). - */ -#define LAPD_NS(sap) (sap->vs) -#define LAPD_NR(sap) (sap->vr) - -/* 3.5.2.4 Send sequence number N(S) - * Only I frames contain N(S), the send sequence number of transmitted I frames. At the time that an in- - * sequence I frame is designated for transmission, the value of N(S) is set equal to V(S). - * - * 3.5.2.6 Receive sequence number N(R) - * All I frames and supervisory frames contain N(R), the expected send sequence number of the next - * received I frame. At the time that a frame of the above types is designated for transmission, the value - * of N(R) is set equal to V(R). N(R) indicates that the data link layer entity transmitting the N(R) has - * correctly received all I frames numbered up to and including N(R) − 1. - */ - -/* Resolve TEI structure from given numeric TEI */ -static struct lapd_tei *teip_from_tei(struct lapd_instance *li, uint8_t tei) -{ - struct lapd_tei *lt; - - llist_for_each_entry(lt, &li->tei_list, list) { - if (lt->tei == tei) - return lt; - } - return NULL; -}; - -static void lapd_tei_set_state(struct lapd_tei *teip, int newstate) -{ - DEBUGP(DMI, "state change on TEI %d: %s -> %s\n", teip->tei, - lapd_tei_states[teip->state], lapd_tei_states[newstate]); - teip->state = newstate; -}; - -/* Allocate a new TEI */ -struct lapd_tei *lapd_tei_alloc(struct lapd_instance *li, uint8_t tei) -{ - struct lapd_tei *teip; - - teip = talloc_zero(li, struct lapd_tei); - if (!teip) - return NULL; - - teip->li = li; - teip->tei = tei; - llist_add(&teip->list, &li->tei_list); - INIT_LLIST_HEAD(&teip->sap_list); - - lapd_tei_set_state(teip, LAPD_TEI_ASSIGNED); - - return teip; -} - -/* Find a SAP within a given TEI */ -static struct lapd_sap *lapd_sap_find(struct lapd_tei *teip, uint8_t sapi) -{ - struct lapd_sap *sap; - - llist_for_each_entry(sap, &teip->sap_list, list) { - if (sap->sapi == sapi) - return sap; - } - - return NULL; -} - -static void sabme_timer_cb(void *_sap); - -/* Allocate a new SAP within a given TEI */ -static struct lapd_sap *lapd_sap_alloc(struct lapd_tei *teip, uint8_t sapi) -{ - struct lapd_sap *sap = talloc_zero(teip, struct lapd_sap); - - LOGP(DMI, LOGL_INFO, "Allocating SAP for SAPI=%u / TEI=%u\n", - sapi, teip->tei); - - sap->sapi = sapi; - sap->tei = teip; - sap->sabme_timer.cb = &sabme_timer_cb; - sap->sabme_timer.data = sap; - - llist_add(&sap->list, &teip->sap_list); - - return sap; -} - -static void lapd_sap_set_state(struct lapd_tei *teip, uint8_t sapi, - enum lapd_sap_state newstate) -{ - struct lapd_sap *sap = lapd_sap_find(teip, sapi); - if (!sap) - return; - - DEBUGP(DMI, "state change on TEI %u / SAPI %u: %s -> %s\n", teip->tei, - sapi, lapd_sap_states[sap->state], lapd_sap_states[newstate]); - switch (sap->state) { - case SAP_STATE_SABM_RETRANS: - if (newstate != SAP_STATE_SABM_RETRANS) - bsc_del_timer(&sap->sabme_timer); - break; - default: - if (newstate == SAP_STATE_SABM_RETRANS) - bsc_schedule_timer(&sap->sabme_timer, SABM_INTERVAL); - break; - } - - sap->state = newstate; -}; - -/* Input function into TEI manager */ -static void lapd_tei_receive(struct lapd_instance *li, uint8_t *data, int len) -{ - uint8_t entity = data[0]; - uint8_t ref = data[1]; - uint8_t mt = data[3]; - uint8_t action = data[4] >> 1; - uint8_t e = data[4] & 1; - uint8_t resp[8]; - struct lapd_tei *teip; - - DEBUGP(DMI, "TEIMGR: entity %x, ref %x, mt %x, action %x, e %x\n", entity, ref, mt, action, e); - - switch (mt) { - case 0x01: /* IDENTITY REQUEST */ - DEBUGP(DMI, "TEIMGR: identity request for TEI %u\n", action); - - teip = teip_from_tei(li, action); - if (!teip) { - LOGP(DMI, LOGL_INFO, "TEI MGR: New TEI %u\n", action); - lapd_tei_alloc(li, action); - } - - /* Send ACCEPT */ - memmove(resp, "\xfe\xff\x03\x0f\x00\x00\x02\x00", 8); - resp[7] = (action << 1) | 1; - li->transmit_cb(resp, 8, li->cbdata); - - if (teip->state == LAPD_TEI_NONE) - lapd_tei_set_state(teip, LAPD_TEI_ASSIGNED); - break; - default: - LOGP(DMI, LOGL_NOTICE, "TEIMGR: unknown mt %x action %x\n", - mt, action); - break; - }; -}; - -/* General input function for any data received for this LAPD instance */ -uint8_t *lapd_receive(struct lapd_instance *li, uint8_t * data, unsigned int len, - int *ilen, lapd_mph_type *prim) -{ - uint8_t sapi, cr, tei, command; - int pf, ns, nr; - uint8_t *contents; - struct lapd_tei *teip; - struct lapd_sap *sap; - - uint8_t resp[8]; - int l = 0; - - *ilen = 0; - *prim = 0; - - if (len < 2) { - DEBUGP(DMI, "len %d < 2\n", len); - return NULL; - }; - - if ((data[0] & 1) != 0 || (data[1] & 1) != 1) { - DEBUGP(DMI, "address field %x/%x not well formed\n", data[0], - data[1]); - return NULL; - }; - - sapi = data[0] >> 2; - cr = (data[0] >> 1) & 1; - tei = data[1] >> 1; - command = li->network_side ^ cr; - //DEBUGP(DMI, " address sapi %x tei %d cmd %d cr %d\n", sapi, tei, command, cr); - - if (len < 3) { - DEBUGP(DMI, "len %d < 3\n", len); - return NULL; - }; - - lapd_msg_type typ = 0; - lapd_cmd_type cmd = 0; - pf = -1; - ns = -1; - nr = -1; - if ((data[2] & 1) == 0) { - typ = LAPD_TYPE_I; - assert(len >= 4); - ns = data[2] >> 1; - nr = data[3] >> 1; - pf = data[3] & 1; - cmd = LAPD_CMD_I; - } else if ((data[2] & 3) == 1) { - typ = LAPD_TYPE_S; - assert(len >= 4); - nr = data[3] >> 1; - pf = data[3] & 1; - switch (data[2]) { - case 0x1: - cmd = LAPD_CMD_RR; - break; - case 0x5: - cmd = LAPD_CMD_RNR; - break; - case 0x9: - cmd = LAPD_CMD_REJ; - break; - default: - LOGP(DMI, LOGL_ERROR, "unknown LAPD S cmd %x\n", data[2]); - return NULL; - }; - } else if ((data[2] & 3) == 3) { - typ = LAPD_TYPE_U; - pf = (data[2] >> 4) & 1; - int val = data[2] & ~(1 << 4); - switch (val) { - case 0x6f: - cmd = LAPD_CMD_SABME; - break; - case 0x0f: - cmd = LAPD_CMD_DM; - break; - case 0x03: - cmd = LAPD_CMD_UI; - break; - case 0x43: - cmd = LAPD_CMD_DISC; - break; - case 0x63: - cmd = LAPD_CMD_UA; - break; - case 0x87: - cmd = LAPD_CMD_FRMR; - break; - case 0xaf: - cmd = LAPD_CMD_XID; - break; - - default: - LOGP(DMI, LOGL_ERROR, "unknown U cmd %x " - "(pf %x data %x)\n", val, pf, data[2]); - return NULL; - }; - }; - - contents = &data[4]; - if (typ == LAPD_TYPE_U) - contents--; - *ilen = len - (contents - data); - - if (tei == 127) - lapd_tei_receive(li, contents, *ilen); - - teip = teip_from_tei(li, tei); - if (!teip) { - LOGP(DMI, LOGL_NOTICE, "Unknown TEI %u\n", tei); - return NULL; - } - - sap = lapd_sap_find(teip, sapi); - if (!sap) { - LOGP(DMI, LOGL_INFO, "No SAP for TEI=%u / SAPI=%u, " - "allocating\n", tei, sapi); - sap = lapd_sap_alloc(teip, sapi); - } - - DEBUGP(DMI, "<- %c %s sapi %x tei %3d cmd %x pf %x ns %3d nr %3d " - "ilen %d teip %p vs %d va %d vr %d len %d\n", - lapd_msg_types[typ], lapd_cmd_types[cmd], sapi, tei, command, pf, - ns, nr, *ilen, teip, sap->vs, sap->va, sap->vr, len); - - switch (cmd) { - case LAPD_CMD_I: - if (ns != sap->vr) { - DEBUGP(DMI, "ns %d != vr %d\n", ns, sap->vr); - if (ns == ((sap->vr - 1) & 0x7f)) { - DEBUGP(DMI, "DOUBLE FRAME, ignoring\n"); - cmd = 0; // ignore - } else { - assert(0); - }; - } else { - //printf("IN SEQUENCE\n"); - sap->vr = (ns + 1) & 0x7f; // FIXME: hack! - }; - - break; - case LAPD_CMD_UI: - break; - case LAPD_CMD_SABME: - sap->vs = 0; - sap->vr = 0; - sap->va = 0; - - // ua - resp[l++] = data[0]; - resp[l++] = (tei << 1) | 1; - resp[l++] = 0x73; - li->transmit_cb(resp, l, li->cbdata); - if (teip->state != LAPD_TEI_ACTIVE) { - if (teip->state == LAPD_TEI_ASSIGNED) { - lapd_tei_set_state(teip, - LAPD_TEI_ACTIVE); - //printf("ASSIGNED and ACTIVE\n"); - } else { -#if 0 - DEBUGP(DMI, "rr in strange state, send rej\n"); - - // rej - resp[l++] = (sap-> sapi << 2) | (li->network_side ? 0 : 2); - resp[l++] = (tei << 1) | 1; - resp[l++] = 0x09; //rej - resp[l++] = ((sap->vr + 1) << 1) | 0; - li->transmit_cb(resp, l, li->cbdata); - pf = 0; // dont reply -#endif - }; - }; - - *prim = LAPD_MPH_ACTIVATE_IND; - break; - case LAPD_CMD_UA: - sap->vs = 0; - sap->vr = 0; - sap->va = 0; - lapd_tei_set_state(teip, LAPD_TEI_ACTIVE); - lapd_sap_set_state(teip, sapi, SAP_STATE_ACTIVE); - *prim = LAPD_MPH_ACTIVATE_IND; - break; - case LAPD_CMD_RR: - sap->va = (nr & 0x7f); -#if 0 - if (teip->state != LAPD_TEI_ACTIVE) { - if (teip->state == LAPD_TEI_ASSIGNED) { - lapd_tei_set_state(teip, LAPD_TEI_ACTIVE); - *prim = LAPD_MPH_ACTIVATE_IND; - //printf("ASSIGNED and ACTIVE\n"); - } else { -#if 0 - DEBUGP(DMI, "rr in strange " "state, send rej\n"); - - // rej - resp[l++] = (sap-> sapi << 2) | (li->network_side ? 0 : 2); - resp[l++] = (tei << 1) | 1; - resp[l++] = 0x09; //rej - resp[l++] = - ((sap->vr + 1) << 1) | 0; - li->transmit_cb(resp, l, li->cbdata); - pf = 0; // dont reply -#endif - }; - }; -#endif - if (pf) { - // interrogating us, send rr - resp[l++] = data[0]; - resp[l++] = (tei << 1) | 1; - resp[l++] = 0x01; // rr - resp[l++] = (LAPD_NR(sap) << 1) | (data[3] & 1); // pf bit from req - - li->transmit_cb(resp, l, li->cbdata); - - }; - break; - case LAPD_CMD_FRMR: - // frame reject -#if 0 - if (teip->state == LAPD_TEI_ACTIVE) - *prim = LAPD_MPH_DEACTIVATE_IND; - lapd_tei_set_state(teip, LAPD_TEI_ASSIGNED); -#endif - LOGP(DMI, LOGL_NOTICE, "frame reject, ignoring\n"); - break; - case LAPD_CMD_DISC: - // disconnect - resp[l++] = data[0]; - resp[l++] = (tei << 1) | 1; - resp[l++] = 0x73; - li->transmit_cb(resp, l, li->cbdata); - lapd_tei_set_state(teip, LAPD_TEI_NONE); - break; - default: - LOGP(DMI, LOGL_NOTICE, "unknown cmd for tei %d (cmd %x)\n", - tei, cmd); - break; - } - - if (typ == LAPD_TYPE_I) { - /* send rr - * Thu Jan 22 19:17:13 2009 <4000> sangoma.c:340 read (62/25) 4: fa 33 01 0a - * lapd <- S RR sapi 3e tei 25 cmd 0 pf 0 ns -1 nr 5 ilen 0 teip 0x613800 vs 7 va 5 vr 2 len 4 - */ - - /* interrogating us, send rr */ - DEBUGP(DMI, "Sending RR response\n"); - resp[l++] = data[0]; - resp[l++] = (tei << 1) | 1; - resp[l++] = 0x01; // rr - resp[l++] = (LAPD_NR(sap) << 1) | (data[3] & 1); // pf bit from req - - li->transmit_cb(resp, l, li->cbdata); - - if (cmd != 0) { - *prim = LAPD_DL_DATA_IND; - return contents; - } - } else if (tei != 127 && typ == LAPD_TYPE_U && cmd == LAPD_CMD_UI) { - *prim = LAPD_DL_UNITDATA_IND; - return contents; - } - - return NULL; -}; - -/* low-level function to send a single SABM message */ -static int lapd_send_sabm(struct lapd_instance *li, uint8_t tei, uint8_t sapi) -{ - struct msgb *msg = msgb_alloc_headroom(1024, 128, "LAPD SABM"); - if (!msg) - return -ENOMEM; - - DEBUGP(DMI, "Sending SABM for TEI=%u, SAPI=%u\n", tei, sapi); - - msgb_put_u8(msg, (sapi << 2) | (li->network_side ? 2 : 0)); - msgb_put_u8(msg, (tei << 1) | 1); - msgb_put_u8(msg, 0x7F); - - li->transmit_cb(msg->data, msg->len, li->cbdata); - - msgb_free(msg); - - return 0; -} - -/* timer call-back function for SABM re-transmission */ -static void sabme_timer_cb(void *_sap) -{ - struct lapd_sap *sap = _sap; - - lapd_send_sabm(sap->tei->li, sap->tei->tei, sap->sapi); - - if (sap->state == SAP_STATE_SABM_RETRANS) - bsc_schedule_timer(&sap->sabme_timer, SABM_INTERVAL); -} - -/* Start a (user-side) SAP for the specified TEI/SAPI on the LAPD instance */ -int lapd_sap_start(struct lapd_instance *li, uint8_t tei, uint8_t sapi) -{ - struct lapd_sap *sap; - struct lapd_tei *teip; - - teip = teip_from_tei(li, tei); - if (!teip) - teip = lapd_tei_alloc(li, tei); - - sap = lapd_sap_find(teip, sapi); - if (sap) - return -EEXIST; - - sap = lapd_sap_alloc(teip, sapi); - - lapd_sap_set_state(teip, sapi, SAP_STATE_SABM_RETRANS); - - return 0; -} - -/* Stop a (user-side) SAP for the specified TEI/SAPI on the LAPD instance */ -int lapd_sap_stop(struct lapd_instance *li, uint8_t tei, uint8_t sapi) -{ - struct lapd_tei *teip; - struct lapd_sap *sap; - - teip = teip_from_tei(li, tei); - if (!teip) - return -ENODEV; - - sap = lapd_sap_find(teip, sapi); - if (!sap) - return -ENODEV; - - lapd_sap_set_state(teip, sapi, SAP_STATE_INACTIVE); - - llist_del(&sap->list); - talloc_free(sap); - - return 0; -} - -/* Transmit Data (I-Frame) on the given LAPD Instance / TEI / SAPI */ -void lapd_transmit(struct lapd_instance *li, uint8_t tei, uint8_t sapi, - uint8_t *data, unsigned int len) -{ - struct lapd_tei *teip = teip_from_tei(li, tei); - struct lapd_sap *sap; - - if (!teip) { - LOGP(DMI, LOGL_ERROR, "Cannot transmit on non-existing " - "TEI %u\n", tei); - return; - } - - sap = lapd_sap_find(teip, sapi); - if (!sap) { - LOGP(DMI, LOGL_INFO, "Tx on unknown SAPI=%u in TEI=%u, " - "allocating\n", sapi, tei); - sap = lapd_sap_alloc(teip, sapi); - } - - /* prepend stuff */ - uint8_t buf[10000]; - memset(buf, 0, sizeof(buf)); - memmove(buf + 4, data, len); - len += 4; - - buf[0] = (sapi << 2) | (li->network_side ? 2 : 0); - buf[1] = (tei << 1) | 1; - buf[2] = (LAPD_NS(sap) << 1); - buf[3] = (LAPD_NR(sap) << 1) | 0; - - sap->vs = (sap->vs + 1) & 0x7f; - - li->transmit_cb(buf, len, li->cbdata); -}; - -/* Allocate a new LAPD instance */ -struct lapd_instance *lapd_instance_alloc(int network_side, - void (*tx_cb)(uint8_t *data, int len, - void *cbdata), void *cbdata) -{ - struct lapd_instance *li; - - li = talloc_zero(NULL, struct lapd_instance); - if (!li) - return NULL; - - li->transmit_cb = tx_cb; - li->cbdata = cbdata; - li->network_side = network_side; - INIT_LLIST_HEAD(&li->tei_list); - - return li; -} diff --git a/openbsc/src/abis/input/lapd.h b/openbsc/src/abis/input/lapd.h deleted file mode 100644 index fd11edaa3..000000000 --- a/openbsc/src/abis/input/lapd.h +++ /dev/null @@ -1,46 +0,0 @@ -#ifndef OPENBSC_LAPD_H -#define OPENBSC_LAPD_H - -#include - -#include - -typedef enum { - LAPD_MPH_NONE = 0, - - LAPD_MPH_ACTIVATE_IND, - LAPD_MPH_DEACTIVATE_IND, - - LAPD_DL_DATA_IND, - LAPD_DL_UNITDATA_IND, - -} lapd_mph_type; - -struct lapd_instance { - struct llist_head list; /* list of LAPD instances */ - int network_side; - - void (*transmit_cb)(uint8_t *data, int len, void *cbdata); - void *cbdata; - - struct llist_head tei_list; /* list of TEI in this LAPD instance */ -}; - -extern uint8_t *lapd_receive(struct lapd_instance *li, uint8_t *data, unsigned int len, - int *ilen, lapd_mph_type *prim); - -extern void lapd_transmit(struct lapd_instance *li, uint8_t tei, uint8_t sapi, - uint8_t *data, unsigned int len); - -struct lapd_instance *lapd_instance_alloc(int network_side, - void (*tx_cb)(uint8_t *data, int len, - void *cbdata), void *cbdata); - - -/* Start a (user-side) SAP for the specified TEI/SAPI on the LAPD instance */ -int lapd_sap_start(struct lapd_instance *li, uint8_t tei, uint8_t sapi); - -/* Stop a (user-side) SAP for the specified TEI/SAPI on the LAPD instance */ -int lapd_sap_stop(struct lapd_instance *li, uint8_t tei, uint8_t sapi); - -#endif /* OPENBSC_LAPD_H */ diff --git a/openbsc/src/abis/input/misdn.c b/openbsc/src/abis/input/misdn.c deleted file mode 100644 index 459887917..000000000 --- a/openbsc/src/abis/input/misdn.c +++ /dev/null @@ -1,542 +0,0 @@ -/* OpenBSC Abis input driver for mISDNuser */ - -/* (C) 2008-2009 by Harald Welte - * (C) 2009 by Holger Hans Peter Freyther - * - * All Rights Reserved - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation; either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - * - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -//#define AF_COMPATIBILITY_FUNC -//#include -#ifndef AF_ISDN -#define AF_ISDN 34 -#define PF_ISDN AF_ISDN -#endif - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#define TS1_ALLOC_SIZE 300 - -struct prim_name { - unsigned int prim; - const char *name; -}; - -const struct prim_name prim_names[] = { - { PH_CONTROL_IND, "PH_CONTROL_IND" }, - { PH_DATA_IND, "PH_DATA_IND" }, - { PH_DATA_CNF, "PH_DATA_CNF" }, - { PH_ACTIVATE_IND, "PH_ACTIVATE_IND" }, - { DL_ESTABLISH_IND, "DL_ESTABLISH_IND" }, - { DL_ESTABLISH_CNF, "DL_ESTABLISH_CNF" }, - { DL_RELEASE_IND, "DL_RELEASE_IND" }, - { DL_RELEASE_CNF, "DL_RELEASE_CNF" }, - { DL_DATA_IND, "DL_DATA_IND" }, - { DL_UNITDATA_IND, "DL_UNITDATA_IND" }, - { DL_INFORMATION_IND, "DL_INFORMATION_IND" }, - { MPH_ACTIVATE_IND, "MPH_ACTIVATE_IND" }, - { MPH_DEACTIVATE_IND, "MPH_DEACTIVATE_IND" }, -}; - -const char *get_prim_name(unsigned int prim) -{ - int i; - - for (i = 0; i < ARRAY_SIZE(prim_names); i++) { - if (prim_names[i].prim == prim) - return prim_names[i].name; - } - - return "UNKNOWN"; -} - -static int handle_ts1_read(struct bsc_fd *bfd) -{ - struct e1inp_line *line = bfd->data; - unsigned int ts_nr = bfd->priv_nr; - struct e1inp_ts *e1i_ts = &line->ts[ts_nr-1]; - struct e1inp_sign_link *link; - struct msgb *msg = msgb_alloc(TS1_ALLOC_SIZE, "mISDN TS1"); - struct sockaddr_mISDN l2addr; - struct mISDNhead *hh; - socklen_t alen; - int ret; - - if (!msg) - return -ENOMEM; - - hh = (struct mISDNhead *) msg->data; - - alen = sizeof(l2addr); - ret = recvfrom(bfd->fd, msg->data, 300, 0, - (struct sockaddr *) &l2addr, &alen); - if (ret < 0) { - fprintf(stderr, "recvfrom error %s\n", strerror(errno)); - return ret; - } - - if (alen != sizeof(l2addr)) { - fprintf(stderr, "%s error len\n", __func__); - return -EINVAL; - } - - msgb_put(msg, ret); - - DEBUGP(DMI, "alen =%d, dev(%d) channel(%d) sapi(%d) tei(%d)\n", - alen, l2addr.dev, l2addr.channel, l2addr.sapi, l2addr.tei); - - DEBUGP(DMI, "<= len = %d, prim(0x%x) id(0x%x): %s\n", - ret, hh->prim, hh->id, get_prim_name(hh->prim)); - - switch (hh->prim) { - case DL_INFORMATION_IND: - /* mISDN tells us which channel number is allocated for this - * tuple of (SAPI, TEI). */ - DEBUGP(DMI, "DL_INFORMATION_IND: use channel(%d) sapi(%d) tei(%d) for now\n", - l2addr.channel, l2addr.sapi, l2addr.tei); - link = e1inp_lookup_sign_link(e1i_ts, l2addr.tei, l2addr.sapi); - if (!link) { - DEBUGPC(DMI, "mISDN message for unknown sign_link\n"); - msgb_free(msg); - return -EINVAL; - } - /* save the channel number in the driver private struct */ - link->driver.misdn.channel = l2addr.channel; - break; - case DL_ESTABLISH_IND: - DEBUGP(DMI, "DL_ESTABLISH_IND: channel(%d) sapi(%d) tei(%d)\n", - l2addr.channel, l2addr.sapi, l2addr.tei); - /* For some strange reason, sometimes the DL_INFORMATION_IND tells - * us the wrong channel, and we only get the real channel number - * during the DL_ESTABLISH_IND */ - link = e1inp_lookup_sign_link(e1i_ts, l2addr.tei, l2addr.sapi); - if (!link) { - DEBUGPC(DMI, "mISDN message for unknown sign_link\n"); - msgb_free(msg); - return -EINVAL; - } - /* save the channel number in the driver private struct */ - link->driver.misdn.channel = l2addr.channel; - ret = e1inp_event(e1i_ts, S_INP_TEI_UP, l2addr.tei, l2addr.sapi); - break; - case DL_RELEASE_IND: - DEBUGP(DMI, "DL_RELEASE_IND: channel(%d) sapi(%d) tei(%d)\n", - l2addr.channel, l2addr.sapi, l2addr.tei); - ret = e1inp_event(e1i_ts, S_INP_TEI_DN, l2addr.tei, l2addr.sapi); - break; - case DL_DATA_IND: - case DL_UNITDATA_IND: - msg->l2h = msg->data + MISDN_HEADER_LEN; - DEBUGP(DMI, "RX: %s\n", hexdump(msgb_l2(msg), ret - MISDN_HEADER_LEN)); - ret = e1inp_rx_ts(e1i_ts, msg, l2addr.tei, l2addr.sapi); - break; - case PH_ACTIVATE_IND: - DEBUGP(DMI, "PH_ACTIVATE_IND: channel(%d) sapi(%d) tei(%d)\n", - l2addr.channel, l2addr.sapi, l2addr.tei); - break; - case PH_DEACTIVATE_IND: - DEBUGP(DMI, "PH_DEACTIVATE_IND: channel(%d) sapi(%d) tei(%d)\n", - l2addr.channel, l2addr.sapi, l2addr.tei); - break; - default: - break; - } - return ret; -} - -static int ts_want_write(struct e1inp_ts *e1i_ts) -{ - /* We never include the mISDN B-Channel FD into the - * writeset, since it doesn't support poll() based - * write flow control */ - if (e1i_ts->type == E1INP_TS_TYPE_TRAU) - return 0; - - e1i_ts->driver.misdn.fd.when |= BSC_FD_WRITE; - - return 0; -} - -static void timeout_ts1_write(void *data) -{ - struct e1inp_ts *e1i_ts = (struct e1inp_ts *)data; - - /* trigger write of ts1, due to tx delay timer */ - ts_want_write(e1i_ts); -} - -static int handle_ts1_write(struct bsc_fd *bfd) -{ - struct e1inp_line *line = bfd->data; - unsigned int ts_nr = bfd->priv_nr; - struct e1inp_ts *e1i_ts = &line->ts[ts_nr-1]; - struct e1inp_sign_link *sign_link; - struct sockaddr_mISDN sa; - struct msgb *msg; - struct mISDNhead *hh; - u_int8_t *l2_data; - int ret; - - bfd->when &= ~BSC_FD_WRITE; - - /* get the next msg for this timeslot */ - msg = e1inp_tx_ts(e1i_ts, &sign_link); - if (!msg) { - /* no message after tx delay timer */ - return 0; - } - - l2_data = msg->data; - - /* prepend the mISDNhead */ - hh = (struct mISDNhead *) msgb_push(msg, sizeof(*hh)); - hh->prim = DL_DATA_REQ; - - DEBUGP(DMI, "TX channel(%d) TEI(%d) SAPI(%d): %s\n", - sign_link->driver.misdn.channel, sign_link->tei, - sign_link->sapi, hexdump(l2_data, msg->len - MISDN_HEADER_LEN)); - - /* construct the sockaddr */ - sa.family = AF_ISDN; - sa.sapi = sign_link->sapi; - sa.dev = sign_link->tei; - sa.channel = sign_link->driver.misdn.channel; - - ret = sendto(bfd->fd, msg->data, msg->len, 0, - (struct sockaddr *)&sa, sizeof(sa)); - if (ret < 0) - fprintf(stderr, "%s sendto failed %d\n", __func__, ret); - msgb_free(msg); - - /* set tx delay timer for next event */ - e1i_ts->sign.tx_timer.cb = timeout_ts1_write; - e1i_ts->sign.tx_timer.data = e1i_ts; - bsc_schedule_timer(&e1i_ts->sign.tx_timer, 0, e1i_ts->sign.delay); - - return ret; -} - -#define BCHAN_TX_GRAN 160 -/* write to a B channel TS */ -static int handle_tsX_write(struct bsc_fd *bfd) -{ - struct e1inp_line *line = bfd->data; - unsigned int ts_nr = bfd->priv_nr; - struct e1inp_ts *e1i_ts = &line->ts[ts_nr-1]; - struct mISDNhead *hh; - u_int8_t tx_buf[BCHAN_TX_GRAN + sizeof(*hh)]; - struct subch_mux *mx = &e1i_ts->trau.mux; - int ret; - - hh = (struct mISDNhead *) tx_buf; - hh->prim = PH_DATA_REQ; - - subchan_mux_out(mx, tx_buf+sizeof(*hh), BCHAN_TX_GRAN); - - DEBUGP(DMIB, "BCHAN TX: %s\n", - hexdump(tx_buf+sizeof(*hh), BCHAN_TX_GRAN)); - - ret = send(bfd->fd, tx_buf, sizeof(*hh) + BCHAN_TX_GRAN, 0); - if (ret < sizeof(*hh) + BCHAN_TX_GRAN) - DEBUGP(DMIB, "send returns %d instead of %zu\n", ret, - sizeof(*hh) + BCHAN_TX_GRAN); - - return ret; -} - -#define TSX_ALLOC_SIZE 4096 -/* FIXME: read from a B channel TS */ -static int handle_tsX_read(struct bsc_fd *bfd) -{ - struct e1inp_line *line = bfd->data; - unsigned int ts_nr = bfd->priv_nr; - struct e1inp_ts *e1i_ts = &line->ts[ts_nr-1]; - struct msgb *msg = msgb_alloc(TSX_ALLOC_SIZE, "mISDN TSx"); - struct mISDNhead *hh; - int ret; - - if (!msg) - return -ENOMEM; - - hh = (struct mISDNhead *) msg->data; - - ret = recv(bfd->fd, msg->data, TSX_ALLOC_SIZE, 0); - if (ret < 0) { - fprintf(stderr, "recvfrom error %s\n", strerror(errno)); - return ret; - } - - msgb_put(msg, ret); - - if (hh->prim != PH_CONTROL_IND) - DEBUGP(DMIB, "<= BCHAN len = %d, prim(0x%x) id(0x%x): %s\n", - ret, hh->prim, hh->id, get_prim_name(hh->prim)); - - switch (hh->prim) { - case PH_DATA_IND: - msg->l2h = msg->data + MISDN_HEADER_LEN; - DEBUGP(DMIB, "BCHAN RX: %s\n", - hexdump(msgb_l2(msg), ret - MISDN_HEADER_LEN)); - ret = e1inp_rx_ts(e1i_ts, msg, 0, 0); - break; - case PH_ACTIVATE_IND: - case PH_DATA_CNF: - /* physical layer indicates that data has been sent, - * we thus can send some more data */ - ret = handle_tsX_write(bfd); - default: - break; - } - /* FIXME: why do we free signalling msgs in the caller, and trau not? */ - msgb_free(msg); - - return ret; -} - -/* callback from select.c in case one of the fd's can be read/written */ -static int misdn_fd_cb(struct bsc_fd *bfd, unsigned int what) -{ - struct e1inp_line *line = bfd->data; - unsigned int ts_nr = bfd->priv_nr; - unsigned int idx = ts_nr-1; - struct e1inp_ts *e1i_ts = &line->ts[idx]; - int rc = 0; - - switch (e1i_ts->type) { - case E1INP_TS_TYPE_SIGN: - if (what & BSC_FD_READ) - rc = handle_ts1_read(bfd); - if (what & BSC_FD_WRITE) - rc = handle_ts1_write(bfd); - break; - case E1INP_TS_TYPE_TRAU: - if (what & BSC_FD_READ) - rc = handle_tsX_read(bfd); - /* We never include the mISDN B-Channel FD into the - * writeset, since it doesn't support poll() based - * write flow control */ - break; - default: - fprintf(stderr, "unknown E1 TS type %u\n", e1i_ts->type); - break; - } - - return rc; -} - -static int activate_bchan(struct e1inp_line *line, int ts, int act) -{ - struct mISDNhead hh; - int ret; - unsigned int idx = ts-1; - struct e1inp_ts *e1i_ts = &line->ts[idx]; - struct bsc_fd *bfd = &e1i_ts->driver.misdn.fd; - - fprintf(stdout, "activate bchan\n"); - if (act) - hh.prim = PH_ACTIVATE_REQ; - else - hh.prim = PH_DEACTIVATE_REQ; - - hh.id = MISDN_ID_ANY; - ret = sendto(bfd->fd, &hh, sizeof(hh), 0, NULL, 0); - if (ret < 0) { - fprintf(stdout, "could not send ACTIVATE_RQ %s\n", - strerror(errno)); - } - - return ret; -} - -static int mi_e1_line_update(struct e1inp_line *line); - -struct e1inp_driver misdn_driver = { - .name = "misdn", - .want_write = ts_want_write, - .default_delay = 50000, - .line_update = &mi_e1_line_update, -}; - -static int mi_e1_setup(struct e1inp_line *line, int release_l2) -{ - int ts, ret; - - /* TS0 is CRC4, don't need any fd for it */ - for (ts = 1; ts < NUM_E1_TS; ts++) { - unsigned int idx = ts-1; - struct e1inp_ts *e1i_ts = &line->ts[idx]; - struct bsc_fd *bfd = &e1i_ts->driver.misdn.fd; - struct sockaddr_mISDN addr; - - bfd->data = line; - bfd->priv_nr = ts; - bfd->cb = misdn_fd_cb; - - switch (e1i_ts->type) { - case E1INP_TS_TYPE_NONE: - continue; - break; - case E1INP_TS_TYPE_SIGN: - bfd->fd = socket(PF_ISDN, SOCK_DGRAM, ISDN_P_LAPD_NT); - bfd->when = BSC_FD_READ; - break; - case E1INP_TS_TYPE_TRAU: - bfd->fd = socket(PF_ISDN, SOCK_DGRAM, ISDN_P_B_RAW); - /* We never include the mISDN B-Channel FD into the - * writeset, since it doesn't support poll() based - * write flow control */ - bfd->when = BSC_FD_READ; - break; - } - - if (bfd->fd < 0) { - fprintf(stderr, "%s could not open socket %s\n", - __func__, strerror(errno)); - return bfd->fd; - } - - memset(&addr, 0, sizeof(addr)); - addr.family = AF_ISDN; - addr.dev = line->num; - switch (e1i_ts->type) { - case E1INP_TS_TYPE_SIGN: - addr.channel = 0; - /* SAPI not supported yet in kernel */ - //addr.sapi = e1inp_ts->sign.sapi; - addr.sapi = 0; - addr.tei = GROUP_TEI; - break; - case E1INP_TS_TYPE_TRAU: - addr.channel = ts; - break; - default: - DEBUGP(DMI, "unsupported E1 TS type: %u\n", - e1i_ts->type); - break; - } - - ret = bind(bfd->fd, (struct sockaddr *) &addr, sizeof(addr)); - if (ret < 0) { - fprintf(stderr, "could not bind l2 socket %s\n", - strerror(errno)); - return -EIO; - } - - if (e1i_ts->type == E1INP_TS_TYPE_SIGN) { - ret = ioctl(bfd->fd, IMCLEAR_L2, &release_l2); - if (ret < 0) { - fprintf(stderr, "could not send IOCTL IMCLEAN_L2 %s\n", strerror(errno)); - return -EIO; - } - } - - /* FIXME: only activate B-Channels once we start to - * use them to conserve CPU power */ - if (e1i_ts->type == E1INP_TS_TYPE_TRAU) - activate_bchan(line, ts, 1); - - ret = bsc_register_fd(bfd); - if (ret < 0) { - fprintf(stderr, "could not register FD: %s\n", - strerror(ret)); - return ret; - } - } - - return 0; -} - -static int mi_e1_line_update(struct e1inp_line *line) -{ - struct mISDN_devinfo devinfo; - int sk, ret, cnt; - - if (line->driver != &misdn_driver) - return -EINVAL; - - /* open the ISDN card device */ - sk = socket(PF_ISDN, SOCK_RAW, ISDN_P_BASE); - if (sk < 0) { - fprintf(stderr, "%s could not open socket %s\n", - __func__, strerror(errno)); - return sk; - } - - ret = ioctl(sk, IMGETCOUNT, &cnt); - if (ret) { - fprintf(stderr, "%s error getting interf count: %s\n", - __func__, strerror(errno)); - close(sk); - return -ENODEV; - } - //DEBUGP(DMI,"%d device%s found\n", cnt, (cnt==1)?"":"s"); - printf("%d device%s found\n", cnt, (cnt==1)?"":"s"); -#if 1 - devinfo.id = line->num; - ret = ioctl(sk, IMGETDEVINFO, &devinfo); - if (ret < 0) { - fprintf(stdout, "error getting info for device %d: %s\n", - line->num, strerror(errno)); - return -ENODEV; - } - fprintf(stdout, " id: %d\n", devinfo.id); - fprintf(stdout, " Dprotocols: %08x\n", devinfo.Dprotocols); - fprintf(stdout, " Bprotocols: %08x\n", devinfo.Bprotocols); - fprintf(stdout, " protocol: %d\n", devinfo.protocol); - fprintf(stdout, " nrbchan: %d\n", devinfo.nrbchan); - fprintf(stdout, " name: %s\n", devinfo.name); -#endif - - if (!(devinfo.Dprotocols & (1 << ISDN_P_NT_E1))) { - fprintf(stderr, "error: card is not of type E1 (NT-mode)\n"); - return -EINVAL; - } - - ret = mi_e1_setup(line, 1); - if (ret) - return ret; - - return 0; -} - -void e1inp_misdn_init(void) -{ - /* register the driver with the core */ - e1inp_driver_register(&misdn_driver); -} diff --git a/openbsc/src/bsc/Makefile.am b/openbsc/src/bsc/Makefile.am deleted file mode 100644 index 91be7fa62..000000000 --- a/openbsc/src/bsc/Makefile.am +++ /dev/null @@ -1,21 +0,0 @@ -INCLUDES = $(all_includes) -I$(top_srcdir)/include -I$(top_builddir) -AM_CFLAGS=-Wall $(LIBOSMOCORE_CFLAGS) $(LIBOSMOVTY_CFLAGS) $(COVERAGE_CFLAGS) -AM_LDFLAGS = $(LIBOSMOCORE_LIBS) $(COVERAGE_LDFLAGS) - -noinst_LIBRARIES = libbsc.a - -libbsc_a_SOURCES = abis_nm.c abis_nm_vty.c \ - abis_om2000.c abis_om2000_vty.c \ - abis_rsl.c bsc_rll.c \ - paging.c \ - bts_ericsson_rbs2000.c bts_ipaccess_nanobts.c bts_siemens_bs11.c bts_unknown.c \ - chan_alloc.c \ - gsm_subscriber_base.c \ - handover_decision.c handover_logic.c meas_rep.c \ - rest_octets.c system_information.c \ - e1_config.c \ - transaction.c \ - bsc_api.c bsc_msc.c bsc_vty.c \ - gsm_04_08_utils.c \ - bsc_init.c - diff --git a/openbsc/src/bsc/abis_nm.c b/openbsc/src/bsc/abis_nm.c deleted file mode 100644 index 45db8bac5..000000000 --- a/openbsc/src/bsc/abis_nm.c +++ /dev/null @@ -1,3126 +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 - * - * All Rights Reserved - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation; either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - * - */ - - -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include - -#define OM_ALLOC_SIZE 1024 -#define OM_HEADROOM_SIZE 128 -#define IPACC_SEGMENT_SIZE 245 - -/* unidirectional messages from BTS to BSC */ -static const enum abis_nm_msgtype reports[] = { - NM_MT_SW_ACTIVATED_REP, - NM_MT_TEST_REP, - NM_MT_STATECHG_EVENT_REP, - NM_MT_FAILURE_EVENT_REP, -}; - -/* messages without ACK/NACK */ -static const enum abis_nm_msgtype no_ack_nack[] = { - NM_MT_MEAS_RES_REQ, - NM_MT_STOP_MEAS, - NM_MT_START_MEAS, -}; - -/* Messages related to software load */ -static const enum abis_nm_msgtype sw_load_msgs[] = { - NM_MT_LOAD_INIT_ACK, - NM_MT_LOAD_INIT_NACK, - NM_MT_LOAD_SEG_ACK, - NM_MT_LOAD_ABORT, - NM_MT_LOAD_END_ACK, - NM_MT_LOAD_END_NACK, - //NM_MT_SW_ACT_REQ, - NM_MT_ACTIVATE_SW_ACK, - NM_MT_ACTIVATE_SW_NACK, - NM_MT_SW_ACTIVATED_REP, -}; - -static const enum abis_nm_msgtype nacks[] = { - NM_MT_LOAD_INIT_NACK, - NM_MT_LOAD_END_NACK, - NM_MT_SW_ACT_REQ_NACK, - NM_MT_ACTIVATE_SW_NACK, - NM_MT_ESTABLISH_TEI_NACK, - NM_MT_CONN_TERR_SIGN_NACK, - NM_MT_DISC_TERR_SIGN_NACK, - NM_MT_CONN_TERR_TRAF_NACK, - NM_MT_DISC_TERR_TRAF_NACK, - NM_MT_CONN_MDROP_LINK_NACK, - NM_MT_DISC_MDROP_LINK_NACK, - NM_MT_SET_BTS_ATTR_NACK, - NM_MT_SET_RADIO_ATTR_NACK, - NM_MT_SET_CHAN_ATTR_NACK, - NM_MT_PERF_TEST_NACK, - NM_MT_SEND_TEST_REP_NACK, - NM_MT_STOP_TEST_NACK, - NM_MT_STOP_EVENT_REP_NACK, - NM_MT_REST_EVENT_REP_NACK, - NM_MT_CHG_ADM_STATE_NACK, - NM_MT_CHG_ADM_STATE_REQ_NACK, - NM_MT_REP_OUTST_ALARMS_NACK, - NM_MT_CHANGEOVER_NACK, - NM_MT_OPSTART_NACK, - NM_MT_REINIT_NACK, - NM_MT_SET_SITE_OUT_NACK, - NM_MT_CHG_HW_CONF_NACK, - NM_MT_GET_ATTR_NACK, - NM_MT_SET_ALARM_THRES_NACK, - NM_MT_BS11_BEGIN_DB_TX_NACK, - NM_MT_BS11_END_DB_TX_NACK, - NM_MT_BS11_CREATE_OBJ_NACK, - NM_MT_BS11_DELETE_OBJ_NACK, -}; - -static const struct value_string nack_names[] = { - { NM_MT_LOAD_INIT_NACK, "SOFTWARE LOAD INIT" }, - { NM_MT_LOAD_END_NACK, "SOFTWARE LOAD END" }, - { NM_MT_SW_ACT_REQ_NACK, "SOFTWARE ACTIVATE REQUEST" }, - { NM_MT_ACTIVATE_SW_NACK, "ACTIVATE SOFTWARE" }, - { NM_MT_ESTABLISH_TEI_NACK, "ESTABLISH TEI" }, - { NM_MT_CONN_TERR_SIGN_NACK, "CONNECT TERRESTRIAL SIGNALLING" }, - { NM_MT_DISC_TERR_SIGN_NACK, "DISCONNECT TERRESTRIAL SIGNALLING" }, - { NM_MT_CONN_TERR_TRAF_NACK, "CONNECT TERRESTRIAL TRAFFIC" }, - { NM_MT_DISC_TERR_TRAF_NACK, "DISCONNECT TERRESTRIAL TRAFFIC" }, - { NM_MT_CONN_MDROP_LINK_NACK, "CONNECT MULTI-DROP LINK" }, - { NM_MT_DISC_MDROP_LINK_NACK, "DISCONNECT MULTI-DROP LINK" }, - { NM_MT_SET_BTS_ATTR_NACK, "SET BTS ATTRIBUTE" }, - { NM_MT_SET_RADIO_ATTR_NACK, "SET RADIO ATTRIBUTE" }, - { NM_MT_SET_CHAN_ATTR_NACK, "SET CHANNEL ATTRIBUTE" }, - { NM_MT_PERF_TEST_NACK, "PERFORM TEST" }, - { NM_MT_SEND_TEST_REP_NACK, "SEND TEST REPORT" }, - { NM_MT_STOP_TEST_NACK, "STOP TEST" }, - { NM_MT_STOP_EVENT_REP_NACK, "STOP EVENT REPORT" }, - { NM_MT_REST_EVENT_REP_NACK, "RESET EVENT REPORT" }, - { NM_MT_CHG_ADM_STATE_NACK, "CHANGE ADMINISTRATIVE STATE" }, - { NM_MT_CHG_ADM_STATE_REQ_NACK, - "CHANGE ADMINISTRATIVE STATE REQUEST" }, - { NM_MT_REP_OUTST_ALARMS_NACK, "REPORT OUTSTANDING ALARMS" }, - { NM_MT_CHANGEOVER_NACK, "CHANGEOVER" }, - { NM_MT_OPSTART_NACK, "OPSTART" }, - { NM_MT_REINIT_NACK, "REINIT" }, - { NM_MT_SET_SITE_OUT_NACK, "SET SITE OUTPUT" }, - { NM_MT_CHG_HW_CONF_NACK, "CHANGE HARDWARE CONFIGURATION" }, - { NM_MT_GET_ATTR_NACK, "GET ATTRIBUTE" }, - { NM_MT_SET_ALARM_THRES_NACK, "SET ALARM THRESHOLD" }, - { NM_MT_BS11_BEGIN_DB_TX_NACK, "BS11 BEGIN DATABASE TRANSMISSION" }, - { NM_MT_BS11_END_DB_TX_NACK, "BS11 END DATABASE TRANSMISSION" }, - { NM_MT_BS11_CREATE_OBJ_NACK, "BS11 CREATE OBJECT" }, - { NM_MT_BS11_DELETE_OBJ_NACK, "BS11 DELETE OBJECT" }, - { 0, NULL } -}; - -/* Chapter 9.4.36 */ -static const struct value_string nack_cause_names[] = { - /* General Nack Causes */ - { NM_NACK_INCORR_STRUCT, "Incorrect message structure" }, - { NM_NACK_MSGTYPE_INVAL, "Invalid message type value" }, - { NM_NACK_OBJCLASS_INVAL, "Invalid Object class value" }, - { NM_NACK_OBJCLASS_NOTSUPP, "Object class not supported" }, - { NM_NACK_BTSNR_UNKN, "BTS no. unknown" }, - { NM_NACK_TRXNR_UNKN, "Baseband Transceiver no. unknown" }, - { NM_NACK_OBJINST_UNKN, "Object Instance unknown" }, - { NM_NACK_ATTRID_INVAL, "Invalid attribute identifier value" }, - { NM_NACK_ATTRID_NOTSUPP, "Attribute identifier not supported" }, - { NM_NACK_PARAM_RANGE, "Parameter value outside permitted range" }, - { NM_NACK_ATTRLIST_INCONSISTENT,"Inconsistency in attribute list" }, - { NM_NACK_SPEC_IMPL_NOTSUPP, "Specified implementation not supported" }, - { NM_NACK_CANT_PERFORM, "Message cannot be performed" }, - /* Specific Nack Causes */ - { NM_NACK_RES_NOTIMPL, "Resource not implemented" }, - { NM_NACK_RES_NOTAVAIL, "Resource not available" }, - { NM_NACK_FREQ_NOTAVAIL, "Frequency not available" }, - { NM_NACK_TEST_NOTSUPP, "Test not supported" }, - { NM_NACK_CAPACITY_RESTR, "Capacity restrictions" }, - { NM_NACK_PHYSCFG_NOTPERFORM, "Physical configuration cannot be performed" }, - { NM_NACK_TEST_NOTINIT, "Test not initiated" }, - { NM_NACK_PHYSCFG_NOTRESTORE, "Physical configuration cannot be restored" }, - { NM_NACK_TEST_NOSUCH, "No such test" }, - { NM_NACK_TEST_NOSTOP, "Test cannot be stopped" }, - { NM_NACK_MSGINCONSIST_PHYSCFG, "Message inconsistent with physical configuration" }, - { NM_NACK_FILE_INCOMPLETE, "Complete file notreceived" }, - { NM_NACK_FILE_NOTAVAIL, "File not available at destination" }, - { NM_NACK_FILE_NOTACTIVATE, "File cannot be activate" }, - { NM_NACK_REQ_NOT_GRANT, "Request not granted" }, - { NM_NACK_WAIT, "Wait" }, - { NM_NACK_NOTH_REPORT_EXIST, "Nothing reportable existing" }, - { NM_NACK_MEAS_NOTSUPP, "Measurement not supported" }, - { NM_NACK_MEAS_NOTSTART, "Measurement not started" }, - { 0, NULL } -}; - -static const char *nack_cause_name(u_int8_t cause) -{ - return get_value_string(nack_cause_names, cause); -} - -/* Chapter 9.4.16: Event Type */ -static const struct value_string event_type_names[] = { - { NM_EVT_COMM_FAIL, "communication failure" }, - { NM_EVT_QOS_FAIL, "quality of service failure" }, - { NM_EVT_PROC_FAIL, "processing failure" }, - { NM_EVT_EQUIP_FAIL, "equipment failure" }, - { NM_EVT_ENV_FAIL, "environment failure" }, - { 0, NULL } -}; - -static const char *event_type_name(u_int8_t cause) -{ - return get_value_string(event_type_names, cause); -} - -/* Chapter 9.4.63: Perceived Severity */ -static const struct value_string severity_names[] = { - { NM_SEVER_CEASED, "failure ceased" }, - { NM_SEVER_CRITICAL, "critical failure" }, - { NM_SEVER_MAJOR, "major failure" }, - { NM_SEVER_MINOR, "minor failure" }, - { NM_SEVER_WARNING, "warning level failure" }, - { NM_SEVER_INDETERMINATE, "indeterminate failure" }, - { 0, NULL } -}; - -static const char *severity_name(u_int8_t cause) -{ - return get_value_string(severity_names, cause); -} - -/* Attributes that the BSC can set, not only get, according to Section 9.4 */ -static const enum abis_nm_attr nm_att_settable[] = { - NM_ATT_ADD_INFO, - NM_ATT_ADD_TEXT, - NM_ATT_DEST, - NM_ATT_EVENT_TYPE, - NM_ATT_FILE_DATA, - NM_ATT_GET_ARI, - NM_ATT_HW_CONF_CHG, - NM_ATT_LIST_REQ_ATTR, - NM_ATT_MDROP_LINK, - NM_ATT_MDROP_NEXT, - NM_ATT_NACK_CAUSES, - NM_ATT_OUTST_ALARM, - NM_ATT_PHYS_CONF, - NM_ATT_PROB_CAUSE, - NM_ATT_RAD_SUBC, - NM_ATT_SOURCE, - NM_ATT_SPEC_PROB, - NM_ATT_START_TIME, - NM_ATT_TEST_DUR, - NM_ATT_TEST_NO, - NM_ATT_TEST_REPORT, - NM_ATT_WINDOW_SIZE, - NM_ATT_SEVERITY, - NM_ATT_MEAS_RES, - NM_ATT_MEAS_TYPE, -}; - -const struct tlv_definition nm_att_tlvdef = { - .def = { - [NM_ATT_ABIS_CHANNEL] = { TLV_TYPE_FIXED, 3 }, - [NM_ATT_ADD_INFO] = { TLV_TYPE_TL16V }, - [NM_ATT_ADD_TEXT] = { TLV_TYPE_TL16V }, - [NM_ATT_ADM_STATE] = { TLV_TYPE_TV }, - [NM_ATT_ARFCN_LIST]= { TLV_TYPE_TL16V }, - [NM_ATT_AUTON_REPORT] = { TLV_TYPE_TV }, - [NM_ATT_AVAIL_STATUS] = { TLV_TYPE_TL16V }, - [NM_ATT_BCCH_ARFCN] = { TLV_TYPE_FIXED, 2 }, - [NM_ATT_BSIC] = { TLV_TYPE_TV }, - [NM_ATT_BTS_AIR_TIMER] = { TLV_TYPE_TV }, - [NM_ATT_CCCH_L_I_P] = { TLV_TYPE_TV }, - [NM_ATT_CCCH_L_T] = { TLV_TYPE_TV }, - [NM_ATT_CHAN_COMB] = { TLV_TYPE_TV }, - [NM_ATT_CONN_FAIL_CRIT] = { TLV_TYPE_TL16V }, - [NM_ATT_DEST] = { TLV_TYPE_TL16V }, - [NM_ATT_EVENT_TYPE] = { TLV_TYPE_TV }, - [NM_ATT_FILE_DATA] = { TLV_TYPE_TL16V }, - [NM_ATT_FILE_ID] = { TLV_TYPE_TL16V }, - [NM_ATT_FILE_VERSION] = { TLV_TYPE_TL16V }, - [NM_ATT_GSM_TIME] = { TLV_TYPE_FIXED, 2 }, - [NM_ATT_HSN] = { TLV_TYPE_TV }, - [NM_ATT_HW_CONFIG] = { TLV_TYPE_TL16V }, - [NM_ATT_HW_DESC] = { TLV_TYPE_TL16V }, - [NM_ATT_INTAVE_PARAM] = { TLV_TYPE_TV }, - [NM_ATT_INTERF_BOUND] = { TLV_TYPE_FIXED, 6 }, - [NM_ATT_LIST_REQ_ATTR] = { TLV_TYPE_TL16V }, - [NM_ATT_MAIO] = { TLV_TYPE_TV }, - [NM_ATT_MANUF_STATE] = { TLV_TYPE_TV }, - [NM_ATT_MANUF_THRESH] = { TLV_TYPE_TL16V }, - [NM_ATT_MANUF_ID] = { TLV_TYPE_TL16V }, - [NM_ATT_MAX_TA] = { TLV_TYPE_TV }, - [NM_ATT_MDROP_LINK] = { TLV_TYPE_FIXED, 2 }, - [NM_ATT_MDROP_NEXT] = { TLV_TYPE_FIXED, 2 }, - [NM_ATT_NACK_CAUSES] = { TLV_TYPE_TV }, - [NM_ATT_NY1] = { TLV_TYPE_TV }, - [NM_ATT_OPER_STATE] = { TLV_TYPE_TV }, - [NM_ATT_OVERL_PERIOD] = { TLV_TYPE_TL16V }, - [NM_ATT_PHYS_CONF] = { TLV_TYPE_TL16V }, - [NM_ATT_POWER_CLASS] = { TLV_TYPE_TV }, - [NM_ATT_POWER_THRESH] = { TLV_TYPE_FIXED, 3 }, - [NM_ATT_PROB_CAUSE] = { TLV_TYPE_FIXED, 3 }, - [NM_ATT_RACH_B_THRESH] = { TLV_TYPE_TV }, - [NM_ATT_LDAVG_SLOTS] = { TLV_TYPE_FIXED, 2 }, - [NM_ATT_RAD_SUBC] = { TLV_TYPE_TV }, - [NM_ATT_RF_MAXPOWR_R] = { TLV_TYPE_TV }, - [NM_ATT_SITE_INPUTS] = { TLV_TYPE_TL16V }, - [NM_ATT_SITE_OUTPUTS] = { TLV_TYPE_TL16V }, - [NM_ATT_SOURCE] = { TLV_TYPE_TL16V }, - [NM_ATT_SPEC_PROB] = { TLV_TYPE_TV }, - [NM_ATT_START_TIME] = { TLV_TYPE_FIXED, 2 }, - [NM_ATT_T200] = { TLV_TYPE_FIXED, 7 }, - [NM_ATT_TEI] = { TLV_TYPE_TV }, - [NM_ATT_TEST_DUR] = { TLV_TYPE_FIXED, 2 }, - [NM_ATT_TEST_NO] = { TLV_TYPE_TV }, - [NM_ATT_TEST_REPORT] = { TLV_TYPE_TL16V }, - [NM_ATT_VSWR_THRESH] = { TLV_TYPE_FIXED, 2 }, - [NM_ATT_WINDOW_SIZE] = { TLV_TYPE_TV }, - [NM_ATT_TSC] = { TLV_TYPE_TV }, - [NM_ATT_SW_CONFIG] = { TLV_TYPE_TL16V }, - [NM_ATT_SEVERITY] = { TLV_TYPE_TV }, - [NM_ATT_GET_ARI] = { TLV_TYPE_TL16V }, - [NM_ATT_HW_CONF_CHG] = { TLV_TYPE_TL16V }, - [NM_ATT_OUTST_ALARM] = { TLV_TYPE_TV }, - [NM_ATT_MEAS_RES] = { TLV_TYPE_TL16V }, - }, -}; - -static const enum abis_nm_chan_comb chcomb4pchan[] = { - [GSM_PCHAN_CCCH] = NM_CHANC_mainBCCH, - [GSM_PCHAN_CCCH_SDCCH4] = NM_CHANC_BCCHComb, - [GSM_PCHAN_TCH_F] = NM_CHANC_TCHFull, - [GSM_PCHAN_TCH_H] = NM_CHANC_TCHHalf, - [GSM_PCHAN_SDCCH8_SACCH8C] = NM_CHANC_SDCCH, - [GSM_PCHAN_PDCH] = NM_CHANC_IPAC_PDCH, - [GSM_PCHAN_TCH_F_PDCH] = NM_CHANC_IPAC_TCHFull_PDCH, - /* FIXME: bounds check */ -}; - -int abis_nm_chcomb4pchan(enum gsm_phys_chan_config pchan) -{ - if (pchan < ARRAY_SIZE(chcomb4pchan)) - return chcomb4pchan[pchan]; - - return -EINVAL; -} - -int abis_nm_tlv_parse(struct tlv_parsed *tp, struct gsm_bts *bts, const u_int8_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, reports, ARRAY_SIZE(reports)); -} - -#define MT_ACK(x) (x+1) -#define MT_NACK(x) (x+2) - -static void fill_om_hdr(struct abis_om_hdr *oh, u_int8_t len) -{ - oh->mdisc = ABIS_OM_MDISC_FOM; - oh->placement = ABIS_OM_PLACEMENT_ONLY; - oh->sequence = 0; - oh->length = len; -} - -static void fill_om_fom_hdr(struct abis_om_hdr *oh, u_int8_t len, - u_int8_t msg_type, u_int8_t obj_class, - u_int8_t bts_nr, u_int8_t trx_nr, u_int8_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; -} - -static struct msgb *nm_msgb_alloc(void) -{ - return msgb_alloc_headroom(OM_ALLOC_SIZE, OM_HEADROOM_SIZE, - "OML"); -} - -/* Send a OML NM Message from BSC to BTS */ -static int abis_nm_queue_msg(struct gsm_bts *bts, struct msgb *msg) -{ - msg->trx = bts->c0; - - /* 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, 0); - } 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); - -const struct value_string abis_nm_obj_class_names[] = { - { NM_OC_SITE_MANAGER, "SITE-MANAGER" }, - { NM_OC_BTS, "BTS" }, - { NM_OC_RADIO_CARRIER, "RADIO-CARRIER" }, - { NM_OC_BASEB_TRANSC, "BASEBAND-TRANSCEIVER" }, - { NM_OC_CHANNEL, "CHANNEL" }, - { NM_OC_BS11_ADJC, "ADJC" }, - { NM_OC_BS11_HANDOVER, "HANDOVER" }, - { NM_OC_BS11_PWR_CTRL, "POWER-CONTROL" }, - { NM_OC_BS11_BTSE, "BTSE" }, - { NM_OC_BS11_RACK, "RACK" }, - { NM_OC_BS11_TEST, "TEST" }, - { NM_OC_BS11_ENVABTSE, "ENVABTSE" }, - { NM_OC_BS11_BPORT, "BPORT" }, - { NM_OC_GPRS_NSE, "GPRS-NSE" }, - { NM_OC_GPRS_CELL, "GPRS-CELL" }, - { NM_OC_GPRS_NSVC, "GPRS-NSVC" }, - { NM_OC_BS11, "SIEMENSHW" }, - { 0, NULL } -}; - -static const char *obj_class_name(u_int8_t oc) -{ - return get_value_string(abis_nm_obj_class_names, oc); -} - -const char *nm_opstate_name(u_int8_t os) -{ - switch (os) { - case NM_OPSTATE_DISABLED: - return "Disabled"; - case NM_OPSTATE_ENABLED: - return "Enabled"; - case NM_OPSTATE_NULL: - return "NULL"; - default: - return "RFU"; - } -} - -/* Chapter 9.4.7 */ -static const struct value_string avail_names[] = { - { 0, "In test" }, - { 1, "Failed" }, - { 2, "Power off" }, - { 3, "Off line" }, - /* Not used */ - { 5, "Dependency" }, - { 6, "Degraded" }, - { 7, "Not installed" }, - { 0xff, "OK" }, - { 0, NULL } -}; - -const char *nm_avail_name(u_int8_t avail) -{ - return get_value_string(avail_names, avail); -} - -static struct value_string test_names[] = { - /* FIXME: standard test names */ - { NM_IPACC_TESTNO_CHAN_USAGE, "Channel Usage" }, - { NM_IPACC_TESTNO_BCCH_CHAN_USAGE, "BCCH Channel Usage" }, - { NM_IPACC_TESTNO_FREQ_SYNC, "Frequency Synchronization" }, - { NM_IPACC_TESTNO_BCCH_INFO, "BCCH Info" }, - { NM_IPACC_TESTNO_TX_BEACON, "Transmit Beacon" }, - { NM_IPACC_TESTNO_SYSINFO_MONITOR, "System Info Monitor" }, - { NM_IPACC_TESTNO_BCCCH_MONITOR, "BCCH Monitor" }, - { 0, NULL } -}; - -const struct value_string abis_nm_adm_state_names[] = { - { NM_STATE_LOCKED, "Locked" }, - { NM_STATE_UNLOCKED, "Unlocked" }, - { NM_STATE_SHUTDOWN, "Shutdown" }, - { NM_STATE_NULL, "NULL" }, - { 0, NULL } -}; - -const char *nm_adm_name(u_int8_t adm) -{ - return get_value_string(abis_nm_adm_state_names, adm); -} - -int nm_is_running(struct gsm_nm_state *s) { - return (s->operational == NM_OPSTATE_ENABLED) && ( - (s->availability == NM_AVSTATE_OK) || - (s->availability == 0xff) - ); -} - -static void debugp_foh(struct abis_om_fom_hdr *foh) -{ - DEBUGP(DNM, "OC=%s(%02x) INST=(%02x,%02x,%02x) ", - obj_class_name(foh->obj_class), foh->obj_class, - foh->obj_inst.bts_nr, foh->obj_inst.trx_nr, - foh->obj_inst.ts_nr); -} - -/* obtain the gsm_nm_state data structure for a given object instance */ -static struct gsm_nm_state * -objclass2nmstate(struct gsm_bts *bts, u_int8_t obj_class, - struct abis_om_obj_inst *obj_inst) -{ - struct gsm_bts_trx *trx; - struct gsm_nm_state *nm_state = NULL; - - switch (obj_class) { - case NM_OC_BTS: - nm_state = &bts->nm_state; - break; - case NM_OC_RADIO_CARRIER: - if (obj_inst->trx_nr >= bts->num_trx) { - DEBUGPC(DNM, "TRX %u does not exist ", obj_inst->trx_nr); - return NULL; - } - trx = gsm_bts_trx_num(bts, obj_inst->trx_nr); - nm_state = &trx->nm_state; - break; - case NM_OC_BASEB_TRANSC: - if (obj_inst->trx_nr >= bts->num_trx) { - DEBUGPC(DNM, "TRX %u does not exist ", obj_inst->trx_nr); - return NULL; - } - trx = gsm_bts_trx_num(bts, obj_inst->trx_nr); - nm_state = &trx->bb_transc.nm_state; - break; - case NM_OC_CHANNEL: - if (obj_inst->trx_nr >= bts->num_trx) { - DEBUGPC(DNM, "TRX %u does not exist ", obj_inst->trx_nr); - return NULL; - } - trx = gsm_bts_trx_num(bts, obj_inst->trx_nr); - if (obj_inst->ts_nr >= TRX_NR_TS) - return NULL; - nm_state = &trx->ts[obj_inst->ts_nr].nm_state; - break; - case NM_OC_SITE_MANAGER: - nm_state = &bts->site_mgr.nm_state; - break; - case NM_OC_BS11: - switch (obj_inst->bts_nr) { - case BS11_OBJ_CCLK: - nm_state = &bts->bs11.cclk.nm_state; - 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); - nm_state = &trx->bs11.bbsig.nm_state; - 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); - nm_state = &trx->bs11.pa.nm_state; - break; - default: - return NULL; - } - case NM_OC_BS11_RACK: - nm_state = &bts->bs11.rack.nm_state; - break; - case NM_OC_BS11_ENVABTSE: - if (obj_inst->trx_nr >= ARRAY_SIZE(bts->bs11.envabtse)) - return NULL; - nm_state = &bts->bs11.envabtse[obj_inst->trx_nr].nm_state; - break; - case NM_OC_GPRS_NSE: - nm_state = &bts->gprs.nse.nm_state; - break; - case NM_OC_GPRS_CELL: - nm_state = &bts->gprs.cell.nm_state; - break; - case NM_OC_GPRS_NSVC: - if (obj_inst->trx_nr >= ARRAY_SIZE(bts->gprs.nsvc)) - return NULL; - nm_state = &bts->gprs.nsvc[obj_inst->trx_nr].nm_state; - break; - } - return nm_state; -} - -/* obtain the in-memory data structure of a given object instance */ -static void * -objclass2obj(struct gsm_bts *bts, u_int8_t obj_class, - 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) { - DEBUGPC(DNM, "TRX %u does not exist ", obj_inst->trx_nr); - 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) { - DEBUGPC(DNM, "TRX %u does not exist ", obj_inst->trx_nr); - 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) { - DEBUGPC(DNM, "TRX %u does not exist ", obj_inst->trx_nr); - 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; -} - -/* 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, u_int8_t obj_class, - struct abis_om_obj_inst *obj_inst, u_int8_t adm_state) -{ - struct gsm_nm_state *nm_state, new_state; - struct nm_statechg_signal_data nsd; - - nsd.obj = objclass2obj(bts, obj_class, obj_inst); - if (!nsd.obj) - return -EINVAL; - nm_state = objclass2nmstate(bts, obj_class, obj_inst); - if (!nm_state) - return -1; - - new_state = *nm_state; - new_state.administrative = adm_state; - - nsd.obj_class = obj_class; - nsd.old_state = nm_state; - nsd.new_state = &new_state; - nsd.obj_inst = obj_inst; - dispatch_signal(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 gsm_bts *bts = mb->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 = 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 ", 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) ", 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 ", nm_adm_name(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 = 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; - dispatch_signal(SS_NM, S_NM_STATECHG_OPER, &nsd); - nm_state->operational = new_state.operational; - nm_state->availability = new_state.availability; - if (nm_state->administrative == 0) - nm_state->administrative = new_state.administrative; - } -#if 0 - if (op_state == 1) { - /* try to enable objects that are disabled */ - abis_nm_opstart(bts, foh->obj_class, - foh->obj_inst.bts_nr, - foh->obj_inst.trx_nr, - foh->obj_inst.ts_nr); - } -#endif - return 0; -} - -static int rx_fail_evt_rep(struct msgb *mb) -{ - struct abis_om_hdr *oh = msgb_l2(mb); - struct abis_om_fom_hdr *foh = msgb_l3(mb); - struct tlv_parsed tp; - const uint8_t *p_val; - char *p_text; - - DEBUGPC(DNM, "Failure Event Report "); - - abis_nm_tlv_parse(&tp, mb->trx->bts, foh->data, oh->length-sizeof(*foh)); - - if (TLVP_PRESENT(&tp, NM_ATT_EVENT_TYPE)) - DEBUGPC(DNM, "Type=%s ", event_type_name(*TLVP_VAL(&tp, NM_ATT_EVENT_TYPE))); - if (TLVP_PRESENT(&tp, NM_ATT_SEVERITY)) - DEBUGPC(DNM, "Severity=%s ", 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); - DEBUGPC(DNM, "Probable cause= %02X %02X %02X ", p_val[0], p_val[1], p_val[2]); - } - if (TLVP_PRESENT(&tp, NM_ATT_ADD_TEXT)) { - p_val = TLVP_VAL(&tp, NM_ATT_ADD_TEXT); - p_text = talloc_strndup(tall_bsc_ctx, (const char *) p_val, TLVP_LEN(&tp, NM_ATT_ADD_TEXT)); - if (p_text) { - DEBUGPC(DNM, "Additional Text=%s ", p_text); - talloc_free(p_text); - } - } - - DEBUGPC(DNM, "\n"); - - return 0; -} - -static int abis_nm_rcvmsg_report(struct msgb *mb) -{ - struct abis_om_fom_hdr *foh = msgb_l3(mb); - u_int8_t mt = foh->msg_type; - - debugp_foh(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"); - dispatch_signal(SS_NM, S_NM_SW_ACTIV_REP, mb); - break; - case NM_MT_FAILURE_EVENT_REP: - rx_fail_evt_rep(mb); - dispatch_signal(SS_NM, S_NM_FAIL_REP, mb); - break; - case NM_MT_TEST_REP: - DEBUGPC(DNM, "Test Report\n"); - dispatch_signal(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, u_int8_t obj_class, u_int8_t i0, u_int8_t i1, - u_int8_t i2, const u_int8_t *sw_desc, u_int8_t swdesc_len) -{ - struct abis_om_hdr *oh; - struct msgb *msg = nm_msgb_alloc(); - u_int8_t len = swdesc_len; - u_int8_t *trailer; - - oh = (struct abis_om_hdr *) msgb_put(msg, ABIS_OM_FOM_HDR_SIZE); - fill_om_fom_hdr(oh, len, NM_MT_ACTIVATE_SW, obj_class, i0, i1, i2); - - trailer = msgb_put(msg, swdesc_len); - memcpy(trailer, sw_desc, swdesc_len); - - return abis_nm_sendmsg(bts, msg); -} - -static int abis_nm_parse_sw_descr(const u_int8_t *sw_descr, int sw_descr_len) -{ - static const struct tlv_definition sw_descr_def = { - .def = { - [NM_ATT_FILE_ID] = { TLV_TYPE_TL16V, }, - [NM_ATT_FILE_VERSION] = { TLV_TYPE_TL16V, }, - }, - }; - - u_int8_t tag; - u_int16_t tag_len; - const u_int8_t *val; - int ofs = 0, len; - - /* Classic TLV parsing doesn't work well with SW_DESCR because of it's - * nested nature and the fact you have to assume it contains only two sub - * tags NM_ATT_FILE_VERSION & NM_ATT_FILE_ID to parse it */ - - if (sw_descr[0] != NM_ATT_SW_DESCR) { - DEBUGP(DNM, "SW_DESCR attribute identifier not found!\n"); - return -1; - } - ofs += 1; - - len = tlv_parse_one(&tag, &tag_len, &val, - &sw_descr_def, &sw_descr[ofs], sw_descr_len-ofs); - if (len < 0 || (tag != NM_ATT_FILE_ID)) { - DEBUGP(DNM, "FILE_ID attribute identifier not found!\n"); - return -2; - } - ofs += len; - - len = tlv_parse_one(&tag, &tag_len, &val, - &sw_descr_def, &sw_descr[ofs], sw_descr_len-ofs); - if (len < 0 || (tag != NM_ATT_FILE_VERSION)) { - DEBUGP(DNM, "FILE_VERSION attribute identifier not found!\n"); - return -3; - } - ofs += len; - - return ofs; -} - -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 tlv_parsed tp; - const u_int8_t *sw_config; - int ret, sw_config_len, sw_descr_len; - - debugp_foh(foh); - - DEBUGPC(DNM, "SW Activate Request: "); - - DEBUGP(DNM, "Software Activate Request, ACKing and Activating\n"); - - ret = abis_nm_sw_act_req_ack(mb->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)); - - abis_nm_tlv_parse(&tp, mb->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)) { - DEBUGP(DNM, "SW config not found! Can't continue.\n"); - return -EINVAL; - } else { - DEBUGP(DNM, "Found SW config: %s\n", hexdump(sw_config, sw_config_len)); - } - - /* Use the first SW_DESCR present in SW config */ - sw_descr_len = abis_nm_parse_sw_descr(sw_config, sw_config_len); - if (sw_descr_len < 0) - return -EINVAL; - - return ipacc_sw_activate(mb->trx->bts, foh->obj_class, - foh->obj_inst.bts_nr, - foh->obj_inst.trx_nr, - foh->obj_inst.ts_nr, - sw_config, sw_descr_len); -} - -/* Receive a CHANGE_ADM_STATE_ACK, parse the TLV and update local state */ -static int abis_nm_rx_chg_adm_state_ack(struct msgb *mb) -{ - struct abis_om_hdr *oh = msgb_l2(mb); - struct abis_om_fom_hdr *foh = msgb_l3(mb); - struct tlv_parsed tp; - u_int8_t adm_state; - - abis_nm_tlv_parse(&tp, mb->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(mb->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 tlv_parsed tp; - - DEBUGP(DNM, "LMT Event "); - abis_nm_tlv_parse(&tp, mb->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) { - u_int8_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) { - u_int8_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; -} - -static 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, 0); - - 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); - u_int8_t mt = foh->msg_type; - int ret = 0; - - /* check for unsolicited message */ - if (is_report(mt)) - return abis_nm_rcvmsg_report(mb); - - if (is_in_arr(mt, sw_load_msgs, ARRAY_SIZE(sw_load_msgs))) - return abis_nm_rcvmsg_sw(mb); - - if (is_in_arr(mt, nacks, ARRAY_SIZE(nacks))) { - struct nm_nack_signal_data nack_data; - struct tlv_parsed tp; - - debugp_foh(foh); - - DEBUGPC(DNM, "%s NACK ", get_value_string(nack_names, mt)); - - abis_nm_tlv_parse(&tp, mb->trx->bts, foh->data, oh->length-sizeof(*foh)); - if (TLVP_PRESENT(&tp, NM_ATT_NACK_CAUSES)) - DEBUGPC(DNM, "CAUSE=%s\n", - nack_cause_name(*TLVP_VAL(&tp, NM_ATT_NACK_CAUSES))); - else - DEBUGPC(DNM, "\n"); - - nack_data.msg = mb; - nack_data.mt = mt; - dispatch_signal(SS_NM, S_NM_NACK, &nack_data); - abis_nm_queue_send_next(mb->trx->bts); - return 0; - } -#if 0 - /* check if last message is to be acked */ - if (is_ack_nack(nmh->last_msgtype)) { - if (mt == MT_ACK(nmh->last_msgtype)) { - DEBUGP(DNM, "received ACK (0x%x)\n", foh->msg_type); - /* we got our ACK, continue sending the next msg */ - } else if (mt == MT_NACK(nmh->last_msgtype)) { - /* we got a NACK, signal this to the caller */ - DEBUGP(DNM, "received NACK (0x%x)\n", foh->msg_type); - /* FIXME: somehow signal this to the caller */ - } else { - /* really strange things happen */ - return -EINVAL; - } - } -#endif - - switch (mt) { - case NM_MT_CHG_ADM_STATE_ACK: - ret = abis_nm_rx_chg_adm_state_ack(mb); - break; - case NM_MT_SW_ACT_REQ: - ret = abis_nm_rx_sw_act_req(mb); - break; - case NM_MT_BS11_LMT_SESSION: - ret = abis_nm_rx_lmt_event(mb); - break; - case NM_MT_CONN_MDROP_LINK_ACK: - DEBUGP(DNM, "CONN MDROP LINK ACK\n"); - break; - case NM_MT_IPACC_RESTART_ACK: - dispatch_signal(SS_NM, S_NM_IPACC_RESTART_ACK, NULL); - break; - case NM_MT_IPACC_RESTART_NACK: - dispatch_signal(SS_NM, S_NM_IPACC_RESTART_NACK, NULL); - break; - } - - abis_nm_queue_send_next(mb->trx->bts); - return ret; -} - -static int abis_nm_rx_ipacc(struct msgb *mb); - -static int abis_nm_rcvmsg_manuf(struct msgb *mb) -{ - int rc; - int bts_type = mb->trx->bts->type; - - switch (bts_type) { - case GSM_BTS_TYPE_NANOBTS: - rc = abis_nm_rx_ipacc(mb); - abis_nm_queue_send_next(mb->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) - return -EINVAL; - } - if (oh->sequence != 0) { - LOGP(DNM, LOGL_ERROR, "ABIS OML sequence 0x%x != 0x00\n", - oh->sequence); - return -EINVAL; - } -#if 0 - unsigned int l2_len = msg->tail - (u_int8_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); - return -EINVAL; - } - - 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 */ - u_int8_t obj_class; - u_int8_t obj_instance[3]; - - u_int8_t file_id[255]; - u_int8_t file_id_len; - - u_int8_t file_version[255]; - u_int8_t file_version_len; - - u_int8_t window_size; - u_int8_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(); - u_int8_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); - if (!fgets(next_seg_buf, sizeof(next_seg_buf)-2, stream)) { - fseek(stream, pos, SEEK_SET); - return 1; - } - - fseek(stream, pos, SEEK_SET); - return 0; -} - -/* 6.2.2 / 8.3.2 Load Data Segment */ -static int sw_load_segment(struct abis_nm_sw *sw) -{ - struct abis_om_hdr *oh; - struct msgb *msg = nm_msgb_alloc(); - char seg_buf[256]; - char *line_buf = seg_buf+2; - unsigned char *tlv; - u_int8_t 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, (u_int8_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: { - 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 u_int8_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(); - u_int8_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(); - u_int8_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); - 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(mb->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(mb->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(mb->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(mb->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(mb->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(mb->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(mb->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, - u_int8_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, u_int8_t bts_port, - u_int8_t ts_nr, u_int8_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, u_int8_t trx_nr, - u_int8_t e1_port, u_int8_t e1_timeslot, u_int8_t e1_subslot, - u_int8_t tei) -{ - struct abis_om_hdr *oh; - struct abis_nm_channel *ch; - u_int8_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, - u_int8_t e1_port, u_int8_t e1_timeslot, u_int8_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, - u_int8_t e1_port, u_int8_t e1_timeslot, - u_int8_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, - u_int8_t subchan) -{ -} -#endif - -/* Chapter 8.6.1 */ -int abis_nm_set_bts_attr(struct gsm_bts *bts, u_int8_t *attr, int attr_len) -{ - struct abis_om_hdr *oh; - struct msgb *msg = nm_msgb_alloc(); - u_int8_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, u_int8_t *attr, int attr_len) -{ - struct abis_om_hdr *oh; - struct msgb *msg = nm_msgb_alloc(); - u_int8_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); -} - -static int verify_chan_comb(struct gsm_bts_trx_ts *ts, u_int8_t chan_comb) -{ - int i; - - /* As it turns out, the BS-11 has some very peculiar restrictions - * on the channel combinations it allows */ - switch (ts->trx->bts->type) { - case GSM_BTS_TYPE_BS11: - switch (chan_comb) { - case NM_CHANC_TCHHalf: - case NM_CHANC_TCHHalf2: - /* not supported */ - 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) - return -EINVAL; - } - /* not allowed for TS0 of BCCH-TRX */ - if (ts->trx == ts->trx->bts->c0 && - ts->nr == 0) - 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)) - 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) - return -EINVAL; - break; - case NM_CHANC_BCCH: - /* allowed only for TS 2/4/6 of C0 */ - if (ts->trx != ts->trx->bts->c0) - return -EINVAL; - if (ts->nr != 2 && ts->nr != 4 && - ts->nr != 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: - return -EINVAL; - } - } else { - switch (chan_comb) { - case NM_CHANC_TCHFull: - case NM_CHANC_TCHHalf: - case NM_CHANC_IPAC_TCHFull_TCHHalf: - return 0; - default: - 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; - return -EINVAL; - case NM_CHANC_SDCCH: - case NM_CHANC_TCHFull: - case NM_CHANC_TCHHalf: - case NM_CHANC_IPAC_TCHFull_TCHHalf: - case NM_CHANC_IPAC_TCHFull_PDCH: - return 0; - } - } 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: - return -EINVAL; - } - } - break; - case 2: - case 3: - case 4: - case 5: - case 6: - case 7: - switch (chan_comb) { - case NM_CHANC_TCHFull: - case NM_CHANC_TCHHalf: - case NM_CHANC_IPAC_TCHFull_TCHHalf: - return 0; - case NM_CHANC_IPAC_PDCH: - case NM_CHANC_IPAC_TCHFull_PDCH: - if (ts->trx->nr == 0) - return 0; - else - return -EINVAL; - } - break; - } - return -EINVAL; - default: - /* unknown BTS type */ - return 0; - } - return 0; -} - -/* Chapter 8.6.3 */ -int abis_nm_set_channel_attr(struct gsm_bts_trx_ts *ts, u_int8_t chan_comb) -{ - struct gsm_bts *bts = ts->trx->bts; - struct abis_om_hdr *oh; - u_int8_t zero = 0x00; - struct msgb *msg = nm_msgb_alloc(); - u_int8_t len = 2 + 2; - - 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) < 0) { - msgb_free(msg); - DEBUGP(DNM, "Invalid Channel Combination!!!\n"); - 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, bts->tsc); /* 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, u_int8_t obj_class, u_int8_t i1, - u_int8_t i2, u_int8_t i3, int nack, u_int8_t *attr, int att_len) -{ - struct abis_om_hdr *oh; - struct msgb *msg = nm_msgb_alloc(); - u_int8_t msgtype = NM_MT_SW_ACT_REQ_ACK; - u_int8_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) { - u_int8_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, u_int8_t *rawmsg) -{ - struct msgb *msg = nm_msgb_alloc(); - struct abis_om_hdr *oh; - u_int8_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, u_int8_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, u_int8_t obj_class, u_int8_t i0, u_int8_t i1, u_int8_t i2) -{ - 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_OPSTART, obj_class, i0, i1, i2); - - debugp_foh((struct abis_om_fom_hdr *) oh->data); - 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, u_int8_t obj_class, u_int8_t i0, - u_int8_t i1, u_int8_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, u_int8_t e1_port0, u_int8_t ts0, - u_int8_t e1_port1, u_int8_t ts1) -{ - struct abis_om_hdr *oh; - struct msgb *msg = nm_msgb_alloc(); - u_int8_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, u_int8_t obj_class, - u_int8_t bts_nr, u_int8_t trx_nr, u_int8_t ts_nr, - u_int8_t test_nr, u_int8_t auton_report, struct msgb *msg) -{ - struct abis_om_hdr *oh; - - DEBUGP(DNM, "PEFORM TEST %s\n", get_value_string(test_names, 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 { - u_int16_t year; - u_int8_t month; - u_int8_t day; - u_int8_t hour; - u_int8_t min; - u_int8_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, u_int8_t idx, - u_int8_t attr_len, const u_int8_t *attr) -{ - struct abis_om_hdr *oh; - struct msgb *msg = nm_msgb_alloc(); - u_int8_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, u_int8_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, u_int8_t idx) -{ - struct abis_om_hdr *oh; - struct msgb *msg = nm_msgb_alloc(); - u_int8_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, u_int8_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, u_int8_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 u_int8_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, u_int8_t e1_port, - u_int8_t e1_timeslot, u_int8_t e1_subslot, - u_int8_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, u_int8_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(); - u_int8_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(); - u_int8_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(); - u_int8_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 u_int8_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, u_int8_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) { - u_int8_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), (u_int8_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), (u_int8_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 u_int8_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; - u_int8_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; - u_int8_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]; - u_int8_t win_size; - int forced; - struct llist_head file_list; - gsm_cbfn *user_cb; /* specified by the user */ -}; -static struct abis_nm_bs11_sw _g_bs11_sw, *g_bs11_sw = &_g_bs11_sw; - -struct file_list_entry { - struct llist_head list; - char fname[PATH_MAX]; -}; - -struct file_list_entry *fl_dequeue(struct llist_head *queue) -{ - struct llist_head *lh; - - if (llist_empty(queue)) - return NULL; - - lh = queue->next; - llist_del(lh); - - return llist_entry(lh, struct file_list_entry, list); -} - -static int bs11_read_swl_file(struct abis_nm_bs11_sw *bs11_sw) -{ - char linebuf[255]; - struct llist_head *lh, *lh2; - FILE *swl; - int rc = 0; - - swl = fopen(bs11_sw->swl_fname, "r"); - if (!swl) - return -ENODEV; - - /* zero the stale file list, if any */ - llist_for_each_safe(lh, lh2, &bs11_sw->file_list) { - llist_del(lh); - talloc_free(lh); - } - - while (fgets(linebuf, sizeof(linebuf), swl)) { - char file_id[12+1]; - char file_version[80+1]; - struct file_list_entry *fle; - static char dir[PATH_MAX]; - - if (strlen(linebuf) < 4) - continue; - - rc = sscanf(linebuf+4, "%12s:%80s\r\n", file_id, file_version); - if (rc < 0) { - perror("ERR parsing SWL file"); - rc = -EINVAL; - goto out; - } - if (rc < 2) - continue; - - fle = talloc_zero(tall_fle_ctx, struct file_list_entry); - if (!fle) { - rc = -ENOMEM; - goto out; - } - - /* construct new filename */ - strncpy(dir, bs11_sw->swl_fname, sizeof(dir)); - strncat(fle->fname, dirname(dir), sizeof(fle->fname) - 1); - strcat(fle->fname, "/"); - strncat(fle->fname, file_id, sizeof(fle->fname) - 1 -strlen(fle->fname)); - - llist_add_tail(&fle->list, &bs11_sw->file_list); - } - -out: - fclose(swl); - return rc; -} - -/* bs11 swload specific callback, passed to abis_nm core swload */ -static int bs11_swload_cbfn(unsigned int hook, unsigned int event, - struct msgb *msg, void *data, void *param) -{ - struct abis_nm_bs11_sw *bs11_sw = data; - struct file_list_entry *fle; - int rc = 0; - - switch (event) { - case NM_MT_LOAD_END_ACK: - fle = fl_dequeue(&bs11_sw->file_list); - if (fle) { - /* start download the next file of our file list */ - rc = abis_nm_software_load(bs11_sw->bts, 0xff, fle->fname, - bs11_sw->win_size, - bs11_sw->forced, - &bs11_swload_cbfn, bs11_sw); - talloc_free(fle); - } else { - /* activate the SWL */ - rc = abis_nm_software_activate(bs11_sw->bts, - bs11_sw->swl_fname, - bs11_swload_cbfn, - bs11_sw); - } - break; - case NM_MT_LOAD_SEG_ACK: - case NM_MT_LOAD_END_NACK: - case NM_MT_LOAD_INIT_ACK: - case NM_MT_LOAD_INIT_NACK: - case NM_MT_ACTIVATE_SW_NACK: - case NM_MT_ACTIVATE_SW_ACK: - default: - /* fallthrough to the user callback */ - if (bs11_sw->user_cb) - rc = bs11_sw->user_cb(hook, event, msg, NULL, NULL); - break; - } - - return rc; -} - -/* Siemens provides a SWL file that is a mere listing of all the other - * files that are part of a software release. We need to upload first - * the list file, and then each file that is listed in the list file */ -int abis_nm_bs11_load_swl(struct gsm_bts *bts, const char *fname, - u_int8_t win_size, int forced, gsm_cbfn *cbfn) -{ - struct abis_nm_bs11_sw *bs11_sw = g_bs11_sw; - struct file_list_entry *fle; - int rc = 0; - - INIT_LLIST_HEAD(&bs11_sw->file_list); - bs11_sw->bts = bts; - bs11_sw->win_size = win_size; - bs11_sw->user_cb = cbfn; - bs11_sw->forced = forced; - - strncpy(bs11_sw->swl_fname, fname, sizeof(bs11_sw->swl_fname)); - rc = bs11_read_swl_file(bs11_sw); - if (rc < 0) - return rc; - - /* dequeue next item in file list */ - fle = fl_dequeue(&bs11_sw->file_list); - if (!fle) - return -EINVAL; - - /* start download the next file of our file list */ - rc = abis_nm_software_load(bts, 0xff, fle->fname, win_size, forced, - bs11_swload_cbfn, bs11_sw); - talloc_free(fle); - return rc; -} - -#if 0 -static u_int8_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 u_int8_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 u_int8_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), (u_int8_t *) &aet); - - return abis_nm_sendmsg(bts, msg); -} - -int abis_nm_bs11_get_bport_line_cfg(struct gsm_bts *bts, u_int8_t bport) -{ - struct abis_om_hdr *oh; - struct msgb *msg = nm_msgb_alloc(); - u_int8_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, u_int8_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; - u_int8_t idstrlen = oh->data[0]; - struct tlv_parsed tp; - struct ipacc_ack_signal_data signal; - - 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, msg->trx->bts, foh->data, oh->length-sizeof(*foh)); - - debugp_foh(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(*((u_int16_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)) - DEBUGPC(DNM, " CAUSE=%s\n", - nack_cause_name(*TLVP_VAL(&tp, NM_ATT_NACK_CAUSES))); - else - DEBUGPC(DNM, "\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", - 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", - 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", - 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(msg->trx->bts, foh->obj_inst.trx_nr); - signal.msg_type = foh->msg_type; - dispatch_signal(SS_NM, S_NM_IPACC_NACK, &signal); - break; - case NM_MT_IPACC_SET_NVATTR_ACK: - signal.trx = gsm_bts_trx_by_nr(msg->trx->bts, foh->obj_inst.trx_nr); - signal.msg_type = foh->msg_type; - dispatch_signal(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, u_int8_t msg_type, - u_int8_t obj_class, u_int8_t bts_nr, - u_int8_t trx_nr, u_int8_t ts_nr, - u_int8_t *attr, int attr_len) -{ - struct msgb *msg = nm_msgb_alloc(); - struct abis_om_hdr *oh; - struct abis_om_fom_hdr *foh; - u_int8_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, u_int8_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, - u_int32_t ip, u_int16_t port, u_int8_t stream) -{ - struct in_addr ia; - u_int8_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; - *(u_int32_t *)(attr+6) = ia.s_addr; - - /* if ip == 0, we use the default IP */ - if (ip == 0) - attr_len -= 5; - - DEBUGP(DNM, "ip.access RSL CONNECT IP=%s PORT=%u STREAM=0x%02x\n", - inet_ntoa(ia), port, stream); - - return abis_nm_ipaccess_msg(trx->bts, NM_MT_IPACC_RSL_CONNECT, - NM_OC_BASEB_TRANSC, trx->bts->bts_nr, - trx->nr, 0xff, attr, attr_len); -} - -/* restart / reboot an ip.access nanoBTS */ -int abis_nm_ipaccess_restart(struct gsm_bts_trx *trx) -{ - struct abis_om_hdr *oh; - struct msgb *msg = nm_msgb_alloc(); - - oh = (struct abis_om_hdr *) msgb_put(msg, ABIS_OM_FOM_HDR_SIZE); - fill_om_fom_hdr(oh, 0, NM_MT_IPACC_RESTART, NM_OC_BASEB_TRANSC, - trx->bts->nr, trx->nr, 0xff); - - return abis_nm_sendmsg(trx->bts, msg); -} - -int abis_nm_ipaccess_set_attr(struct gsm_bts *bts, u_int8_t obj_class, - u_int8_t bts_nr, u_int8_t trx_nr, u_int8_t ts_nr, - u_int8_t *attr, u_int8_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(u_int8_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); - *((u_int16_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->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(u_int8_t res) -{ - return get_value_string(ipacc_testres_names, res); -} - -void ipac_parse_cgi(struct cell_global_id *cid, const u_int8_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(*((u_int16_t *)&buf[3])); - cid->ci = ntohs(*((u_int16_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, u_int8_t *buf) -{ - u_int8_t *cur = buf; - u_int16_t len; - - memset(binf, 0, sizeof(*binf)); - - if (cur[0] != NM_IPAC_EIE_BCCH_INFO) - return -EINVAL; - cur++; - - len = ntohs(*(u_int16_t *)cur); - cur += 2; - - binf->info_type = ntohs(*(u_int16_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(*(u_int16_t *)cur); - cur += 2; - - if (binf->info_type & IPAC_BINF_FRAME_OFFSET) - binf->frame_offset = ntohs(*(u_int16_t *)cur); - cur += 2; - - if (binf->info_type & IPAC_BINF_FRAME_NR_OFFSET) - binf->frame_nr_offset = ntohl(*(u_int32_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/bsc/abis_nm_ipaccess.c b/openbsc/src/bsc/abis_nm_ipaccess.c deleted file mode 100644 index 754efe28d..000000000 --- a/openbsc/src/bsc/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 - * - * All Rights Reserved - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation; either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - * - */ - -/* A list of all the 'embedded' attributes of ip.access */ -enum ipa_embedded_att { - IPA_ATT_ARFCN_WHITELIST = 0x01, - IPA_ATT_ARFCN_BLACKLIST = 0x02, - IPA_ATT_FREQ_ERR_LIST = 0x03, - IPA_ATT_CHAN_USAGE_LIST = 0x04, - IPA_ATT_BCCH_INF_TYPE = 0x05, - IPA_ATT_BCCH_INF = 0x06, - IPA_ATT_CONFIG = 0x07, - IPA_ATT_RESULT_DETAILS = 0x08, - IPA_ATT_RXLEV_THRESH = 0x09, - IPA_ATT_FREQ_SYNC_OPT = 0x0a, - IPA_ATT_MAC_ADDR = 0x0b, - IPA_ATT_HW_SW_COMPAT_NR = 0x0c, - IPA_ATT_MANUF_SER_NR = 0x0d, - IPA_ATT_OEM_ID = 0x0e, - IPA_ATT_DATETIME_MANUF = 0x0f, - IPA_ATT_DATETIME_CALIB = 0x10, - IPA_ATT_BEACON_INF = 0x11, - IPA_ATT_FREQ_ERR = 0x12, - IPA_ATT_SNMP_COMM_STRING = 0x13, - IPA_ATT_SNMP_TRAP_ADDR = 0x14, - IPA_ATT_SNMP_TRAP_PORT = 0x15, - IPA_ATT_SNMP_MAN_ADDR = 0x16, - IPA_ATT_SNMP_SYS_CONTACT = 0x17, - IPA_ATT_FACTORY_ID = 0x18, - IPA_ATT_FACTORY_SERIAL = 0x19, - IPA_ATT_LOGGED_EVT_IND = 0x1a, - IPA_ATT_LOCAL_ADD_TEXT = 0x1b, - IPA_ATT_FREQ_BANDS = 0x1c, - IPA_ATT_MAX_TA = 0x1d, - IPA_ATT_CIPH_ALG = 0x1e, - IPA_ATT_CHAN_TYPES = 0x1f, - IPA_ATT_CHAN_MODES = 0x20, - IPA_ATT_GPRS_CODING_SCHEMES = 0x21, - IPA_ATT_RTP_FEATURES = 0x22, - IPA_ATT_RSL_FEATURES = 0x23, - IPA_ATT_BTS_HW_CLASS = 0x24, - IPA_ATT_BTS_ID = 0x25, - IPA_ATT_BCAST_L2_MSG = 0x26, -}; - -/* append an ip.access channel list to the given msgb */ -static int ipa_chan_list_append(struct msgb *msg, u_int8_t ie, - u_int16_t *arfcns, int arfcn_count) -{ - int i; - u_int8_t *u8; - u_int16_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/bsc/abis_nm_vty.c b/openbsc/src/bsc/abis_nm_vty.c deleted file mode 100644 index 996a85749..000000000 --- a/openbsc/src/bsc/abis_nm_vty.c +++ /dev/null @@ -1,197 +0,0 @@ -/* VTY interface for A-bis OML (Netowrk Management) */ - -/* (C) 2009-2010 by Harald Welte - * - * All Rights Reserved - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation; either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - * - */ - -#include -#include -#include -#include - -#include - -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include - -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 "FIXME" - -DEFUN(oml_class_inst, oml_class_inst_cmd, - "bts <0-255> oml class " NM_OBJCLASS_VTY - " instance <0-255> <0-255> <0-255>", - "BTS related commands\n" "BTS Number\n" - "Manipulate the OML managed objects\n" - "Object Class\n" NM_OBJCLASS_VTY_HELP - "Object Instance\n" "BTS Number\n" "TRX Number\n" "TS Number\n") -{ - struct gsm_bts *bts; - struct oml_node_state *oms; - int bts_nr = atoi(argv[0]); - - bts = gsm_bts_num(bsc_gsmnet, bts_nr); - if (!bts) { - vty_out(vty, "%% No such BTS (%d)%s", bts_nr, VTY_NEWLINE); - return CMD_WARNING; - } - - oms = talloc_zero(tall_bsc_ctx, struct oml_node_state); - if (!oms) - return CMD_WARNING; - - oms->bts = bts; - oms->obj_class = get_string_value(abis_nm_obj_class_names, argv[1]); - oms->obj_inst[0] = atoi(argv[2]); - oms->obj_inst[1] = atoi(argv[3]); - oms->obj_inst[2] = atoi(argv[4]); - - vty->index = oms; - vty->node = OML_NODE; - - return CMD_SUCCESS; - -} - -DEFUN(oml_classnum_inst, oml_classnum_inst_cmd, - "bts <0-255> oml class <0-255> instance <0-255> <0-255> <0-255>", - "BTS related commands\n" "BTS Number\n" - "Manipulate the OML managed objects\n" - "Object Class\n" "Object Class\n" - "Object Instance\n" "BTS Number\n" "TRX Number\n" "TS Number\n") -{ - struct gsm_bts *bts; - struct oml_node_state *oms; - int bts_nr = atoi(argv[0]); - - bts = gsm_bts_num(bsc_gsmnet, bts_nr); - if (!bts) { - vty_out(vty, "%% No such BTS (%d)%s", bts_nr, VTY_NEWLINE); - return CMD_WARNING; - } - - oms = talloc_zero(tall_bsc_ctx, struct oml_node_state); - if (!oms) - return CMD_WARNING; - - oms->bts = bts; - oms->obj_class = atoi(argv[1]); - oms->obj_inst[0] = atoi(argv[2]); - oms->obj_inst[1] = atoi(argv[3]); - oms->obj_inst[2] = atoi(argv[4]); - - vty->index = oms; - vty->node = OML_NODE; - - return CMD_SUCCESS; -} - -DEFUN(oml_attrib_get, oml_attrib_get_cmd, - "attribute get <0-255>", - "OML Attribute Actions\n" "Get a single OML Attribute\n" - "OML Attribute Number\n") -{ - struct oml_node_state *oms = vty->index; - - /* FIXME */ - return CMD_SUCCESS; -} - -DEFUN(oml_attrib_set, oml_attrib_set_cmd, - "attribute set <0-255> .HEX", - "OML Attribute Actions\n" "Set a single OML Attribute\n" - "OML Attribute Number\n") -{ - struct oml_node_state *oms = vty->index; - - /* FIXME */ - 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); - - install_default(OML_NODE); - install_element(OML_NODE, &ournode_exit_cmd); - install_element(OML_NODE, &oml_attrib_get_cmd); - install_element(OML_NODE, &oml_attrib_set_cmd); - install_element(OML_NODE, &oml_chg_adm_state_cmd); - install_element(OML_NODE, &oml_opstart_cmd); - - return 0; -} diff --git a/openbsc/src/bsc/abis_om2000.c b/openbsc/src/bsc/abis_om2000.c deleted file mode 100644 index e7a5f8386..000000000 --- a/openbsc/src/bsc/abis_om2000.c +++ /dev/null @@ -1,878 +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 by Harald Welte - * - * All Rights Reserved - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation; either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - * - */ - - -#include -#include -#include -#include -#include -#include - -#include - -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include - -#define OM_ALLOC_SIZE 1024 -#define OM_HEADROOM_SIZE 128 - -/* use following functions from abis_nm.c: - * om2k_msgb_alloc() - * abis_om2k_sendmsg() - */ - -struct abis_om2k_hdr { - struct abis_om_hdr om; - uint16_t msg_type; - struct abis_om2k_mo mo; - uint8_t data[0]; -} __attribute__ ((packed)); - -enum abis_om2k_msgtype { - OM2K_MSGT_ABORT_SP_CMD = 0x0000, - OM2K_MSGT_ABORT_SP_COMPL = 0x0002, - OM2K_MSGT_ALARM_REP_ACK = 0x0004, - OM2K_MSGT_ALARM_REP_NACK = 0x0005, - OM2K_MSGT_ALARM_REP = 0x0006, - OM2K_MSGT_ALARM_STATUS_REQ = 0x0008, - OM2K_MSGT_ALARM_STATUS_REQ_ACK = 0x000a, - OM2K_MSGT_ALARM_STATUS_REQ_REJ = 0x000b, - OM2K_MSGT_ALARM_STATUS_RES_ACK = 0x000c, - OM2K_MSGT_ALARM_STATUS_RES_NACK = 0x000d, - OM2K_MSGT_ALARM_STATUS_RES = 0x000e, - OM2K_MSGT_CAL_TIME_RESP = 0x0010, - OM2K_MSGT_CAL_TIME_REJ = 0x0011, - OM2K_MSGT_CAL_TIME_REQ = 0x0012, - - OM2K_MSGT_CON_CONF_REQ = 0x0014, - OM2K_MSGT_CON_CONF_REQ_ACK = 0x0016, - OM2K_MSGT_CON_CONF_REQ_REJ = 0x0017, - OM2K_MSGT_CON_CONF_RES_ACK = 0x0018, - OM2K_MSGT_CON_CONF_RES_NACK = 0x0019, - OM2K_MSGT_CON_CONF_RES = 0x001a, - - OM2K_MSGT_CONNECT_CMD = 0x001c, - OM2K_MSGT_CONNECT_COMPL = 0x001e, - OM2K_MSGT_CONNECT_REJ = 0x001f, - - OM2K_MSGT_DISABLE_REQ = 0x0028, - OM2K_MSGT_DISABLE_REQ_ACK = 0x002a, - OM2K_MSGT_DISABLE_REQ_REJ = 0x002b, - OM2K_MSGT_DISABLE_RES_ACK = 0x002c, - OM2K_MSGT_DISABLE_RES_NACK = 0x002d, - OM2K_MSGT_DISABLE_RES = 0x002e, - OM2K_MSGT_DISCONNECT_CMD = 0x0030, - OM2K_MSGT_DISCONNECT_COMPL = 0x0032, - OM2K_MSGT_DISCONNECT_REJ = 0x0033, - OM2K_MSGT_ENABLE_REQ = 0x0034, - OM2K_MSGT_ENABLE_REQ_ACK = 0x0036, - OM2K_MSGT_ENABLE_REQ_REJ = 0x0037, - OM2K_MSGT_ENABLE_RES_ACK = 0x0038, - OM2K_MSGT_ENABLE_RES_NACK = 0x0039, - OM2K_MSGT_ENABLE_RES = 0x003a, - - OM2K_MSGT_FAULT_REP_ACK = 0x0040, - OM2K_MSGT_FAULT_REP_NACK = 0x0041, - OM2K_MSGT_FAULT_REP = 0x0042, - - OM2K_MSGT_IS_CONF_REQ = 0x0060, - OM2K_MSGT_IS_CONF_REQ_ACK = 0x0062, - OM2K_MSGT_IS_CONF_REQ_REJ = 0x0063, - OM2K_MSGT_IS_CONF_RES_ACK = 0x0064, - OM2K_MSGT_IS_CONF_RES_NACK = 0x0065, - OM2K_MSGT_IS_CONF_RES = 0x0066, - - OM2K_MSGT_OP_INFO = 0x0074, - OM2K_MSGT_OP_INFO_ACK = 0x0076, - OM2K_MSGT_OP_INFO_REJ = 0x0077, - OM2K_MSGT_RESET_CMD = 0x0078, - OM2K_MSGT_RESET_COMPL = 0x007a, - OM2K_MSGT_RESET_REJ = 0x007b, - - OM2K_MSGT_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_NEGOT_REQ_ACK = 0x0104, - OM2K_MSGT_NEGOT_REQ_NACK = 0x0105, - OM2K_MSGT_NEGOT_REQ = 0x0106, -}; - -enum abis_om2k_dei { - OM2K_DEI_CAL_TIME = 0x0d, - OM2K_DEI_CON_CONN_LIST = 0x10, - OM2K_DEI_END_LIST_NR = 0x13, - OM2K_DEI_IS_CONN_LIST = 0x27, - OM2K_DEI_LIST_NR = 0x28, - OM2K_DEI_OP_INFO = 0x2e, - OM2K_DEI_NEGOT_REC1 = 0x90, - OM2K_DEI_NEGOT_REC2 = 0x91, -}; - -enum abis_om2k_mo_cls { - OM2K_MO_CLS_TRXC = 0x01, - OM2K_MO_CLS_TS = 0x03, - OM2K_MO_CLS_TF = 0x04, - OM2K_MO_CLS_IS = 0x05, - OM2K_MO_CLS_CON = 0x06, - OM2K_MO_CLS_DP = 0x07, - OM2K_MO_CLS_CF = 0x0a, - OM2K_MO_CLS_TX = 0x0b, - OM2K_MO_CLS_RX = 0x0c, -}; - -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 Rejecte" }, - { 0x0028, "Disable Request" }, - { 0x002a, "Disable Request Accept" }, - { 0x002b, "Disable Request Reject" }, - { 0x002c, "Disable Result ACK" }, - { 0x002d, "Disable Result NACK" }, - { 0x002e, "Disable Result" }, - { 0x0030, "Disconnect Command" }, - { 0x0032, "Disconnect Complete" }, - { 0x0033, "Disconnect Reject" }, - { 0x0034, "Enable Request" }, - { 0x0036, "Enable Request Accept" }, - { 0x0037, "Enable Request Reject" }, - { 0x0038, "Enable Result ACK" }, - { 0x0039, "Enable Result NACK" }, - { 0x003a, "Enable Result" }, - { 0x003c, "Escape Downlink Normal" }, - { 0x003d, "Escape Downlink NACK" }, - { 0x003e, "Escape Uplink Normal" }, - { 0x003f, "Escape Uplink NACK" }, - { 0x0040, "Fault Report ACK" }, - { 0x0041, "Fault Report NACK" }, - { 0x0042, "Fault Report" }, - { 0x0044, "File Package End Command" }, - { 0x0046, "File Package End Result" }, - { 0x0047, "File Package End Reject" }, - { 0x0048, "File Relation Request" }, - { 0x004a, "File Relation Response" }, - { 0x004b, "File Relation Request Reject" }, - { 0x004c, "File Segment Transfer" }, - { 0x004e, "File Segment Transfer Complete" }, - { 0x004f, "File Segment Transfer Reject" }, - { 0x0050, "HW Information Request" }, - { 0x0052, "HW Information Request Accept" }, - { 0x0053, "HW Information Request Reject" }, - { 0x0054, "HW Information Result ACK" }, - { 0x0055, "HW Information Result NACK" }, - { 0x0056, "HW Information Result" }, - { 0x0060, "IS Configuration Request" }, - { 0x0062, "IS Configuration Request Accept" }, - { 0x0063, "IS Configuration Request Reject" }, - { 0x0064, "IS Configuration Result ACK" }, - { 0x0065, "IS Configuration Result NACK" }, - { 0x0066, "IS Configuration Result" }, - { 0x0068, "Load Data End" }, - { 0x006a, "Load Data End Result" }, - { 0x006b, "Load Data End Reject" }, - { 0x006c, "Load Data Init" }, - { 0x006e, "Load Data Init Accept" }, - { 0x006f, "Load Data Init Reject" }, - { 0x0070, "Loop Control Command" }, - { 0x0072, "Loop Control Complete" }, - { 0x0073, "Loop Control Reject" }, - { 0x0074, "Operational Information" }, - { 0x0076, "Operational Information Accept" }, - { 0x0077, "Operational Information Reject" }, - { 0x0078, "Reset Command" }, - { 0x007a, "Reset Complete" }, - { 0x007b, "Reset Reject" }, - { 0x007c, "RX Configuration Request" }, - { 0x007e, "RX Configuration Request Accept" }, - { 0x007f, "RX Configuration Request Reject" }, - { 0x0080, "RX Configuration Result ACK" }, - { 0x0081, "RX Configuration Result NACK" }, - { 0x0082, "RX Configuration Result" }, - { 0x0084, "Start Request" }, - { 0x0086, "Start Request Accept" }, - { 0x0087, "Start Request Reject" }, - { 0x0088, "Start Result ACK" }, - { 0x0089, "Start Result NACK" }, - { 0x008a, "Start Result" }, - { 0x008c, "Status Request" }, - { 0x008e, "Status Response" }, - { 0x008f, "Status Reject" }, - { 0x0094, "Test Request" }, - { 0x0096, "Test Request Accept" }, - { 0x0097, "Test Request Reject" }, - { 0x0098, "Test Result ACK" }, - { 0x0099, "Test Result NACK" }, - { 0x009a, "Test Result" }, - { 0x00a0, "TF Configuration Request" }, - { 0x00a2, "TF Configuration Request Accept" }, - { 0x00a3, "TF Configuration Request Reject" }, - { 0x00a4, "TF Configuration Result ACK" }, - { 0x00a5, "TF Configuration Result NACK" }, - { 0x00a6, "TF Configuration Result" }, - { 0x00a8, "TS Configuration Request" }, - { 0x00aa, "TS Configuration Request Accept" }, - { 0x00ab, "TS Configuration Request Reject" }, - { 0x00ac, "TS Configuration Result ACK" }, - { 0x00ad, "TS Configuration Result NACK" }, - { 0x00ae, "TS Configuration Result" }, - { 0x00b0, "TX Configuration Request" }, - { 0x00b2, "TX Configuration Request Accept" }, - { 0x00b3, "TX Configuration Request Reject" }, - { 0x00b4, "TX Configuration Result ACK" }, - { 0x00b5, "TX Configuration Result NACK" }, - { 0x00b6, "TX Configuration Result" }, - { 0x00bc, "DIP Alarm Report ACK" }, - { 0x00bd, "DIP Alarm Report NACK" }, - { 0x00be, "DIP Alarm Report" }, - { 0x00c0, "DIP Alarm Status Request" }, - { 0x00c2, "DIP Alarm Status Response" }, - { 0x00c3, "DIP Alarm Status Reject" }, - { 0x00c4, "DIP Quality Report I ACK" }, - { 0x00c5, "DIP Quality Report I NACK" }, - { 0x00c6, "DIP Quality Report I" }, - { 0x00c8, "DIP Quality Report II ACK" }, - { 0x00c9, "DIP Quality Report II NACK" }, - { 0x00ca, "DIP Quality Report II" }, - { 0x00dc, "DP Configuration Request" }, - { 0x00de, "DP Configuration Request Accept" }, - { 0x00df, "DP Configuration Request Reject" }, - { 0x00e0, "DP Configuration Result ACK" }, - { 0x00e1, "DP Configuration Result NACK" }, - { 0x00e2, "DP Configuration Result" }, - { 0x00e4, "Capabilities HW Info Report ACK" }, - { 0x00e5, "Capabilities HW Info Report NACK" }, - { 0x00e6, "Capabilities HW Info Report" }, - { 0x00e8, "Capabilities Request" }, - { 0x00ea, "Capabilities Request Accept" }, - { 0x00eb, "Capabilities Request Reject" }, - { 0x00ec, "Capabilities Result ACK" }, - { 0x00ed, "Capabilities Result NACK" }, - { 0x00ee, "Capabilities Result" }, - { 0x00f0, "FM Configuration Request" }, - { 0x00f2, "FM Configuration Request Accept" }, - { 0x00f3, "FM Configuration Request Reject" }, - { 0x00f4, "FM Configuration Result ACK" }, - { 0x00f5, "FM Configuration Result NACK" }, - { 0x00f6, "FM Configuration Result" }, - { 0x00f8, "FM Report Request" }, - { 0x00fa, "FM Report Response" }, - { 0x00fb, "FM Report Reject" }, - { 0x00fc, "FM Start Command" }, - { 0x00fe, "FM Start Complete" }, - { 0x00ff, "FM Start Reject" }, - { 0x0100, "FM Stop Command" }, - { 0x0102, "FM Stop Complete" }, - { 0x0103, "FM Stop Reject" }, - { 0x0104, "Negotiation Request ACK" }, - { 0x0105, "Negotiation Request NACK" }, - { 0x0106, "Negotiation Request" }, - { 0x0108, "BTS Initiated Request ACK" }, - { 0x0109, "BTS Initiated Request NACK" }, - { 0x010a, "BTS Initiated Request" }, - { 0x010c, "Radio Channels Release Command" }, - { 0x010e, "Radio Channels Release Complete" }, - { 0x010f, "Radio Channels Release Reject" }, - { 0x0118, "Feature Control Command" }, - { 0x011a, "Feature Control Complete" }, - { 0x011b, "Feature Control Reject" }, - - { 0, NULL } -}; - -/* TS 12.21 Section 9.4: Attributes */ -static const struct value_string om2k_attr_vals[] = { - { 0x00, "Accordance indication" }, - { 0x01, "Alarm Id" }, - { 0x02, "Alarm Data" }, - { 0x03, "Alarm Severity" }, - { 0x04, "Alarm Status" }, - { 0x05, "Alarm Status Type" }, - { 0x06, "BCC" }, - { 0x07, "BS_AG_BKS_RES" }, - { 0x09, "BSIC" }, - { 0x0a, "BA_PA_MFRMS" }, - { 0x0b, "CBCH Indicator" }, - { 0x0c, "CCCH Options" }, - { 0x0d, "Calendar Time" }, - { 0x0f, "Channel Combination" }, - { 0x10, "CON Connection List" }, - { 0x11, "Data End Indication" }, - { 0x12, "DRX_DEV_MAX" }, - { 0x13, "End List Number" }, - { 0x14, "External Condition Map Class 1" }, - { 0x15, "External Condition Map Class 2" }, - { 0x16, "File Relation Indication" }, - { 0x17, "File Revision" }, - { 0x18, "File Segment Data" }, - { 0x19, "File Segment Length" }, - { 0x1a, "File Segment Sequence Number" }, - { 0x1b, "File Size" }, - { 0x1c, "Filling Marker" }, - { 0x1d, "FN Offset" }, - { 0x1e, "Frequency List" }, - { 0x1f, "Frequency Specifier RX" }, - { 0x20, "Frequency Specifier TX" }, - { 0x21, "HSN" }, - { 0x22, "ICM Indicator" }, - { 0x23, "Internal Fault Map Class 1A" }, - { 0x24, "Internal Fault Map Class 1B" }, - { 0x25, "Internal Fault Map Class 2A" }, - { 0x26, "Internal Fault Map Class 2A Extension" }, - { 0x27, "IS Connection List" }, - { 0x28, "List Number" }, - { 0x29, "File Package State Indication" }, - { 0x2a, "Local Access State" }, - { 0x2b, "MAIO" }, - { 0x2c, "MO State" }, - { 0x2d, "Ny1" }, - { 0x2e, "Operational Information" }, - { 0x2f, "Power" }, - { 0x30, "RU Position Data" }, - { 0x31, "Protocol Error" }, - { 0x32, "Reason Code" }, - { 0x33, "Receiver Diversity" }, - { 0x34, "Replacement Unit Map" }, - { 0x35, "Result Code" }, - { 0x36, "RU Revision Data" }, - { 0x38, "T3105" }, - { 0x39, "Test Loop Setting" }, - { 0x3a, "TF Mode" }, - { 0x3b, "TF Compensation Value" }, - { 0x3c, "Time Slot Number" }, - { 0x3d, "TSC" }, - { 0x3e, "RU Logical Id" }, - { 0x3f, "RU Serial Number Data" }, - { 0x40, "BTS Version" }, - { 0x41, "OML IWD Version" }, - { 0x42, "RWL IWD Version" }, - { 0x43, "OML Function Map 1" }, - { 0x44, "OML Function Map 2" }, - { 0x45, "RSL Function Map 1" }, - { 0x46, "RSL Function Map 2" }, - { 0x47, "Extended Range Indicator" }, - { 0x48, "Request Indicators" }, - { 0x49, "DIP Alarm Condition Map" }, - { 0x4a, "ES Incoming" }, - { 0x4b, "ES Outgoing" }, - { 0x4e, "SES Incoming" }, - { 0x4f, "SES Outgoing" }, - { 0x50, "Replacement Unit Map Extension" }, - { 0x52, "UAS Incoming" }, - { 0x53, "UAS Outgoing" }, - { 0x58, "DF Incoming" }, - { 0x5a, "DF Outgoing" }, - { 0x5c, "SF" }, - { 0x60, "S Bits Setting" }, - { 0x61, "CRC-4 Use Option" }, - { 0x62, "T Parameter" }, - { 0x63, "N Parameter" }, - { 0x64, "N1 Parameter" }, - { 0x65, "N3 Parameter" }, - { 0x66, "N4 Parameter" }, - { 0x67, "P Parameter" }, - { 0x68, "Q Parameter" }, - { 0x69, "BI_Q1" }, - { 0x6a, "BI_Q2" }, - { 0x74, "ICM Boundary Parameters" }, - { 0x77, "AFT" }, - { 0x78, "AFT RAI" }, - { 0x79, "Link Supervision Control" }, - { 0x7a, "Link Supervision Filtering Time" }, - { 0x7b, "Call Supervision Time" }, - { 0x7c, "Interval Length UAS Incoming" }, - { 0x7d, "Interval Length UAS Outgoing" }, - { 0x7e, "ICM Channel Rate" }, - { 0x7f, "Attribute Identifier" }, - { 0x80, "FM Frequency List" }, - { 0x81, "FM Frequency Report" }, - { 0x82, "FM Percentile" }, - { 0x83, "FM Clear Indication" }, - { 0x84, "HW Info Signature" }, - { 0x85, "MO Record" }, - { 0x86, "TF Synchronisation Source" }, - { 0x87, "TTA" }, - { 0x88, "End Segment Number" }, - { 0x89, "Segment Number" }, - { 0x8a, "Capabilities Signature" }, - { 0x8c, "File Relation List" }, - { 0x90, "Negotiation Record I" }, - { 0x91, "Negotiation Record II" }, - { 0x92, "Encryption Algorithm" }, - { 0x94, "Interference Rejection Combining" }, - { 0x95, "Dedication Information" }, - { 0x97, "Feature Code" }, - { 0x98, "FS Offset" }, - { 0x99, "ESB Timeslot" }, - { 0x9a, "Master TG Instance" }, - { 0x9b, "Master TX Chain Delay" }, - { 0x9c, "External Condition Class 2 Extension" }, - { 0x9d, "TSs MO State" }, - { 0, NULL } -}; - -const struct value_string om2k_mo_class_short_vals[] = { - { 0x01, "TRXC" }, - { 0x03, "TS" }, - { 0x04, "TF" }, - { 0x05, "IS" }, - { 0x06, "CON" }, - { 0x07, "DP" }, - { 0x0a, "CF" }, - { 0x0b, "TX" }, - { 0x0c, "RX" }, - { 0, NULL } -}; - -static struct msgb *om2k_msgb_alloc(void) -{ - return msgb_alloc_headroom(OM_ALLOC_SIZE, OM_HEADROOM_SIZE, - "OM2000"); -} - -static 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; -} - -static int abis_om2k_sendmsg(struct gsm_bts *bts, struct msgb *msg) -{ - struct abis_om2k_hdr *o2h; - int to_trx_oml; - - msg->l2h = msg->data; - o2h = (struct abis_om2k_hdr *) msg->l2h; - - switch (o2h->mo.class) { - case OM2K_MO_CLS_TRXC: - case OM2K_MO_CLS_TX: - case OM2K_MO_CLS_RX: - case OM2K_MO_CLS_TS: - /* Route through per-TRX OML Link to the appropriate TRX */ - to_trx_oml = 1; - msg->trx = gsm_bts_trx_by_nr(bts, o2h->mo.inst); - if (!msg->trx) { - LOGP(DNM, LOGL_ERROR, "MO=%s Tx Dropping msg to " - "non-existing TRX\n", om2k_mo_name(&o2h->mo)); - return -ENODEV; - } - break; - default: - /* Route through the IXU/DXU OML Link */ - msg->trx = bts->c0; - to_trx_oml = 0; - break; - } - - return _abis_nm_sendmsg(msg, to_trx_oml); -} - -static void fill_om2k_hdr(struct abis_om2k_hdr *o2h, const struct abis_om2k_mo *mo, - uint16_t msg_type, uint8_t attr_len) -{ - o2h->om.mdisc = ABIS_OM_MDISC_FOM; - o2h->om.placement = ABIS_OM_PLACEMENT_ONLY; - o2h->om.sequence = 0; - o2h->om.length = 6 + attr_len; - o2h->msg_type = htons(msg_type); - memcpy(&o2h->mo, mo, sizeof(o2h->mo)); -} - -const struct abis_om2k_mo om2k_mo_cf = { OM2K_MO_CLS_CF, 0, 0xFF, 0 }; -const struct abis_om2k_mo om2k_mo_is = { OM2K_MO_CLS_IS, 0, 0xFF, 0 }; -const struct abis_om2k_mo om2k_mo_con = { OM2K_MO_CLS_CON, 0, 0xFF, 0 }; - -static int abis_om2k_cal_time_resp(struct gsm_bts *bts) -{ - struct msgb *msg = om2k_msgb_alloc(); - struct abis_om2k_hdr *o2k; - time_t tm_t; - struct tm *tm; - - o2k = (struct abis_om2k_hdr *) msgb_put(msg, sizeof(*o2k)); - fill_om2k_hdr(o2k, &om2k_mo_cf, OM2K_MSGT_CAL_TIME_RESP, 7); - - 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, 0); - - 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, 2); - - 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)); - - return abis_om2k_sendmsg(bts, msg); -} - -int abis_om2k_tx_is_conf_req(struct gsm_bts *bts, struct om2k_is_conn_grp *cg, - unsigned int num_cg ) -{ - struct msgb *msg = om2k_msgb_alloc(); - struct abis_om2k_hdr *o2k; - - o2k = (struct abis_om2k_hdr *) msgb_put(msg, sizeof(*o2k)); - fill_om2k_hdr(o2k, &om2k_mo_is, OM2K_MSGT_IS_CONF_REQ, - 2 + 2 + TLV_GROSS_LEN(num_cg * sizeof(*cg))); - - 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_cg * sizeof(*cg), (uint8_t *)cg); - - return abis_om2k_sendmsg(bts, msg); -} - -int abis_om2k_tx_con_conf_req(struct gsm_bts *bts, uint8_t *data, - unsigned int len) -{ - struct msgb *msg = om2k_msgb_alloc(); - struct abis_om2k_hdr *o2k; - - o2k = (struct abis_om2k_hdr *) msgb_put(msg, sizeof(*o2k)); - fill_om2k_hdr(o2k, &om2k_mo_con, OM2K_MSGT_CON_CONF_REQ, - 2 + 2 + TLV_GROSS_LEN(len)); - - msgb_tv_put(msg, OM2K_DEI_LIST_NR, 1); - msgb_tv_put(msg, OM2K_DEI_END_LIST_NR, 1); - - msgb_tlv_put(msg, OM2K_DEI_CON_CONN_LIST, len, data); - - return abis_om2k_sendmsg(bts, msg); -} - - -static int abis_om2k_tx_negot_req_ack(struct gsm_bts *bts, const struct abis_om2k_mo *mo, - uint8_t *data, unsigned int len) -{ - struct msgb *msg = om2k_msgb_alloc(); - struct abis_om2k_hdr *o2k; - - o2k = (struct abis_om2k_hdr *) msgb_put(msg, sizeof(*o2k)); - fill_om2k_hdr(o2k, mo, OM2K_MSGT_NEGOT_REQ_ACK, 2+len); - - 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 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(msg->trx->bts, &o2h->mo, out_buf, out_cur - out_buf); -} - -static int om2k_rx_start_res(struct msgb *msg) -{ - struct abis_om2k_hdr *o2h = msgb_l2(msg); - int rc; - - rc = abis_om2k_tx_simple(msg->trx->bts, &o2h->mo, OM2K_MSGT_START_RES_ACK); - rc = abis_om2k_tx_op_info(msg->trx->bts, &o2h->mo, 1); - - return rc; -} - -static int om2k_rx_op_info_ack(struct msgb *msg) -{ - struct abis_om2k_hdr *o2h = msgb_l2(msg); - - /* FIXME: update Operational state in our structures */ - - return 0; -} - -int abis_om2k_rcvmsg(struct msgb *msg) -{ - struct gsm_bts *bts = msg->trx->bts; - struct abis_om2k_hdr *o2h = msgb_l2(msg); - struct abis_om_hdr *oh = &o2h->om; - uint16_t msg_type = ntohs(o2h->msg_type); - int rc = 0; - - /* Various consistency checks */ - if (oh->placement != ABIS_OM_PLACEMENT_ONLY) { - LOGP(DNM, LOGL_ERROR, "ABIS OML placement 0x%x not supported\n", - oh->placement); - if (oh->placement != ABIS_OM_PLACEMENT_FIRST) - return -EINVAL; - } - if (oh->sequence != 0) { - LOGP(DNM, LOGL_ERROR, "ABIS OML sequence 0x%x != 0x00\n", - oh->sequence); - return -EINVAL; - } - - msg->l3h = (unsigned char *)o2h + sizeof(*o2h); - - if (oh->mdisc != ABIS_OM_MDISC_FOM) { - LOGP(DNM, LOGL_ERROR, "unknown ABIS OM2000 message discriminator 0x%x\n", - oh->mdisc); - return -EINVAL; - } - - DEBUGP(DNM, "Rx MO=%s %s (%s)\n", om2k_mo_name(&o2h->mo), - get_value_string(om2k_msgcode_vals, msg_type), - hexdump(msg->l2h, msgb_l2len(msg))); - - switch (msg_type) { - case OM2K_MSGT_CAL_TIME_REQ: - rc = abis_om2k_cal_time_resp(bts); - break; - case OM2K_MSGT_FAULT_REP: - rc = abis_om2k_tx_simple(bts, &o2h->mo, OM2K_MSGT_FAULT_REP_ACK); - break; - case OM2K_MSGT_NEGOT_REQ: - rc = om2k_rx_negot_req(msg); - break; - case OM2K_MSGT_START_RES: - rc = om2k_rx_start_res(msg); - break; - case OM2K_MSGT_OP_INFO_ACK: - rc = om2k_rx_op_info_ack(msg); - break; - case OM2K_MSGT_IS_CONF_RES: - rc = abis_om2k_tx_simple(bts, &o2h->mo, OM2K_MSGT_IS_CONF_RES_ACK); - 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_CONNECT_COMPL: - rc = abis_om2k_tx_simple(bts, &o2h->mo, OM2K_MSGT_RESET_CMD); - break; - case OM2K_MSGT_RESET_COMPL: - rc = abis_om2k_tx_simple(bts, &o2h->mo, OM2K_MSGT_START_REQ); - break; - case OM2K_MSGT_ENABLE_RES: - rc = abis_om2k_tx_simple(bts, &o2h->mo, OM2K_MSGT_ENABLE_RES_ACK); - 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_STATUS_RESP: - break; - case OM2K_MSGT_START_REQ_ACK: - case OM2K_MSGT_CON_CONF_REQ_ACK: - case OM2K_MSGT_IS_CONF_REQ_ACK: - case OM2K_MSGT_ENABLE_REQ_ACK: - case OM2K_MSGT_ALARM_STATUS_REQ_ACK: - case OM2K_MSGT_DISABLE_REQ_ACK: - break; - default: - LOGP(DNM, LOGL_NOTICE, "Rx unhandled OM2000 msg %s\n", - get_value_string(om2k_msgcode_vals, msg_type)); - } - - msgb_free(msg); - return rc; -} diff --git a/openbsc/src/bsc/abis_om2000_vty.c b/openbsc/src/bsc/abis_om2000_vty.c deleted file mode 100644 index 5949c869c..000000000 --- a/openbsc/src/bsc/abis_om2000_vty.c +++ /dev/null @@ -1,456 +0,0 @@ -/* VTY interface for A-bis OM2000 */ - -/* (C) 2010-2011 by Harald Welte - * - * All Rights Reserved - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation; either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - * - */ - -#include -#include -#include -#include - -#include - -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include - -extern struct gsm_network *bsc_gsmnet; - -static struct cmd_node om2k_node = { - OM2K_NODE, - "%s(om2k)# ", - 1, -}; - -struct oml_node_state { - struct gsm_bts *bts; - struct abis_om2k_mo mo; -}; - -static int dummy_config_write(struct vty *v) -{ - return CMD_SUCCESS; -} - -/* FIXME: auto-generate those strings from the value_string lists */ -#define OM2K_OBJCLASS_VTY "(trxc|ts|tf|is|con|dp|cf|tx|rx)" -#define OM2K_OBJCLASS_VTY_HELP "TRX Controller\n" \ - "Timeslot\n" \ - "Timing Function\n" \ - "Interface Switch\n" \ - "Abis Concentrator\n" \ - "Digital Path\n" \ - "Central Function\n" \ - "Transmitter\n" \ - "Receiver\n" - -DEFUN(om2k_class_inst, om2k_class_inst_cmd, - "bts <0-255> om2000 class " OM2K_OBJCLASS_VTY - " <0-255> <0-255> <0-255>", - "BTS related commands\n" "BTS Number\n" - "Manipulate the OM2000 managed objects\n" - "Object Class\n" OM2K_OBJCLASS_VTY_HELP - "BTS Number\n" "Associated SO Instance\n" "Instance Number\n") -{ - struct gsm_bts *bts; - struct oml_node_state *oms; - int bts_nr = atoi(argv[0]); - - bts = gsm_bts_num(bsc_gsmnet, bts_nr); - if (!bts) { - vty_out(vty, "%% No such BTS (%d)%s", bts_nr, VTY_NEWLINE); - return CMD_WARNING; - } - - if (bts->type != GSM_BTS_TYPE_RBS2000) { - vty_out(vty, "%% BTS %d not an Ericsson RBS%s", - bts_nr, VTY_NEWLINE); - return CMD_WARNING; - } - - oms = talloc_zero(tall_bsc_ctx, struct oml_node_state); - if (!oms) - return CMD_WARNING; - - oms->bts = bts; - oms->mo.class = get_string_value(om2k_mo_class_short_vals, argv[1]); - oms->mo.bts = atoi(argv[2]); - oms->mo.assoc_so = atoi(argv[3]); - oms->mo.inst = atoi(argv[4]); - - vty->index = oms; - vty->node = OM2K_NODE; - - return CMD_SUCCESS; - -} - -DEFUN(om2k_classnum_inst, om2k_classnum_inst_cmd, - "bts <0-255> om2000 class <0-255> <0-255> <0-255> <0-255>", - "BTS related commands\n" "BTS Number\n" - "Manipulate the OML managed objects\n" - "Object Class\n" "Object Class\n" - "BTS Number\n" "Associated SO Instance\n" "Instance Number\n") -{ - struct gsm_bts *bts; - struct oml_node_state *oms; - int bts_nr = atoi(argv[0]); - - bts = gsm_bts_num(bsc_gsmnet, bts_nr); - if (!bts) { - vty_out(vty, "%% No such BTS (%d)%s", bts_nr, VTY_NEWLINE); - return CMD_WARNING; - } - - oms = talloc_zero(tall_bsc_ctx, struct oml_node_state); - if (!oms) - return CMD_WARNING; - - oms->bts = bts; - oms->mo.class = atoi(argv[1]); - oms->mo.bts = atoi(argv[2]); - oms->mo.assoc_so = atoi(argv[3]); - oms->mo.inst = atoi(argv[4]); - - vty->index = oms; - vty->node = OM2K_NODE; - - return CMD_SUCCESS; -} - -DEFUN(om2k_reset, om2k_reset_cmd, - "reset-command", - "Reset the MO\n") -{ - struct oml_node_state *oms = vty->index; - - abis_om2k_tx_reset_cmd(oms->bts, &oms->mo); - return CMD_SUCCESS; -} - -DEFUN(om2k_start, om2k_start_cmd, - "start-request", - "Start the MO\n") -{ - struct oml_node_state *oms = vty->index; - - abis_om2k_tx_start_req(oms->bts, &oms->mo); - return CMD_SUCCESS; -} - -DEFUN(om2k_status, om2k_status_cmd, - "status-request", - "Get the MO Status\n") -{ - struct oml_node_state *oms = vty->index; - - abis_om2k_tx_status_req(oms->bts, &oms->mo); - return CMD_SUCCESS; -} - -DEFUN(om2k_connect, om2k_connect_cmd, - "connect-command", - "Connect the MO\n") -{ - struct oml_node_state *oms = vty->index; - - abis_om2k_tx_connect_cmd(oms->bts, &oms->mo); - return CMD_SUCCESS; -} - -DEFUN(om2k_disconnect, om2k_disconnect_cmd, - "disconnect-command", - "Disconnect the MO\n") -{ - struct oml_node_state *oms = vty->index; - - abis_om2k_tx_disconnect_cmd(oms->bts, &oms->mo); - return CMD_SUCCESS; -} - -DEFUN(om2k_enable, om2k_enable_cmd, - "enable-request", - "Enable the MO\n") -{ - struct oml_node_state *oms = vty->index; - - abis_om2k_tx_enable_req(oms->bts, &oms->mo); - return CMD_SUCCESS; -} - -DEFUN(om2k_disable, om2k_disable_cmd, - "disable-request", - "Disable the MO\n") -{ - struct oml_node_state *oms = vty->index; - - abis_om2k_tx_disable_req(oms->bts, &oms->mo); - return CMD_SUCCESS; -} - -DEFUN(om2k_op_info, om2k_op_info_cmd, - "operational-info <0-1>", - "Set operational information\n") -{ - struct oml_node_state *oms = vty->index; - int oper = atoi(argv[0]); - - abis_om2k_tx_op_info(oms->bts, &oms->mo, oper); - return CMD_SUCCESS; -} - -DEFUN(om2k_test, om2k_test_cmd, - "test-request", - "Test the MO\n") -{ - struct oml_node_state *oms = vty->index; - - abis_om2k_tx_test_req(oms->bts, &oms->mo); - return CMD_SUCCESS; -} - -struct con_conn_group { - struct llist_head list; - - uint8_t cg; - uint16_t ccp; - uint8_t tag; - uint8_t tei; -}; - -static void add_con_list(struct gsm_bts *bts, uint8_t cg, uint16_t ccp, - uint8_t tag, uint8_t tei) -{ - struct con_conn_group *ent = talloc_zero(bts, struct con_conn_group); - - ent->cg = cg; - ent->ccp = ccp; - ent->tag = tag; - ent->tei = tei; - - llist_add_tail(&ent->list, &bts->rbs2000.con.conn_groups); -} - -static int del_con_list(struct gsm_bts *bts, uint8_t cg, uint16_t ccp, - uint8_t tag, uint8_t tei) -{ - struct con_conn_group *grp, *grp2; - - llist_for_each_entry_safe(grp, grp2, &bts->rbs2000.con.conn_groups, list) { - if (grp->cg == cg && grp->ccp == ccp && grp->tag == tag - && grp->tei == tei) { - llist_del(&grp->list); - talloc_free(grp); - return 0; - } - } - return -ENOENT; -} - -#define CON_LIST_HELP "CON connetiton list\n" \ - "Add entry to CON list\n" \ - "Delete entry from CON list\n" \ - "Connection Group Number\n" \ - "CON Connection Point\n" \ - -DEFUN(om2k_con_list_dec, om2k_con_list_dec_cmd, - "con-connection-list (add|del) <1-255> <0-1023> deconcentrated", - CON_LIST_HELP "De-concentrated in/outlet\n") -{ - struct oml_node_state *oms = vty->index; - struct gsm_bts *bts = oms->bts; - uint8_t cg = atoi(argv[1]); - uint16_t ccp = atoi(argv[2]); - - if (!strcmp(argv[0], "add")) - add_con_list(bts, cg, ccp, 0, 0xff); - else { - if (del_con_list(bts, cg, ccp, 0, 0xff) < 0) { - vty_out(vty, "%% No matching CON list entry%s", - VTY_NEWLINE); - return CMD_WARNING; - } - } - - return CMD_SUCCESS; -} - -DEFUN(om2k_con_list_tei, om2k_con_list_tei_cmd, - "con-connection-list (add|del) <1-255> <0-1023> tei <0-63>", - CON_LIST_HELP "Concentrated in/outlet with TEI\n" "TEI Number\n") -{ - struct oml_node_state *oms = vty->index; - struct gsm_bts *bts = oms->bts; - uint8_t cg = atoi(argv[1]); - uint16_t ccp = atoi(argv[2]); - uint8_t tei = atoi(argv[3]); - - if (!strcmp(argv[0], "add")) - add_con_list(bts, cg, ccp, cg, tei); - else { - if (del_con_list(bts, cg, ccp, cg, tei) < 0) { - vty_out(vty, "%% No matching CON list entry%s", - VTY_NEWLINE); - return CMD_WARNING; - } - } - - return CMD_SUCCESS; -} - -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; -} - -struct is_conn_group { - struct llist_head list; - uint16_t icp1; - uint16_t icp2; - uint8_t ci; -}; - -DEFUN(cfg_bts_is_conn_list, cfg_bts_is_conn_list_cmd, - "is-connection-list (add|del) <0-2047> <0-2047> <0-255>", - "Interface Switch Connnection List\n" - "Add to IS list\n" "Delete from IS list\n" - "ICP1\n" "ICP2\n" "Contiguity Index\n") -{ - struct gsm_bts *bts = vty->index; - uint16_t icp1 = atoi(argv[1]); - uint16_t icp2 = atoi(argv[2]); - uint8_t ci = atoi(argv[3]); - struct is_conn_group *grp, *grp2; - - if (!strcmp(argv[0], "add")) { - grp = talloc_zero(bts, struct is_conn_group); - grp->icp1 = icp1; - grp->icp2 = icp2; - grp->ci = ci; - llist_add_tail(&grp->list, &bts->rbs2000.is.conn_groups); - } else { - llist_for_each_entry_safe(grp, grp2, &bts->rbs2000.is.conn_groups, list) { - if (grp->icp1 == icp1 && grp->icp2 == icp2 - && grp->ci == ci) { - llist_del(&grp->list); - talloc_free(grp); - return CMD_SUCCESS; - } - } - vty_out(vty, "%% No matching IS Conn Group found!%s", - VTY_NEWLINE); - return CMD_WARNING; - } - - return CMD_SUCCESS; -} - - -DEFUN(om2k_is_conf_req, om2k_is_conf_req_cmd, - "is-conf-req", - "Send IS Configuration Request\n") -{ - struct oml_node_state *oms = vty->index; - struct gsm_bts *bts = oms->bts; - struct is_conn_group *grp; - unsigned int num_grps = 0, i = 0; - struct om2k_is_conn_grp *o2grps; - - /* count number of groups in linked list */ - llist_for_each_entry(grp, &bts->rbs2000.is.conn_groups, list) - num_grps++; - - if (!num_grps) { - vty_out(vty, "%% No IS connection groups configured!%s", - VTY_NEWLINE); - return CMD_WARNING; - } - - /* allocate buffer for oml group array */ - o2grps = 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(&o2grps[i++], grp->icp1, grp->icp2, grp->ci); - - /* send the actual OML request */ - abis_om2k_tx_is_conf_req(oms->bts, o2grps, num_grps); - - talloc_free(o2grps); - - return CMD_SUCCESS; -} - -void abis_om2k_config_write_bts(struct vty *vty, struct gsm_bts *bts) -{ - struct is_conn_group *igrp; - struct con_conn_group *cgrp; - - llist_for_each_entry(igrp, &bts->rbs2000.is.conn_groups, list) - vty_out(vty, " is-connection-list add %u %u %u%s", - igrp->icp1, igrp->icp2, igrp->ci, VTY_NEWLINE); - - llist_for_each_entry(cgrp, &bts->rbs2000.con.conn_groups, list) { - vty_out(vty, " con-connection-list add %u %u ", - cgrp->cg, cgrp->ccp); - if (cgrp->tei == 0xff) - vty_out(vty, "deconcentrated%s", VTY_NEWLINE); - else - vty_out(vty, "tei %u%s", cgrp->tei, VTY_NEWLINE); - } -} - -int abis_om2k_vty_init(void) -{ - install_element(ENABLE_NODE, &om2k_class_inst_cmd); - install_element(ENABLE_NODE, &om2k_classnum_inst_cmd); - install_node(&om2k_node, dummy_config_write); - - install_default(OM2K_NODE); - install_element(OM2K_NODE, &ournode_exit_cmd); - 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_is_conf_req_cmd); - install_element(OM2K_NODE, &om2k_con_list_dec_cmd); - install_element(OM2K_NODE, &om2k_con_list_tei_cmd); - - install_element(BTS_NODE, &cfg_bts_is_conn_list_cmd); - - return 0; -} diff --git a/openbsc/src/bsc/abis_rsl.c b/openbsc/src/bsc/abis_rsl.c deleted file mode 100644 index 07a7dc68c..000000000 --- a/openbsc/src/bsc/abis_rsl.c +++ /dev/null @@ -1,1960 +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 - * - * All Rights Reserved - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation; either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - * - */ - -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -#define RSL_ALLOC_SIZE 1024 -#define RSL_ALLOC_HEADROOM 128 - -#define MAX(a, b) (a) >= (b) ? (a) : (b) - -static int rsl_send_imm_assignment(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; - dispatch_signal(SS_LCHAN, sig_no, &sig); -} - -static u_int8_t mdisc_by_msgtype(u_int8_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, - u_int8_t msg_type) -{ - dh->c.msg_discr = mdisc_by_msgtype(msg_type); - dh->c.msg_type = msg_type; - dh->ie_chan = RSL_IE_CHAN_NR; -} - -/* determine logical channel based on TRX and channel number IE */ -struct gsm_lchan *lchan_lookup(struct gsm_bts_trx *trx, u_int8_t chan_nr) -{ - struct gsm_lchan *lchan; - u_int8_t ts_nr = chan_nr & 0x07; - u_int8_t cbits = chan_nr >> 3; - u_int8_t lch_idx; - struct gsm_bts_trx_ts *ts = &trx->ts[ts_nr]; - - if (cbits == 0x01) { - lch_idx = 0; /* TCH/F */ - if (ts->pchan != GSM_PCHAN_TCH_F && - ts->pchan != GSM_PCHAN_PDCH && - ts->pchan != GSM_PCHAN_TCH_F_PDCH) - LOGP(DRSL, LOGL_ERROR, "chan_nr=0x%02x but pchan=%u\n", - chan_nr, ts->pchan); - } else if ((cbits & 0x1e) == 0x02) { - lch_idx = cbits & 0x1; /* TCH/H */ - if (ts->pchan != GSM_PCHAN_TCH_H) - LOGP(DRSL, LOGL_ERROR, "chan_nr=0x%02x but pchan=%u\n", - chan_nr, ts->pchan); - } else if ((cbits & 0x1c) == 0x04) { - lch_idx = cbits & 0x3; /* SDCCH/4 */ - if (ts->pchan != GSM_PCHAN_CCCH_SDCCH4) - LOGP(DRSL, LOGL_ERROR, "chan_nr=0x%02x but pchan=%u\n", - chan_nr, ts->pchan); - } else if ((cbits & 0x18) == 0x08) { - lch_idx = cbits & 0x7; /* SDCCH/8 */ - if (ts->pchan != GSM_PCHAN_SDCCH8_SACCH8C) - LOGP(DRSL, LOGL_ERROR, "chan_nr=0x%02x but pchan=%u\n", - chan_nr, ts->pchan); - } else if (cbits == 0x10 || cbits == 0x11 || cbits == 0x12) { - lch_idx = 0; - if (ts->pchan != GSM_PCHAN_CCCH && - ts->pchan != GSM_PCHAN_CCCH_SDCCH4) - LOGP(DRSL, LOGL_ERROR, "chan_nr=0x%02x but pchan=%u\n", - chan_nr, ts->pchan); - /* FIXME: we should not return first sdcch4 !!! */ - } else { - LOGP(DRSL, LOGL_ERROR, "unknown chan_nr=0x%02x\n", chan_nr); - return NULL; - } - - lchan = &ts->lchan[lch_idx]; - log_set_context(BSC_CTX_LCHAN, lchan); - if (lchan->conn) - log_set_context(BSC_CTX_SUBSCR, lchan->conn->subscr); - - return lchan; -} - -/* See Table 10.5.25 of GSM04.08 */ -static u_int8_t ts2chan_nr(const struct gsm_bts_trx_ts *ts, uint8_t lchan_nr) -{ - u_int8_t cbits, chan_nr; - - switch (ts->pchan) { - case GSM_PCHAN_TCH_F: - case GSM_PCHAN_PDCH: - case GSM_PCHAN_TCH_F_PDCH: - cbits = 0x01; - break; - case GSM_PCHAN_TCH_H: - cbits = 0x02; - cbits += lchan_nr; - break; - case GSM_PCHAN_CCCH_SDCCH4: - cbits = 0x04; - cbits += lchan_nr; - break; - case GSM_PCHAN_SDCCH8_SACCH8C: - cbits = 0x08; - cbits += lchan_nr; - break; - default: - case GSM_PCHAN_CCCH: - cbits = 0x10; - break; - } - - chan_nr = (cbits << 3) | (ts->nr & 0x7); - - return chan_nr; -} - -u_int8_t lchan2chan_nr(const struct gsm_lchan *lchan) -{ - return ts2chan_nr(lchan->ts, lchan->nr); -} - -/* As per TS 03.03 Section 2.2, the IMSI has 'not more than 15 digits' */ -u_int64_t str_to_imsi(const char *imsi_str) -{ - u_int64_t ret; - - ret = strtoull(imsi_str, NULL, 10); - - return ret; -} - -/* Table 5 Clause 7 TS 05.02 */ -unsigned int n_pag_blocks(int bs_ccch_sdcch_comb, unsigned int bs_ag_blks_res) -{ - if (!bs_ccch_sdcch_comb) - return 9 - bs_ag_blks_res; - else - return 3 - bs_ag_blks_res; -} - -/* Chapter 6.5.2 of TS 05.02 */ -unsigned int get_ccch_group(u_int64_t imsi, unsigned int bs_cc_chans, - unsigned int n_pag_blocks) -{ - return (imsi % 1000) % (bs_cc_chans * n_pag_blocks) / n_pag_blocks; -} - -/* Chapter 6.5.2 of TS 05.02 */ -unsigned int get_paging_group(u_int64_t imsi, unsigned int bs_cc_chans, - int n_pag_blocks) -{ - return (imsi % 1000) % (bs_cc_chans * n_pag_blocks) % n_pag_blocks; -} - -static struct msgb *rsl_msgb_alloc(void) -{ - return msgb_alloc_headroom(RSL_ALLOC_SIZE, RSL_ALLOC_HEADROOM, - "RSL"); -} - -#define MACBLOCK_SIZE 23 -static void pad_macblock(u_int8_t *out, const u_int8_t *in, int len) -{ - memcpy(out, in, len); - - if (len < MACBLOCK_SIZE) - memset(out+len, 0x2b, MACBLOCK_SIZE-len); -} - -/* Chapter 9.3.7: Encryption Information */ -static int build_encr_info(u_int8_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 u_int8_t *cause_v, u_int8_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]); -} - -/* Send a BCCH_INFO message as per Chapter 8.5.1 */ -int rsl_bcch_info(struct gsm_bts_trx *trx, u_int8_t type, - const u_int8_t *data, int len) -{ - struct abis_rsl_dchan_hdr *dh; - struct msgb *msg = rsl_msgb_alloc(); - - dh = (struct abis_rsl_dchan_hdr *) msgb_put(msg, sizeof*dh); - init_dchan_hdr(dh, RSL_MT_BCCH_INFO); - dh->chan_nr = RSL_CHAN_BCCH; - - msgb_tv_put(msg, RSL_IE_SYSINFO_TYPE, type); - msgb_tlv_put(msg, RSL_IE_FULL_BCCH_INFO, len, data); - - msg->trx = trx; - - return abis_rsl_sendmsg(msg); -} - -int rsl_sacch_filling(struct gsm_bts_trx *trx, u_int8_t type, - const u_int8_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->trx = trx; - - return abis_rsl_sendmsg(msg); -} - -int rsl_sacch_info_modify(struct gsm_lchan *lchan, u_int8_t type, - const u_int8_t *data, int len) -{ - struct abis_rsl_dchan_hdr *dh; - struct msgb *msg = rsl_msgb_alloc(); - u_int8_t chan_nr = 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->trx = lchan->ts->trx; - - 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; - u_int8_t chan_nr = 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->trx = lchan->ts->trx; - - 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; - u_int8_t chan_nr = 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->trx = lchan->ts->trx; - - return abis_rsl_sendmsg(msg); -} - -static int channel_mode_from_lchan(struct rsl_ie_chan_mode *cm, - struct gsm_lchan *lchan) -{ - memset(cm, 0, sizeof(cm)); - - /* FIXME: what to do with data calls ? */ - if (lchan->ts->trx->bts->network->dtx_enabled) - cm->dtx_dtu = 0x03; - else - cm->dtx_dtu = 0x00; - - /* set TCH Speech/Data */ - cm->spd_ind = lchan->rsl_cmode; - - if (lchan->rsl_cmode == RSL_CMOD_SPD_SIGN && - lchan->tch_mode != GSM48_CMODE_SIGN) - LOGP(DRSL, LOGL_ERROR, "unsupported: rsl_mode == signalling, " - "but tch_mode != signalling\n"); - - switch (lchan->type) { - case GSM_LCHAN_SDCCH: - cm->chan_rt = RSL_CMOD_CRT_SDCCH; - break; - case GSM_LCHAN_TCH_F: - cm->chan_rt = RSL_CMOD_CRT_TCH_Bm; - break; - case GSM_LCHAN_TCH_H: - cm->chan_rt = RSL_CMOD_CRT_TCH_Lm; - break; - case GSM_LCHAN_NONE: - case GSM_LCHAN_UNKNOWN: - default: - return -EINVAL; - } - - switch (lchan->tch_mode) { - case GSM48_CMODE_SIGN: - cm->chan_rate = 0; - break; - case GSM48_CMODE_SPEECH_V1: - cm->chan_rate = RSL_CMOD_SP_GSM1; - break; - case GSM48_CMODE_SPEECH_EFR: - cm->chan_rate = RSL_CMOD_SP_GSM2; - break; - case GSM48_CMODE_SPEECH_AMR: - cm->chan_rate = RSL_CMOD_SP_GSM3; - break; - case GSM48_CMODE_DATA_14k5: - cm->chan_rate = RSL_CMOD_SP_NT_14k5; - break; - case GSM48_CMODE_DATA_12k0: - cm->chan_rate = RSL_CMOD_SP_NT_12k0; - break; - case GSM48_CMODE_DATA_6k0: - cm->chan_rate = RSL_CMOD_SP_NT_6k0; - break; - default: - return -EINVAL; - } - - return 0; -} - -/* Chapter 8.4.1 */ -#if 0 -int rsl_chan_activate(struct gsm_bts_trx *trx, u_int8_t chan_nr, - u_int8_t act_type, - struct rsl_ie_chan_mode *chan_mode, - struct rsl_ie_chan_ident *chan_ident, - u_int8_t bs_power, u_int8_t ms_power, - u_int8_t ta) -{ - 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_CHAN_ACTIV); - dh->chan_nr = chan_nr; - - msgb_tv_put(msg, RSL_IE_ACT_TYPE, act_type); - /* For compatibility with Phase 1 */ - msgb_tlv_put(msg, RSL_IE_CHAN_MODE, sizeof(*chan_mode), - (u_int8_t *) chan_mode); - msgb_tlv_put(msg, RSL_IE_CHAN_IDENT, 4, - (u_int8_t *) chan_ident); -#if 0 - msgb_tlv_put(msg, RSL_IE_ENCR_INFO, 1, - (u_int8_t *) &encr_info); -#endif - msgb_tv_put(msg, RSL_IE_BS_POWER, bs_power); - msgb_tv_put(msg, RSL_IE_MS_POWER, ms_power); - msgb_tv_put(msg, RSL_IE_TIMING_ADVANCE, ta); - - msg->trx = trx; - - return abis_rsl_sendmsg(msg); -} -#endif - -int rsl_chan_activate_lchan(struct gsm_lchan *lchan, u_int8_t act_type, - u_int8_t ta, u_int8_t ho_ref) -{ - struct abis_rsl_dchan_hdr *dh; - struct msgb *msg; - int rc; - uint8_t *len; - - u_int8_t chan_nr = lchan2chan_nr(lchan); - struct rsl_ie_chan_mode cm; - struct gsm48_chan_desc cd; - - rc = channel_mode_from_lchan(&cm, lchan); - if (rc < 0) - return rc; - - memset(&cd, 0, sizeof(cd)); - gsm48_lchan2chan_desc(&cd, lchan); - - msg = rsl_msgb_alloc(); - dh = (struct abis_rsl_dchan_hdr *) msgb_put(msg, sizeof(*dh)); - init_dchan_hdr(dh, RSL_MT_CHAN_ACTIV); - dh->chan_nr = chan_nr; - - msgb_tv_put(msg, RSL_IE_ACT_TYPE, act_type); - msgb_tlv_put(msg, RSL_IE_CHAN_MODE, sizeof(cm), - (u_int8_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_tlv_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)) { - u_int8_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); - - if (lchan->tch_mode == GSM48_CMODE_SPEECH_AMR) - msgb_tlv_put(msg, RSL_IE_MR_CONFIG, sizeof(lchan->mr_conf), - (u_int8_t *) &lchan->mr_conf); - - msg->trx = lchan->ts->trx; - - 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; - - u_int8_t chan_nr = 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), - (u_int8_t *) &cm); - - if (lchan->encr.alg_id > RSL_ENC_ALG_A5(0)) { - u_int8_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); - } - - if (lchan->tch_mode == GSM48_CMODE_SPEECH_AMR) { - msgb_tlv_put(msg, RSL_IE_MR_CONFIG, sizeof(lchan->mr_conf), - (u_int8_t *) &lchan->mr_conf); - } - - msg->trx = lchan->ts->trx; - - 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; - u_int8_t chan_nr = lchan2chan_nr(lchan); - u_int8_t encr_info[MAX_A5_KEY_LEN+2]; - u_int8_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->trx = lchan->ts->trx; - - 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 = lchan2chan_nr(lchan); - - msg->lchan = lchan; - msg->trx = lchan->ts->trx; - - DEBUGP(DRSL, "%s DEACTivate SACCH CMD\n", gsm_lchan_name(lchan)); - - return abis_rsl_sendmsg(msg); -} - -static void error_timeout_cb(void *data) -{ - struct gsm_lchan *lchan = data; - if (lchan->state != LCHAN_S_REL_ERR) { - LOGP(DRSL, LOGL_ERROR, "%s error timeout but not in error state: %d\n", - gsm_lchan_name(lchan), lchan->state); - return; - } - - /* go back to the none state */ - LOGP(DRSL, LOGL_NOTICE, "%s is back in operation.\n", gsm_lchan_name(lchan)); - rsl_lchan_set_state(lchan, LCHAN_S_NONE); -} - -/* 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) -{ - struct abis_rsl_dchan_hdr *dh; - struct msgb *msg; - - 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 = lchan2chan_nr(lchan); - - msg->lchan = lchan; - msg->trx = lchan->ts->trx; - - DEBUGP(DRSL, "%s RF Channel Release CMD due error %d\n", gsm_lchan_name(lchan), error); - - if (error) { - /* - * the nanoBTS sends RLL release indications after the channel release. This can - * be a problem when we have reassigned the channel to someone else and then can - * not figure out who used this channel. - */ - rsl_lchan_set_state(lchan, LCHAN_S_REL_ERR); - lchan->error_timer.data = lchan; - lchan->error_timer.cb = error_timeout_cb; - bsc_schedule_timer(&lchan->error_timer, - msg->trx->bts->network->T3111 + 2, 0); - } - - /* BTS will respond by RF CHAN REL ACK */ - return abis_rsl_sendmsg(msg); -} - -static int rsl_rx_rf_chan_rel_ack(struct gsm_lchan *lchan) -{ - - DEBUGP(DRSL, "%s RF CHANNEL RELEASE ACK\n", gsm_lchan_name(lchan)); - - 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)); - bsc_del_timer(&lchan->T3111); - /* we have an error timer pending to release that */ - if (lchan->state != LCHAN_S_REL_ERR) - rsl_lchan_set_state(lchan, LCHAN_S_NONE); - lchan_free(lchan); - - return 0; -} - -int rsl_paging_cmd(struct gsm_bts *bts, u_int8_t paging_group, u_int8_t len, - u_int8_t *ms_ident, u_int8_t chan_needed) -{ - struct abis_rsl_dchan_hdr *dh; - struct msgb *msg = rsl_msgb_alloc(); - - dh = (struct abis_rsl_dchan_hdr *) msgb_put(msg, sizeof(*dh)); - init_dchan_hdr(dh, RSL_MT_PAGING_CMD); - dh->chan_nr = RSL_CHAN_PCH_AGCH; - - msgb_tv_put(msg, RSL_IE_PAGING_GROUP, paging_group); - msgb_tlv_put(msg, RSL_IE_MS_IDENTITY, len-2, ms_ident+2); - msgb_tv_put(msg, RSL_IE_CHAN_NEEDED, chan_needed); - - msg->trx = bts->c0; - - return abis_rsl_sendmsg(msg); -} - -int imsi_str2bcd(u_int8_t *bcd_out, const char *str_in) -{ - int i, len = strlen(str_in); - - for (i = 0; i < len; i++) { - int num = str_in[i] - 0x30; - if (num < 0 || num > 9) - return -1; - if (i % 2 == 0) - bcd_out[i/2] = num; - else - bcd_out[i/2] |= (num << 4); - } - - return 0; -} - -/* Chapter 8.5.6 */ -int rsl_imm_assign_cmd(struct gsm_bts *bts, u_int8_t len, u_int8_t *val) -{ - struct msgb *msg = rsl_msgb_alloc(); - struct abis_rsl_dchan_hdr *dh; - u_int8_t buf[MACBLOCK_SIZE]; - - dh = (struct abis_rsl_dchan_hdr *) msgb_put(msg, sizeof(*dh)); - init_dchan_hdr(dh, RSL_MT_IMMEDIATE_ASSIGN_CMD); - dh->chan_nr = RSL_CHAN_PCH_AGCH; - - switch (bts->type) { - case GSM_BTS_TYPE_BS11: - msgb_tlv_put(msg, RSL_IE_IMM_ASS_INFO, len, val); - break; - default: - /* If phase 2, construct a FULL_IMM_ASS_INFO */ - pad_macblock(buf, val, len); - msgb_tlv_put(msg, RSL_IE_FULL_IMM_ASS_INFO, MACBLOCK_SIZE, buf); - break; - } - - msg->trx = bts->c0; - - 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 = lchan2chan_nr(lchan); - msgb_tv_put(msg, RSL_IE_SIEMENS_MRPCI, *(u_int8_t *)mrpci); - - DEBUGP(DRSL, "%s TX Siemens MRPCI 0x%02x\n", - gsm_lchan_name(lchan), *(u_int8_t *)mrpci); - - msg->trx = lchan->ts->trx; - - 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, u_int8_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, lchan2chan_nr(msg->lchan), - link_id, 1); - - msg->trx = msg->lchan->ts->trx; - - 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, u_int8_t link_id) -{ - struct msgb *msg; - - msg = rsl_rll_simple(RSL_MT_EST_REQ, lchan2chan_nr(lchan), - link_id, 0); - msg->trx = lchan->ts->trx; - - return abis_rsl_sendmsg(msg); -} - -/* 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, u_int8_t link_id, u_int8_t reason) -{ - - struct msgb *msg; - - msg = rsl_rll_simple(RSL_MT_REL_REQ, lchan2chan_nr(lchan), - link_id, 0); - /* 0 is normal release, 1 is local end */ - msgb_tv_put(msg, RSL_IE_RELEASE_MODE, reason); - - /* FIXME: start some timer in case we don't receive a REL ACK ? */ - - msg->trx = lchan->ts->trx; - - return abis_rsl_sendmsg(msg); -} - -int rsl_lchan_set_state(struct gsm_lchan *lchan, int state) -{ - lchan->state = state; - return 0; -} - -/* Chapter 8.4.2: Channel Activate Acknowledge */ -static int rsl_rx_chan_act_ack(struct msgb *msg) -{ - struct abis_rsl_dchan_hdr *rslh = msgb_l2(msg); - - /* BTS has confirmed channel activation, we now need - * to assign the activated channel to the MS */ - if (rslh->ie_chan != RSL_IE_CHAN_NR) - return -EINVAL; - - if (msg->lchan->state != LCHAN_S_ACT_REQ) - LOGP(DRSL, LOGL_NOTICE, "%s CHAN ACT ACK, but state %s\n", - gsm_lchan_name(msg->lchan), - gsm_lchans_name(msg->lchan->state)); - rsl_lchan_set_state(msg->lchan, LCHAN_S_ACTIVE); - - if (msg->lchan->rqd_ref) { - rsl_send_imm_assignment(msg->lchan); - talloc_free(msg->lchan->rqd_ref); - msg->lchan->rqd_ref = NULL; - msg->lchan->rqd_ta = 0; - } - - send_lchan_signal(S_LCHAN_ACTIVATE_ACK, msg->lchan, NULL); - - return 0; -} - -/* Chapter 8.4.3: Channel Activate NACK */ -static int rsl_rx_chan_act_nack(struct msgb *msg) -{ - struct abis_rsl_dchan_hdr *dh = msgb_l2(msg); - struct tlv_parsed tp; - - 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 u_int8_t *cause = TLVP_VAL(&tp, RSL_IE_CAUSE); - print_rsl_cause(LOGL_ERROR, cause, - TLVP_LEN(&tp, RSL_IE_CAUSE)); - if (*cause != RSL_ERR_RCH_ALR_ACTV_ALLOC) - rsl_lchan_set_state(msg->lchan, LCHAN_S_NONE); - } else - rsl_lchan_set_state(msg->lchan, LCHAN_S_NONE); - - LOGPC(DRSL, LOGL_ERROR, "\n"); - - send_lchan_signal(S_LCHAN_ACTIVATE_NACK, msg->lchan, NULL); - - lchan_free(msg->lchan); - 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; - - /* FIXME: print which channel */ - LOGP(DRSL, LOGL_NOTICE, "%s CONNECTION FAIL: RELEASING ", - gsm_lchan_name(msg->lchan)); - - 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"); - /* FIXME: only free it after channel release ACK */ - counter_inc(msg->lchan->ts->trx->bts->network->stats.chan.rf_fail); - return rsl_rf_chan_release(msg->lchan, 1); -} - -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_meas_rep *mr) -{ - int i; - - DEBUGP(DMEAS, "MEASUREMENT RESULT NR=%d ", 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); - u_int8_t len; - const u_int8_t *val; - int rc; - - /* check if this channel is actually active */ - /* FIXME: maybe this check should be way more generic/centralized */ - if (msg->lchan->state != LCHAN_S_ACTIVE) { - LOGP(DRSL, LOGL_DEBUG, "%s: MEAS RES for inactive channel\n", - gsm_lchan_name(msg->lchan)); - return 0; - } - - memset(mr, 0, sizeof(*mr)); - mr->lchan = msg->lchan; - - rsl_tlv_parse(&tp, dh->data, msgb_l2len(msg)-sizeof(*dh)); - - if (!TLVP_PRESENT(&tp, RSL_IE_MEAS_RES_NR) || - !TLVP_PRESENT(&tp, RSL_IE_UPLINK_MEAS) || - !TLVP_PRESENT(&tp, RSL_IE_BS_POWER)) - return -EIO; - - /* Mandatory Parts */ - mr->nr = *TLVP_VAL(&tp, RSL_IE_MEAS_RES_NR); - - len = TLVP_LEN(&tp, RSL_IE_UPLINK_MEAS); - val = TLVP_VAL(&tp, RSL_IE_UPLINK_MEAS); - if (len >= 3) { - if (val[0] & 0x40) - mr->flags |= MEAS_REP_F_DL_DTX; - mr->ul.full.rx_lev = val[0] & 0x3f; - mr->ul.sub.rx_lev = val[1] & 0x3f; - mr->ul.full.rx_qual = val[2]>>3 & 0x7; - mr->ul.sub.rx_qual = val[2] & 0x7; - } - - mr->bs_power = *TLVP_VAL(&tp, RSL_IE_BS_POWER); - - /* Optional Parts */ - if (TLVP_PRESENT(&tp, RSL_IE_MS_TIMING_OFFSET)) - mr->ms_timing_offset = - *TLVP_VAL(&tp, RSL_IE_MS_TIMING_OFFSET); - - if (TLVP_PRESENT(&tp, RSL_IE_L1_INFO)) { - val = TLVP_VAL(&tp, RSL_IE_L1_INFO); - mr->flags |= MEAS_REP_F_MS_L1; - mr->ms_l1.pwr = ms_pwr_dbm(msg->trx->bts->band, val[0] >> 3); - if (val[0] & 0x04) - mr->flags |= MEAS_REP_F_FPC; - mr->ms_l1.ta = val[1]; - } - if (TLVP_PRESENT(&tp, RSL_IE_L3_INFO)) { - msg->l3h = (u_int8_t *) TLVP_VAL(&tp, RSL_IE_L3_INFO); - rc = gsm48_parse_meas_rep(mr, msg); - if (rc < 0) - return rc; - } - - print_meas_rep(mr); - - send_lchan_signal(S_LCHAN_MEAS_REP, msg->lchan, mr); - - return 0; -} - -/* Chapter 8.4.7 */ -static int rsl_rx_hando_det(struct msgb *msg) -{ - struct abis_rsl_dchan_hdr *dh = msgb_l2(msg); - struct tlv_parsed tp; - - DEBUGP(DRSL, "%s HANDOVER DETECT ", gsm_lchan_name(msg->lchan)); - - rsl_tlv_parse(&tp, dh->data, msgb_l2len(msg)-sizeof(*dh)); - - if (TLVP_PRESENT(&tp, RSL_IE_ACCESS_DELAY)) - DEBUGPC(DRSL, "access delay = %u\n", - *TLVP_VAL(&tp, RSL_IE_ACCESS_DELAY)); - else - DEBUGPC(DRSL, "\n"); - - send_lchan_signal(S_LCHAN_HANDOVER_DETECT, msg->lchan, NULL); - - return 0; -} - -static int abis_rsl_rx_dchan(struct msgb *msg) -{ - struct abis_rsl_dchan_hdr *rslh = msgb_l2(msg); - int rc = 0; - char *ts_name; - - msg->lchan = lchan_lookup(msg->trx, rslh->chan_nr); - ts_name = gsm_lchan_name(msg->lchan); - - switch (rslh->c.msg_type) { - case RSL_MT_CHAN_ACTIV_ACK: - DEBUGP(DRSL, "%s CHANNEL ACTIVATE ACK\n", ts_name); - rc = rsl_rx_chan_act_ack(msg); - break; - case RSL_MT_CHAN_ACTIV_NACK: - rc = rsl_rx_chan_act_nack(msg); - break; - case RSL_MT_CONN_FAIL: - rc = rsl_rx_conn_fail(msg); - break; - case RSL_MT_MEAS_RES: - rc = rsl_rx_meas_res(msg); - break; - case RSL_MT_HANDO_DET: - rc = rsl_rx_hando_det(msg); - break; - case RSL_MT_RF_CHAN_REL_ACK: - rc = rsl_rx_rf_chan_rel_ack(msg->lchan); - break; - case RSL_MT_MODE_MODIFY_ACK: - DEBUGP(DRSL, "%s CHANNEL MODE MODIFY ACK\n", ts_name); - break; - case RSL_MT_MODE_MODIFY_NACK: - LOGP(DRSL, LOGL_ERROR, "%s CHANNEL MODE MODIFY NACK\n", ts_name); - break; - case RSL_MT_IPAC_PDCH_ACT_ACK: - DEBUGPC(DRSL, "%s IPAC PDCH ACT ACK\n", ts_name); - msg->lchan->ts->flags |= TS_F_PDCH_MODE; - break; - case RSL_MT_IPAC_PDCH_ACT_NACK: - LOGP(DRSL, LOGL_ERROR, "%s IPAC PDCH ACT NACK\n", ts_name); - break; - case RSL_MT_IPAC_PDCH_DEACT_ACK: - DEBUGP(DRSL, "%s IPAC PDCH DEACT ACK\n", ts_name); - msg->lchan->ts->flags &= ~TS_F_PDCH_MODE; - break; - case RSL_MT_IPAC_PDCH_DEACT_NACK: - LOGP(DRSL, LOGL_ERROR, "%s IPAC PDCH DEACT NACK\n", ts_name); - break; - case RSL_MT_PHY_CONTEXT_CONF: - case RSL_MT_PREPROC_MEAS_RES: - case RSL_MT_TALKER_DET: - case RSL_MT_LISTENER_DET: - case RSL_MT_REMOTE_CODEC_CONF_REP: - case RSL_MT_MR_CODEC_MOD_ACK: - case RSL_MT_MR_CODEC_MOD_NACK: - case RSL_MT_MR_CODEC_MOD_PER: - LOGP(DRSL, LOGL_NOTICE, "%s Unimplemented Abis RSL DChan " - "msg 0x%02x\n", ts_name, rslh->c.msg_type); - break; - default: - LOGP(DRSL, LOGL_NOTICE, "%s unknown Abis RSL DChan msg 0x%02x\n", - ts_name, rslh->c.msg_type); - return -EINVAL; - } - - return rc; -} - -static int rsl_rx_error_rep(struct msgb *msg) -{ - struct abis_rsl_common_hdr *rslh = msgb_l2(msg); - struct tlv_parsed tp; - - LOGP(DRSL, LOGL_ERROR, "%s ERROR REPORT ", gsm_trx_name(msg->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); - 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(msg->trx)); - break; - case RSL_MT_OVERLOAD: - /* indicate CCCH / ACCH / processor overload */ - LOGP(DRSL, LOGL_ERROR, "%s CCCH/ACCH/CPU Overload\n", - gsm_trx_name(msg->trx)); - break; - default: - LOGP(DRSL, LOGL_NOTICE, "%s Unknown Abis RSL TRX message " - "type 0x%02x\n", gsm_trx_name(msg->trx), rslh->msg_type); - return -EINVAL; - } - return rc; -} - -/* If T3101 expires, we never received a response to IMMEDIATE ASSIGN */ -static void t3101_expired(void *data) -{ - struct gsm_lchan *lchan = data; - - rsl_rf_chan_release(lchan, 1); -} - -/* If T3111 expires, we will send the RF Channel Request */ -static void t3111_expired(void *data) -{ - struct gsm_lchan *lchan = data; - - rsl_rf_chan_release(lchan, 0); -} - -#define GSM48_LEN2PLEN(a) (((a) << 2) | 1) - -/* Format an IMM ASS REJ according to 04.08 Chapter 9.1.20 */ -static int rsl_send_imm_ass_rej(struct gsm_bts *bts, - unsigned int num_req_refs, - struct gsm48_req_ref *rqd_refs, - uint8_t wait_ind) -{ - uint8_t buf[GSM_MACBLOCK_LEN]; - struct gsm48_imm_ass_rej *iar = (struct gsm48_imm_ass_rej *)buf; - - /* create IMMEDIATE ASSIGN REJECT 04.08 message */ - memset(iar, 0, sizeof(*iar)); - iar->proto_discr = GSM48_PDISC_RR; - iar->msg_type = GSM48_MT_RR_IMM_ASS; - 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; - - return rsl_imm_assign_cmd(bts, sizeof(iar), (uint8_t *) iar); -} - -/* MS has requested a channel on the RACH */ -static int rsl_rx_chan_rqd(struct msgb *msg) -{ - struct gsm_bts *bts = msg->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; - u_int8_t rqd_ta; - int is_lu; - - u_int16_t arfcn; - u_int8_t ts_number, subch; - - /* parse request reference to be used in immediate assign */ - if (rqd_hdr->data[0] != RSL_IE_REQ_REFERENCE) - return -EINVAL; - - rqd_ref = (struct gsm48_req_ref *) &rqd_hdr->data[1]; - - /* parse access delay and use as TA */ - if (rqd_hdr->data[sizeof(struct gsm48_req_ref)+1] != RSL_IE_ACCESS_DELAY) - return -EINVAL; - rqd_ta = rqd_hdr->data[sizeof(struct gsm48_req_ref)+2]; - - /* determine channel type (SDCCH/TCH_F/TCH_H) based on - * request reference RA */ - lctype = get_ctype_by_chreq(bts->network, rqd_ref->ra); - chreq_reason = get_reason_by_chreq(rqd_ref->ra, bts->network->neci); - - counter_inc(bts->network->stats.chreq.total); - - /* - * We want LOCATION UPDATES to succeed and will assign a TCH - * if we have no SDCCH available. - */ - is_lu = !!(chreq_reason == GSM_CHREQ_REASON_LOCATION_UPD); - - /* check availability / allocate channel */ - lchan = lchan_alloc(bts, lctype, is_lu); - if (!lchan) { - LOGP(DRSL, LOGL_NOTICE, "BTS %d CHAN RQD: no resources for %s 0x%x\n", - msg->lchan->ts->trx->bts->nr, gsm_lchant_name(lctype), rqd_ref->ra); - counter_inc(bts->network->stats.chreq.no_channel); - /* FIXME gather multiple CHAN RQD and reject up to 4 at the same time */ - if (bts->network->T3122) - rsl_send_imm_ass_rej(bts, 1, rqd_ref, bts->network->T3122 & 0xff); - return -ENOMEM; - } - - if (lchan->state != LCHAN_S_NONE) - LOGP(DRSL, LOGL_NOTICE, "%s lchan_alloc() returned channel " - "in state %s\n", gsm_lchan_name(lchan), - gsm_lchans_name(lchan->state)); - rsl_lchan_set_state(lchan, LCHAN_S_ACT_REQ); - - /* 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; - - ts_number = lchan->ts->nr; - 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; - - /* FIXME: Start another timer or assume the BTS sends a ACK/NACK? */ - rsl_chan_activate_lchan(lchan, 0x00, rqd_ta, 0); - - DEBUGP(DRSL, "%s Activating ARFCN(%u) SS(%u) lctype %s " - "r=%s ra=0x%02x\n", gsm_lchan_name(lchan), arfcn, subch, - gsm_lchant_name(lchan->type), gsm_chreq_name(chreq_reason), - rqd_ref->ra); - return 0; -} - -static int rsl_send_imm_assignment(struct gsm_lchan *lchan) -{ - struct gsm_bts *bts = lchan->ts->trx->bts; - u_int8_t buf[GSM_MACBLOCK_LEN]; - struct gsm48_imm_ass *ia = (struct gsm48_imm_ass *) buf; - - /* create IMMEDIATE ASSIGN 04.08 messge */ - memset(ia, 0, sizeof(*ia)); - /* we set ia->l2_plen once we know the length of the MA below */ - ia->proto_discr = GSM48_PDISC_RR; - ia->msg_type = GSM48_MT_RR_IMM_ASS; - ia->page_mode = GSM48_PM_SAME; - gsm48_lchan2chan_desc(&ia->chan_desc, lchan); - - /* use request reference extracted from CHAN_RQD */ - memcpy(&ia->req_ref, lchan->rqd_ref, sizeof(ia->req_ref)); - ia->timing_advance = lchan->rqd_ta; - if (!lchan->ts->hopping.enabled) { - ia->mob_alloc_len = 0; - } else { - ia->mob_alloc_len = lchan->ts->hopping.ma_len; - memcpy(ia->mob_alloc, lchan->ts->hopping.ma_data, ia->mob_alloc_len); - } - /* we need to subtract 1 byte from sizeof(*ia) since ia includes the l2_plen field */ - ia->l2_plen = GSM48_LEN2PLEN((sizeof(*ia)-1) + ia->mob_alloc_len); - - /* Start timer T3101 to wait for GSM48_MT_RR_PAG_RESP */ - lchan->T3101.cb = t3101_expired; - lchan->T3101.data = lchan; - bsc_schedule_timer(&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, (u_int8_t *) ia); -} - -/* MS has requested a channel on the RACH */ -static int rsl_rx_ccch_load(struct msgb *msg) -{ - struct abis_rsl_dchan_hdr *rslh = msgb_l2(msg); - u_int16_t pg_buf_space; - u_int16_t rach_slot_count = -1; - u_int16_t rach_busy_count = -1; - u_int16_t rach_access_count = -1; - - switch (rslh->data[0]) { - case RSL_IE_PAGING_LOAD: - pg_buf_space = rslh->data[1] << 8 | rslh->data[2]; - if (is_ipaccess_bts(msg->trx->bts) && pg_buf_space == 0xffff) { - /* paging load below configured threshold, use 50 as default */ - pg_buf_space = 50; - } - paging_update_buffer_space(msg->trx->bts, pg_buf_space); - break; - case RSL_IE_RACH_LOAD: - if (msg->data_len >= 7) { - rach_slot_count = rslh->data[2] << 8 | rslh->data[3]; - rach_busy_count = rslh->data[4] << 8 | rslh->data[5]; - rach_access_count = rslh->data[6] << 8 | rslh->data[7]; - } - break; - default: - break; - } - - return 0; -} - -static int abis_rsl_rx_cchan(struct msgb *msg) -{ - struct abis_rsl_dchan_hdr *rslh = msgb_l2(msg); - int rc = 0; - - msg->lchan = lchan_lookup(msg->trx, rslh->chan_nr); - - switch (rslh->c.msg_type) { - case RSL_MT_CHAN_RQD: - /* MS has requested a channel on the RACH */ - rc = rsl_rx_chan_rqd(msg); - break; - case RSL_MT_CCCH_LOAD_IND: - /* current load on the CCCH */ - rc = rsl_rx_ccch_load(msg); - break; - case RSL_MT_DELETE_IND: - /* CCCH overloaded, IMM_ASSIGN was dropped */ - case RSL_MT_CBCH_LOAD_IND: - /* current load on the CBCH */ - LOGP(DRSL, LOGL_NOTICE, "Unimplemented Abis RSL TRX message " - "type 0x%02x\n", rslh->c.msg_type); - break; - default: - LOGP(DRSL, LOGL_NOTICE, "Unknown Abis RSL TRX message type " - "0x%02x\n", rslh->c.msg_type); - return -EINVAL; - } - - return rc; -} - -static int rsl_rx_rll_err_ind(struct msgb *msg) -{ - struct abis_rsl_rll_hdr *rllh = msgb_l2(msg); - u_int8_t *rlm_cause = rllh->data; - - LOGP(DRLL, LOGL_ERROR, "%s ERROR INDICATION cause=%s\n", - gsm_lchan_name(msg->lchan), - rsl_rlm_cause_name(rlm_cause[1])); - - rll_indication(msg->lchan, rllh->link_id, BSC_RLLR_IND_ERR_IND); - - if (rlm_cause[1] == RLL_CAUSE_T200_EXPIRED) { - counter_inc(msg->lchan->ts->trx->bts->network->stats.chan.rll_err); - return rsl_rf_chan_release(msg->lchan, 1); - } - - return 0; -} - -static void rsl_handle_release(struct gsm_lchan *lchan) -{ - int sapi; - struct gsm_bts *bts; - - /* maybe we have only brought down one RLL */ - 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; - } - - - - /* wait a bit to send the RF Channel Release */ - lchan->T3111.cb = t3111_expired; - lchan->T3111.data = lchan; - bts = lchan->ts->trx->bts; - bsc_schedule_timer(&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 abis_rsl_rll_hdr *rllh = msgb_l2(msg); - int rc = 0; - char *ts_name; - u_int8_t sapi = rllh->link_id & 7; - - msg->lchan = lchan_lookup(msg->trx, rllh->chan_nr); - ts_name = gsm_lchan_name(msg->lchan); - DEBUGP(DRLL, "%s SAPI=%u ", ts_name, sapi); - - switch (rllh->c.msg_type) { - case RSL_MT_DATA_IND: - DEBUGPC(DRLL, "DATA INDICATION\n"); - if (msgb_l2len(msg) > - sizeof(struct abis_rsl_common_hdr) + sizeof(*rllh) && - rllh->data[0] == RSL_IE_L3_INFO) { - msg->l3h = &rllh->data[3]; - return gsm0408_rcvmsg(msg, rllh->link_id); - } - break; - case RSL_MT_EST_IND: - DEBUGPC(DRLL, "ESTABLISH INDICATION\n"); - /* lchan is established, stop T3101 */ - msg->lchan->sapis[rllh->link_id & 0x7] = LCHAN_SAPI_MS; - bsc_del_timer(&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); - rsl_lchan_rll_release(msg->lchan, rllh->link_id); - 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); - rsl_lchan_rll_release(msg->lchan, rllh->link_id); - break; - case RSL_MT_ERROR_IND: - rc = rsl_rx_rll_err_ind(msg); - break; - case RSL_MT_UNIT_DATA_IND: - LOGP(DRLL, LOGL_NOTICE, "unimplemented Abis RLL message " - "type 0x%02x\n", rllh->c.msg_type); - break; - default: - LOGP(DRLL, LOGL_NOTICE, "unknown Abis RLL message " - "type 0x%02x\n", rllh->c.msg_type); - } - return rc; -} - -static u_int8_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; - } - case GSM48_CMODE_SPEECH_EFR: - switch (lchan->type) { - case GSM_LCHAN_TCH_F: - return 0x01; - /* there's no half-rate EFR */ - default: - 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; - } - default: - break; - } - LOGP(DRSL, LOGL_ERROR, "Cannot determine ip.access speech mode for " - "tch_mode == 0x%02x\n", lchan->tch_mode); - return 0; -} - -static u_int8_t ipa_rtp_pt_for_lchan(struct gsm_lchan *lchan) -{ - struct gsm_network *net = lchan->ts->trx->bts->network; - - /* allow to hardcode the rtp payload */ - if (net->hardcoded_rtp_payload != 0) - return net->hardcoded_rtp_payload; - - 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; - } - 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; - } - case GSM48_CMODE_SPEECH_AMR: - switch (lchan->type) { - case GSM_LCHAN_TCH_F: - return RTP_PT_AMR_FULL; - case GSM_LCHAN_TCH_H: - return RTP_PT_AMR_HALF; - default: - 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; - u_int16_t port, conn_id; - - if (TLVP_PRESENT(tv, RSL_IE_IPAC_LOCAL_IP)) { - ip.s_addr = *((u_int32_t *) TLVP_VAL(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 = *((u_int16_t *) TLVP_VAL(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 = *((u_int16_t *) TLVP_VAL(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 = *((u_int32_t *) TLVP_VAL(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 = *((u_int16_t *) TLVP_VAL(tv, RSL_IE_IPAC_REMOTE_PORT)); - port = ntohs(port); - DEBUGPC(DRSL, "REMOTE_PORT=%u ", port); - lchan->abis_ip.connect_port = port; - } -} - -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 = 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->trx = lchan->ts->trx; - - return abis_rsl_sendmsg(msg); -} - -int rsl_ipacc_mdcx(struct gsm_lchan *lchan, u_int32_t ip, u_int16_t port, - u_int8_t rtp_payload2) -{ - struct msgb *msg = rsl_msgb_alloc(); - struct abis_rsl_dchan_hdr *dh; - u_int32_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 = 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 = (u_int32_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->trx = lchan->ts->trx; - - 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; - u_int8_t msg_type; - - if (act) - msg_type = RSL_MT_IPAC_PDCH_ACT; - else - msg_type = RSL_MT_IPAC_PDCH_DEACT; - - dh = (struct abis_rsl_dchan_hdr *) msgb_put(msg, sizeof(*dh)); - init_dchan_hdr(dh, msg_type); - dh->c.msg_discr = ABIS_RSL_MDISC_DED_CHAN; - dh->chan_nr = ts2chan_nr(ts, 0); - - DEBUGP(DRSL, "%s IPAC_PDCH_%sACT\n", gsm_ts_name(ts), - act ? "" : "DE"); - - msg->trx = ts->trx; - - 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); - - dispatch_signal(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); - dispatch_signal(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)); - - dispatch_signal(SS_ABISIP, S_ABISIP_DLCX_IND, msg->lchan); - - return 0; -} - -static int abis_rsl_rx_ipacc(struct msgb *msg) -{ - struct abis_rsl_rll_hdr *rllh = msgb_l2(msg); - char *ts_name; - int rc = 0; - - msg->lchan = lchan_lookup(msg->trx, rllh->chan_nr); - ts_name = gsm_lchan_name(msg->lchan); - - switch (rllh->c.msg_type) { - case RSL_MT_IPAC_CRCX_ACK: - DEBUGP(DRSL, "%s IPAC_CRCX_ACK ", ts_name); - rc = abis_rsl_rx_ipacc_crcx_ack(msg); - break; - case RSL_MT_IPAC_CRCX_NACK: - /* somehow the BTS was unable to bind the lchan to its local - * port?!? */ - LOGP(DRSL, LOGL_ERROR, "%s IPAC_CRCX_NACK\n", ts_name); - break; - case RSL_MT_IPAC_MDCX_ACK: - /* the BTS tells us that a connect operation was successful */ - DEBUGP(DRSL, "%s IPAC_MDCX_ACK ", ts_name); - rc = abis_rsl_rx_ipacc_mdcx_ack(msg); - break; - case RSL_MT_IPAC_MDCX_NACK: - /* somehow the BTS was unable to connect the lchan to a remote - * port */ - LOGP(DRSL, LOGL_ERROR, "%s IPAC_MDCX_NACK\n", ts_name); - break; - case RSL_MT_IPAC_DLCX_IND: - DEBUGP(DRSL, "%s IPAC_DLCX_IND ", ts_name); - rc = abis_rsl_rx_ipacc_dlcx_ind(msg); - break; - default: - LOGP(DRSL, LOGL_NOTICE, "Unknown ip.access msg_type 0x%02x\n", - rllh->c.msg_type); - break; - } - DEBUGPC(DRSL, "\n"); - - return rc; -} - - -/* Entry-point where L2 RSL from BTS enters */ -int abis_rsl_rcvmsg(struct msgb *msg) -{ - struct abis_rsl_common_hdr *rslh; - int rc = 0; - - if (!msg) { - DEBUGP(DRSL, "Empty RSL msg?..\n"); - return -1; - } - - if (msgb_l2len(msg) < sizeof(*rslh)) { - DEBUGP(DRSL, "Truncated RSL message with l2len: %u\n", msgb_l2len(msg)); - 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); - return -EINVAL; - } - msgb_free(msg); - return rc; -} - -/* From Table 10.5.33 of GSM 04.08 */ -int rsl_number_of_paging_subchannels(struct gsm_bts *bts) -{ - if (bts->si_common.chan_desc.ccch_conf == RSL_BCCH_CCCH_CONF_1_C) { - return MAX(1, (3 - bts->si_common.chan_desc.bs_ag_blks_res)) - * (bts->si_common.chan_desc.bs_pa_mfrms + 2); - } else { - return (9 - bts->si_common.chan_desc.bs_ag_blks_res) - * (bts->si_common.chan_desc.bs_pa_mfrms + 2); - } -} - -int rsl_sms_cb_command(struct gsm_bts *bts, uint8_t chan_number, - uint8_t 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->chan_nr = RSL_CHAN_SDCCH4_ACCH; /* TODO: check the chan config */ - - msgb_tv_put(cb_cmd, RSL_IE_CB_CMD_TYPE, cb_command); - msgb_tlv_put(cb_cmd, RSL_IE_SMSCB_MSG, len, data); - - cb_cmd->trx = bts->c0; - - return abis_rsl_sendmsg(cb_cmd); -} diff --git a/openbsc/src/bsc/bsc_api.c b/openbsc/src/bsc/bsc_api.c deleted file mode 100644 index 0f09aecd3..000000000 --- a/openbsc/src/bsc/bsc_api.c +++ /dev/null @@ -1,675 +0,0 @@ -/* GSM 08.08 like API for OpenBSC. The bridge from MSC to BSC */ - -/* (C) 2010 by Holger Hans Peter Freyther - * (C) 2010 by On-Waves - * (C) 2009 by Harald Welte - * - * All Rights Reserved - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation; either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - * - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -#include - -#define GSM0808_T10_VALUE 6, 0 - -static LLIST_HEAD(sub_connections); - -static void rll_ind_cb(struct gsm_lchan *, uint8_t, void *, enum bsc_rllr_ind); -static void send_sapi_reject(struct gsm_subscriber_connection *conn, int link_id); -static void handle_release(struct gsm_subscriber_connection *conn, struct bsc_api *bsc, struct gsm_lchan *lchan); -static void handle_chan_ack(struct gsm_subscriber_connection *conn, struct bsc_api *bsc, struct gsm_lchan *lchan); -static void handle_chan_nack(struct gsm_subscriber_connection *conn, struct bsc_api *bsc, struct gsm_lchan *lchan); - -/* GSM 08.08 3.2.2.33 */ -static u_int8_t lchan_to_chosen_channel(struct gsm_lchan *lchan) -{ - u_int8_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: - LOGP(DMSC, LOGL_ERROR, "Unknown lchan type: %p\n", lchan); - break; - } - - return channel_mode << 4 | channel; -} - -static u_int8_t chan_mode_to_speech(struct gsm_lchan *lchan) -{ - int mode = 0; - - switch (lchan->tch_mode) { - case GSM48_CMODE_SPEECH_V1: - mode = 1; - break; - case GSM48_CMODE_SPEECH_EFR: - mode = 0x11; - break; - case GSM48_CMODE_SPEECH_AMR: - mode = 0x21; - break; - case GSM48_CMODE_SIGN: - case GSM48_CMODE_DATA_14k5: - case GSM48_CMODE_DATA_12k0: - case GSM48_CMODE_DATA_6k0: - case GSM48_CMODE_DATA_3k6: - default: - LOGP(DMSC, LOGL_ERROR, "Using non speech mode: %d\n", mode); - return 0; - break; - } - - /* assume to always do AMR HR on any TCH type */ - if (lchan->type == GSM_LCHAN_TCH_H || - lchan->tch_mode == GSM48_CMODE_SPEECH_AMR) - mode |= 0x4; - - return mode; -} - -static void assignment_t10_timeout(void *_conn) -{ - struct bsc_api *api; - struct gsm_subscriber_connection *conn = - (struct gsm_subscriber_connection *) _conn; - - LOGP(DMSC, LOGL_ERROR, "Assigment T10 timeout on %p\n", conn); - - /* normal release on the secondary channel */ - lchan_release(conn->secondary_lchan, 0, 1); - conn->secondary_lchan = NULL; - - /* inform them about the failure */ - api = conn->bts->network->bsc_api; - api->assign_fail(conn, GSM0808_CAUSE_NO_RADIO_RESOURCE_AVAILABLE, NULL); -} - -/* - * 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; - - /* 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) { - new_lchan->mr_conf.ver = 1; - new_lchan->mr_conf.icmi = 1; - new_lchan->mr_conf.m5_90 = 1; - } - - if (rsl_chan_activate_lchan(new_lchan, 0x1, 0, 0) < 0) { - LOGP(DHO, LOGL_ERROR, "could not activate channel\n"); - lchan_free(new_lchan); - return -1; - } - - /* remember that we have the channel */ - conn->secondary_lchan = new_lchan; - new_lchan->conn = conn; - - rsl_lchan_set_state(new_lchan, LCHAN_S_ACT_REQ); - return 0; -} - -struct gsm_subscriber_connection *subscr_con_allocate(struct gsm_lchan *lchan) -{ - struct gsm_subscriber_connection *conn; - - conn = talloc_zero(lchan->ts->trx->bts->network, struct gsm_subscriber_connection); - if (!conn) - return NULL; - - /* Configure the time and start it so it will be closed */ - conn->lchan = lchan; - conn->bts = lchan->ts->trx->bts; - lchan->conn = conn; - llist_add_tail(&conn->entry, &sub_connections); - return conn; -} - -/* TODO: move subscriber put here... */ -void subscr_con_free(struct gsm_subscriber_connection *conn) -{ - if (!conn) - return; - - - if (conn->subscr) { - subscr_put(conn->subscr); - conn->subscr = NULL; - } - - - if (conn->ho_lchan) { - LOGP(DNM, LOGL_ERROR, "The ho_lchan should have been cleared.\n"); - conn->ho_lchan->conn = NULL; - } - - if (conn->lchan) { - LOGP(DNM, LOGL_ERROR, "The lchan should have been cleared.\n"); - conn->lchan->conn = NULL; - } - - if (conn->secondary_lchan) { - LOGP(DNM, LOGL_ERROR, "The secondary_lchan should have been cleared.\n"); - conn->secondary_lchan->conn = NULL; - } - - llist_del(&conn->entry); - talloc_free(conn); -} - -int bsc_api_init(struct gsm_network *network, struct bsc_api *api) -{ - network->bsc_api = api; - return 0; -} - -int gsm0808_submit_dtap(struct gsm_subscriber_connection *conn, - struct msgb *msg, int link_id, int allow_sach) -{ - 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->trx = msg->lchan->ts->trx; - - /* If we are on a TCH and need to submit a SMS (on SAPI=3) we need to use the SACH */ - if (allow_sach && sapi != 0) { - if (conn->lchan->type == GSM_LCHAN_TCH_F || conn->lchan->type == GSM_LCHAN_TCH_H) - link_id |= 0x40; - } - - msg->l3h = msg->data; - if (conn->lchan->sapis[sapi] == LCHAN_SAPI_UNUSED) { - 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 { - return rsl_data_request(msg, link_id); - } -} - -/** - * 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 and that when we have a TCH it is capable of - * handling the audio codec. On top of that it is assumed that we are using - * AMR 5.9 when assigning a TCH/H. - */ -int gsm0808_assign_req(struct gsm_subscriber_connection *conn, int chan_mode, int full_rate) -{ - struct bsc_api *api; - api = conn->bts->network->bsc_api; - - if (conn->lchan->type == GSM_LCHAN_SDCCH) { - if (handle_new_assignment(conn, chan_mode, full_rate) != 0) - goto error; - } else { - LOGP(DMSC, LOGL_NOTICE, - "Sending ChanModify for speech %d %d\n", chan_mode, full_rate); - if (chan_mode == GSM48_CMODE_SPEECH_AMR) { - conn->lchan->mr_conf.ver = 1; - conn->lchan->mr_conf.icmi = 1; - conn->lchan->mr_conf.m5_90 = 1; - } - - gsm48_lchan_modify(conn->lchan, chan_mode); - } - - /* we will now start the timer to complete the assignment */ - conn->T10.cb = assignment_t10_timeout; - conn->T10.data = conn; - bsc_schedule_timer(&conn->T10, GSM0808_T10_VALUE); - return 0; - -error: - api->assign_fail(conn, 0, NULL); - return -1; -} - -int gsm0808_page(struct gsm_bts *bts, unsigned int page_group, unsigned int mi_len, - uint8_t *mi, int chan_type) -{ - return rsl_paging_cmd(bts, page_group, mi_len, mi, chan_type); -} - -static void handle_ass_compl(struct gsm_subscriber_connection *conn, - struct msgb *msg) -{ - struct gsm48_hdr *gh; - struct bsc_api *api = conn->bts->network->bsc_api; - - if (conn->secondary_lchan != msg->lchan) { - LOGP(DMSC, LOGL_ERROR, "Assignment Compl should occur on second lchan.\n"); - return; - } - - gh = msgb_l3(msg); - if (msgb_l3len(msg) - sizeof(*gh) != 1) { - LOGP(DMSC, LOGL_ERROR, "Assignment Compl invalid: %lu\n", - msgb_l3len(msg) - sizeof(*gh)); - return; - } - - /* swap channels */ - bsc_del_timer(&conn->T10); - - lchan_release(conn->lchan, 0, 1); - conn->lchan = conn->secondary_lchan; - conn->secondary_lchan = NULL; - - if (is_ipaccess_bts(conn->bts) && conn->lchan->tch_mode != GSM48_CMODE_SIGN) - rsl_ipacc_crcx(conn->lchan); - - api->assign_compl(conn, gh->data[0], - lchan_to_chosen_channel(conn->lchan), - conn->lchan->encr.alg_id, - chan_mode_to_speech(conn->lchan)); -} - -static void handle_ass_fail(struct gsm_subscriber_connection *conn, - struct msgb *msg) -{ - struct bsc_api *api = conn->bts->network->bsc_api; - uint8_t *rr_failure; - struct gsm48_hdr *gh; - - - if (conn->lchan != msg->lchan) { - LOGP(DMSC, LOGL_ERROR, "Assignment failure should occur on primary lchan.\n"); - return; - } - - /* stop the timer and release it */ - bsc_del_timer(&conn->T10); - lchan_release(conn->secondary_lchan, 0, 1); - conn->secondary_lchan = NULL; - - gh = msgb_l3(msg); - if (msgb_l3len(msg) - sizeof(*gh) != 1) { - LOGP(DMSC, LOGL_ERROR, "assignemnt failure unhandled: %lu\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 dispatch_dtap(struct gsm_subscriber_connection *conn, - uint8_t link_id, struct msgb *msg) -{ - struct bsc_api *api = msg->lchan->ts->trx->bts->network->bsc_api; - struct gsm48_hdr *gh; - uint8_t pdisc; - int rc; - - if (msgb_l3len(msg) < sizeof(*gh)) { - LOGP(DMSC, LOGL_ERROR, "Message too short for a GSM48 header.\n"); - return; - } - - gh = msgb_l3(msg); - pdisc = gh->proto_discr & 0x0f; - switch (pdisc) { - case GSM48_PDISC_RR: - switch (gh->msg_type) { - case GSM48_MT_RR_CIPH_M_COMPL: - if (api->cipher_mode_compl) - return 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: - bsc_del_timer(&conn->T10); - rc = gsm48_rx_rr_modif_ack(msg); - if (rc < 0 && api->assign_fail) { - api->assign_fail(conn, - GSM0808_CAUSE_NO_RADIO_RESOURCE_AVAILABLE, - NULL); - } else if (rc >= 0 && api->assign_compl) - api->assign_compl(conn, 0, - lchan_to_chosen_channel(conn->lchan), - conn->lchan->encr.alg_id, - chan_mode_to_speech(conn->lchan)); - return; - break; - } - break; - case GSM48_PDISC_MM: - break; - } - - /* default case */ - if (api->dtap) - api->dtap(conn, link_id, msg); -} - -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) { - dispatch_dtap(lchan->conn, link_id, msg); - } else { - rc = BSC_API_CONN_POL_REJECT; - lchan->conn = subscr_con_allocate(msg->lchan); - if (!lchan->conn) { - lchan_release(lchan, 0, 0); - return -1; - } - - rc = api->compl_l3(lchan->conn, msg, 0); - - if (rc != BSC_API_CONN_POL_ACCEPT) { - lchan->conn->lchan = NULL; - subscr_con_free(lchan->conn); - lchan_release(lchan, 0, 0); - } - } - - return 0; -} - -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, 1); - - if (conn->lchan) - lchan_release(conn->lchan, 1, 0); - - conn->lchan = NULL; - conn->secondary_lchan = NULL; - conn->ho_lchan = NULL; - conn->bts = NULL; - - bsc_del_timer(&conn->T10); - - return 0; -} - -static void send_sapi_reject(struct gsm_subscriber_connection *conn, int link_id) -{ - struct bsc_api *api; - - if (!conn) - return; - - api = conn->bts->network->bsc_api; - if (!api || !api->sapi_n_reject) - return; - - api->sapi_n_reject(conn, link_id); -} - -static void rll_ind_cb(struct gsm_lchan *lchan, uint8_t link_id, void *_data, enum bsc_rllr_ind rllr_ind) -{ - struct msgb *msg = _data; - - /* - * There seems to be a small window that the RLL timer can - * fire after a lchan_release call and before the S_CHALLOC_FREED - * is called. Check if a conn is set before proceeding. - */ - if (!lchan->conn) - return; - - switch (rllr_ind) { - case BSC_RLLR_IND_EST_CONF: - rsl_data_request(msg, OBSC_LINKID_CB(msg)); - break; - case BSC_RLLR_IND_REL_IND: - case BSC_RLLR_IND_ERR_IND: - case BSC_RLLR_IND_TIMEOUT: - send_sapi_reject(lchan->conn, OBSC_LINKID_CB(msg)); - msgb_free(msg); - break; - } -} - -static int bsc_handle_lchan_signal(unsigned int subsys, unsigned int signal, - void *handler_data, void *signal_data) -{ - struct bsc_api *bsc; - struct gsm_lchan *lchan; - struct lchan_signal_data *lchan_data; - - if (subsys != SS_LCHAN) - return 0; - - - lchan_data = signal_data; - if (!lchan_data->lchan || !lchan_data->lchan->conn) - return 0; - - lchan = lchan_data->lchan; - bsc = lchan->ts->trx->bts->network->bsc_api; - if (!bsc) - return 0; - - switch (signal) { - case S_LCHAN_UNEXPECTED_RELEASE: - handle_release(lchan->conn, bsc, lchan); - break; - case S_LCHAN_ACTIVATE_ACK: - handle_chan_ack(lchan->conn, bsc, lchan); - break; - case S_LCHAN_ACTIVATE_NACK: - handle_chan_nack(lchan->conn, bsc, lchan); - break; - } - - return 0; -} - -static void handle_release(struct gsm_subscriber_connection *conn, - struct bsc_api *bsc, struct gsm_lchan *lchan) -{ - int destruct = 1; - - if (conn->secondary_lchan == lchan) { - bsc_del_timer(&conn->T10); - conn->secondary_lchan = NULL; - - bsc->assign_fail(conn, - GSM0808_CAUSE_RADIO_INTERFACE_FAILURE, - NULL); - } - - /* clear the connection now */ - if (bsc->clear_request) - destruct = bsc->clear_request(conn, 0); - - /* now give up all channels */ - if (conn->lchan == lchan) - conn->lchan = NULL; - if (conn->ho_lchan == lchan) { - bsc_clear_handover(conn, 0); - conn->ho_lchan = NULL; - } - lchan->conn = NULL; - - gsm0808_clear(conn); - - if (destruct) - subscr_con_free(conn); -} - -static void handle_chan_ack(struct gsm_subscriber_connection *conn, - struct bsc_api *api, struct gsm_lchan *lchan) -{ - if (conn->secondary_lchan != lchan) - return; - - LOGP(DMSC, LOGL_NOTICE, "Sending assignment on chan: %p\n", lchan); - gsm48_send_rr_ass_cmd(conn->lchan, lchan, 0x3); -} - -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) -{ - register_signal_handler(SS_LCHAN, bsc_handle_lchan_signal, NULL); -} diff --git a/openbsc/src/bsc/bsc_init.c b/openbsc/src/bsc/bsc_init.c deleted file mode 100644 index 752056c37..000000000 --- a/openbsc/src/bsc/bsc_init.c +++ /dev/null @@ -1,440 +0,0 @@ -/* A hackish minimal BSC (+MSC +HLR) implementation */ - -/* (C) 2008-2010 by Harald Welte - * (C) 2009 by Holger Hans Peter Freyther - * All Rights Reserved - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation; either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - * - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -/* global pointer to the gsm network data structure */ -extern struct gsm_network *bsc_gsmnet; - -static void patch_nm_tables(struct gsm_bts *bts); - -/* Callback function for NACK on the OML NM */ -static int oml_msg_nack(struct nm_nack_signal_data *nack) -{ - int i; - - if (nack->mt == NM_MT_SET_BTS_ATTR_NACK) { - - LOGP(DNM, LOGL_FATAL, "Failed to set BTS attributes. That is fatal. " - "Was the bts type and frequency properly specified?\n"); - exit(-1); - } else { - LOGP(DNM, LOGL_ERROR, "Got a NACK going to drop the OML links.\n"); - for (i = 0; i < bsc_gsmnet->num_bts; ++i) { - struct gsm_bts *bts = gsm_bts_num(bsc_gsmnet, i); - if (is_ipaccess_bts(bts)) - ipaccess_drop_oml(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); - dispatch_signal(SS_GLOBAL, S_GLOBAL_BTS_CLOSE_OM, bts); - } - - return 0; -} - -static int generate_and_rsl_si(struct gsm_bts_trx *trx, enum osmo_sysinfo_type i) -{ - struct gsm_bts *bts = trx->bts; - int rc; - - /* Only generate SI if this SI is not in "static" (user-defined) mode */ - if (!(bts->si_mode_static & (1 << i))) { - rc = gsm_generate_si(bts, i); - if (rc < 0) - return rc; - } - - DEBUGP(DRR, "SI%s: %s\n", gsm_sitype_name(i), - 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, gsm_sitype2rsl(i), - GSM_BTS_SI(bts, i), rc); - break; - default: - rc = rsl_bcch_info(trx, gsm_sitype2rsl(i), - GSM_BTS_SI(bts, i), rc); - break; - } - - return rc; -} - -/* set all system information types */ -static int set_system_infos(struct gsm_bts_trx *trx) -{ - int i, rc; - struct gsm_bts *bts = trx->bts; - - bts->si_common.cell_sel_par.ms_txpwr_max_ccch = - ms_pwr_ctl_lvl(bts->band, bts->ms_max_power); - bts->si_common.cell_sel_par.neci = bts->network->neci; - - /* First, we determine which of the SI messages we actually need */ - - if (trx == bts->c0) { - /* 1...4 are always present on a C0 TRX */ - for (i = SYSINFO_TYPE_1; i <= SYSINFO_TYPE_4; i++) - bts->si_valid |= (1 << i); - - /* 13 is always present on a C0 TRX of a GPRS BTS */ - if (bts->gprs.mode != BTS_GPRS_NONE) - bts->si_valid |= (1 << SYSINFO_TYPE_13); - } - - /* 5 and 6 are always present on every TRX */ - bts->si_valid |= (1 << SYSINFO_TYPE_5); - bts->si_valid |= (1 << SYSINFO_TYPE_6); - - /* Second, we generate and send the selected SI via RSL */ - for (i = SYSINFO_TYPE_1; i < _MAX_SYSINFO_TYPE; i++) { - if (!(bts->si_valid & (1 << i))) - continue; - - rc = generate_and_rsl_si(trx, i); - if (rc < 0) - goto err_out; - } - - return 0; -err_out: - LOGP(DRR, LOGL_ERROR, "Cannot generate SI %u for BTS %u, most likely " - "a problem with neighbor cell list generation\n", - i, bts->nr); - return rc; -} - -/* 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 = 1; 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 = 1; i < 1024; i++) { - if (!bitvec_get_bit_pos(cell_chan, i)) - continue; - /* set the corresponding bit in the MA */ - bitnum = (ts->hopping.ma_len * 8) - 1 - n_chan; - if (bitvec_get_bit_pos(ts_arfcn, i)) - bitvec_set_bit_pos(ma, bitnum, 1); - else - bitvec_set_bit_pos(ma, bitnum, 0); - n_chan++; - } - - /* ARFCN 0 is special: It is coded last in the bitmask */ - if (bitvec_get_bit_pos(cell_chan, 0)) { - n_chan++; - /* set the corresponding bit in the MA */ - bitnum = (ts->hopping.ma_len * 8) - 1 - n_chan; - if (bitvec_get_bit_pos(ts_arfcn, 0)) - bitvec_set_bit_pos(ma, bitnum, 1); - else - bitvec_set_bit_pos(ma, bitnum, 0); - } - - return 0; -} - -static void bootstrap_rsl(struct gsm_bts_trx *trx) -{ - unsigned int i; - - LOGP(DRSL, LOGL_NOTICE, "bootstrapping RSL for BTS/TRX (%u/%u) " - "on ARFCN %u using MCC=%u MNC=%u LAC=%u CID=%u BSIC=%u TSC=%u\n", - trx->bts->nr, trx->nr, trx->arfcn, bsc_gsmnet->country_code, - bsc_gsmnet->network_code, trx->bts->location_area_code, - trx->bts->cell_identity, trx->bts->bsic, trx->bts->tsc); - set_system_infos(trx); - - for (i = 0; i < ARRAY_SIZE(trx->ts); i++) - generate_ma_for_ts(&trx->ts[i]); -} - -/* Callback function to be called every time we receive a signal from INPUT */ -static int inp_sig_cb(unsigned int subsys, unsigned int signal, - void *handler_data, void *signal_data) -{ - struct input_signal_data *isd = signal_data; - struct gsm_bts_trx *trx = isd->trx; - int ts_no, lchan_no; - - if (subsys != SS_INPUT) - return -EINVAL; - - switch (signal) { - case S_INP_TEI_UP: - if (isd->link_type == E1INP_SIGN_RSL) - bootstrap_rsl(trx); - break; - case S_INP_TEI_DN: - LOGP(DMI, LOGL_ERROR, "Lost some E1 TEI link: %d %p\n", isd->link_type, trx); - - if (isd->link_type == E1INP_SIGN_OML) - counter_inc(trx->bts->network->stats.bts.oml_fail); - else if (isd->link_type == E1INP_SIGN_RSL) - counter_inc(trx->bts->network->stats.bts.rsl_fail); - - /* - * free all allocated channels. change the nm_state so the - * trx and trx_ts becomes unusable and chan_alloc.c can not - * allocate from it. - */ - for (ts_no = 0; ts_no < ARRAY_SIZE(trx->ts); ++ts_no) { - struct gsm_bts_trx_ts *ts = &trx->ts[ts_no]; - - for (lchan_no = 0; lchan_no < ARRAY_SIZE(ts->lchan); ++lchan_no) { - if (ts->lchan[lchan_no].state != LCHAN_S_NONE) - lchan_free(&ts->lchan[lchan_no]); - lchan_reset(&ts->lchan[lchan_no]); - } - - ts->nm_state.operational = 0; - ts->nm_state.availability = 0; - } - - trx->nm_state.operational = 0; - trx->nm_state.availability = 0; - trx->bb_transc.nm_state.operational = 0; - trx->bb_transc.nm_state.availability = 0; - - abis_nm_clear_queue(trx->bts); - break; - default: - break; - } - - return 0; -} - -static int bootstrap_bts(struct gsm_bts *bts) -{ - int i, n; - - /* 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 < 1 || - (bts->c0->arfcn > 124 && bts->c0->arfcn < 955) || - bts->c0->arfcn > 1023) { - LOGP(DNM, LOGL_ERROR, "GSM900 channel must be between 1-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 */ - bts->si_common.chan_desc.att = 1; - bts->si_common.chan_desc.bs_pa_mfrms = RSL_BS_PA_MFRMS_5; - bts->si_common.chan_desc.bs_ag_blks_res = 1; - - /* 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; - 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; - } - - /* some defaults for our system information */ - bts->si_common.cell_options.radio_link_timeout = 7; /* 12 */ - - /* allow/disallow DTXu */ - if (bts->network->dtx_enabled) - bts->si_common.cell_options.dtx = 0; - else - bts->si_common.cell_options.dtx = 2; - - bts->si_common.cell_options.pwrc = 0; /* PWRC not set */ - - bts->si_common.cell_sel_par.acs = 0; - - bts->si_common.ncc_permitted = 0xff; - - paging_init(bts); - - return 0; -} - -int bsc_bootstrap_network(int (*mncc_recv)(struct gsm_network *, struct msgb *), - const char *config_file) -{ - struct telnet_connection dummy_conn; - struct gsm_bts *bts; - int rc; - - /* initialize our data structures */ - bsc_gsmnet = gsm_network_init(1, 1, mncc_recv); - if (!bsc_gsmnet) - return -ENOMEM; - - bsc_gsmnet->name_long = talloc_strdup(bsc_gsmnet, "OpenBSC"); - bsc_gsmnet->name_short = talloc_strdup(bsc_gsmnet, "OpenBSC"); - - /* our vty command code expects vty->priv to point to a telnet_connection */ - dummy_conn.priv = bsc_gsmnet; - rc = vty_read_config_file(config_file, &dummy_conn); - if (rc < 0) { - LOGP(DNM, LOGL_FATAL, "Failed to parse the config file: '%s'\n", config_file); - return rc; - } - - rc = telnet_init(tall_bsc_ctx, bsc_gsmnet, 4242); - if (rc < 0) - return rc; - - register_signal_handler(SS_NM, nm_sig_cb, NULL); - register_signal_handler(SS_INPUT, inp_sig_cb, NULL); - - llist_for_each_entry(bts, &bsc_gsmnet->bts_list, list) { - bootstrap_bts(bts); - if (!is_ipaccess_bts(bts)) - rc = e1_reconfig_bts(bts); - - if (rc < 0) { - fprintf(stderr, "Error in E1 input driver setup\n"); - exit (1); - } - } - - /* initialize nanoBTS support omce */ - rc = ipaccess_setup(bsc_gsmnet); - - return 0; -} diff --git a/openbsc/src/bsc/bsc_msc.c b/openbsc/src/bsc/bsc_msc.c deleted file mode 100644 index 508697ab1..000000000 --- a/openbsc/src/bsc/bsc_msc.c +++ /dev/null @@ -1,259 +0,0 @@ -/* Routines to talk to the MSC using the IPA Protocol */ -/* - * (C) 2010 by Holger Hans Peter Freyther - * (C) 2010 by On-Waves - * All Rights Reserved - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation; either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - * - */ - -#include -#include -#include - -#include -#include - -#include -#include -#include -#include -#include -#include -#include - -static void connection_loss(struct bsc_msc_connection *con) -{ - struct bsc_fd *fd; - - fd = &con->write_queue.bfd; - - close(fd->fd); - fd->fd = -1; - fd->cb = write_queue_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 Connection timeout.\n"); - bsc_msc_lost(con); -} - -/* called in the case of a non blocking connect */ -static int msc_connection_connect(struct bsc_fd *fd, unsigned int what) -{ - int rc; - int val; - struct bsc_msc_connection *con; - struct write_queue *queue; - - socklen_t len = sizeof(val); - - if ((what & BSC_FD_WRITE) == 0) { - LOGP(DMSC, LOGL_ERROR, "Callback but not writable.\n"); - return -1; - } - - queue = container_of(fd, struct write_queue, bfd); - con = container_of(queue, struct bsc_msc_connection, write_queue); - - /* From here on we will either be connected or reconnect */ - bsc_del_timer(&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 socket failed.\n"); - goto error; - } - if (val != 0) { - LOGP(DMSC, LOGL_ERROR, "Not connected to the MSC: %d\n", val); - goto error; - } - - - /* go to full operation */ - fd->cb = write_queue_bfd_cb; - fd->when = BSC_FD_READ | BSC_FD_EXCEPT; - - con->is_connected = 1; - LOGP(DMSC, LOGL_NOTICE, "(Re)Connected to the MSC.\n"); - if (con->connected) - con->connected(con); - return 0; - -error: - bsc_unregister_fd(fd); - connection_loss(con); - return -1; -} -static void setnonblocking(struct bsc_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_fd *fd; - struct sockaddr_in sin; - int on = 1, ret; - - LOGP(DMSC, LOGL_NOTICE, "Attempting to connect MSC at %s:%d\n", con->ip, con->port); - - con->is_connected = 0; - - 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, - &con->prio, sizeof(con->prio)); - if (ret != 0) - LOGP(DMSC, LOGL_ERROR, "Failed to set prio to %d. %s\n", - con->prio, strerror(errno)); - - memset(&sin, 0, sizeof(sin)); - sin.sin_family = AF_INET; - sin.sin_port = htons(con->port); - inet_aton(con->ip, &sin.sin_addr); - - setsockopt(fd->fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)); - ret = connect(fd->fd, (struct sockaddr *) &sin, sizeof(sin)); - - if (ret == -1 && errno == EINPROGRESS) { - LOGP(DMSC, LOGL_ERROR, "MSC Connection in progress\n"); - fd->when = BSC_FD_WRITE; - fd->cb = msc_connection_connect; - con->timeout_timer.cb = msc_con_timeout; - con->timeout_timer.data = con; - bsc_schedule_timer(&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 = write_queue_bfd_cb; - con->is_connected = 1; - if (con->connected) - con->connected(con); - } - - ret = bsc_register_fd(fd); - if (ret < 0) { - perror("Registering the fd failed"); - close(fd->fd); - return ret; - } - - return ret; -} - -struct bsc_msc_connection *bsc_msc_create(const char *ip, int port, int prio) -{ - 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->ip = ip; - con->port = port; - con->prio = prio; - write_queue_init(&con->write_queue, 100); - return con; -} - -void bsc_msc_lost(struct bsc_msc_connection *con) -{ - write_queue_clear(&con->write_queue); - bsc_del_timer(&con->timeout_timer); - - if (con->write_queue.bfd.fd >= 0) - bsc_unregister_fd(&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.\n"); - bsc_msc_connect(con); -} - -void bsc_msc_schedule_connect(struct bsc_msc_connection *con) -{ - LOGP(DMSC, LOGL_NOTICE, "Attempting to reconnect to the MSC.\n"); - con->reconnect_timer.cb = reconnect_msc; - con->reconnect_timer.data = con; - bsc_schedule_timer(&con->reconnect_timer, 5, 0); -} - -struct msgb *bsc_msc_id_get_resp(const char *token) -{ - 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; - } - - msg->l2h = msgb_v_put(msg, IPAC_MSGT_ID_RESP); - msgb_l16tv_put(msg, strlen(token) + 1, - IPAC_IDTAG_UNITNAME, (u_int8_t *) token); - return msg; -} diff --git a/openbsc/src/bsc/bsc_rll.c b/openbsc/src/bsc/bsc_rll.c deleted file mode 100644 index 722f3fafc..000000000 --- a/openbsc/src/bsc/bsc_rll.c +++ /dev/null @@ -1,141 +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 - * - * All Rights Reserved - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation; either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - * - */ - -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -struct bsc_rll_req { - struct llist_head list; - struct timer_list timer; - - struct gsm_lchan *lchan; - u_int8_t link_id; - - void (*cb)(struct gsm_lchan *lchan, u_int8_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, u_int8_t sapi, - void (*cb)(struct gsm_lchan *, u_int8_t, void *, - enum bsc_rllr_ind), - void *data) -{ - struct bsc_rll_req *rllr = talloc_zero(tall_bsc_ctx, struct bsc_rll_req); - u_int8_t link_id; - if (!rllr) - return -ENOMEM; - - link_id = sapi; - - /* If we are a TCH and not in signalling mode, we need to - * indicate that the new RLL connection is to be made on the SACCH */ - if ((lchan->type == GSM_LCHAN_TCH_F || - lchan->type == GSM_LCHAN_TCH_H) && sapi != 0) - link_id |= 0x40; - - rllr->lchan = lchan; - rllr->link_id = link_id; - rllr->cb = cb; - rllr->data = data; - - llist_add(&rllr->list, &bsc_rll_reqs); - - rllr->timer.cb = &timer_cb; - rllr->timer.data = rllr; - - bsc_schedule_timer(&rllr->timer, 10, 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, u_int8_t link_id, u_int8_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)) { - bsc_del_timer(&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) { - bsc_del_timer(&rllr->timer); - complete_rllr(rllr, BSC_RLLR_IND_ERR_IND); - } - } - - return 0; -} - -static __attribute__((constructor)) void on_dso_load_rll(void) -{ - register_signal_handler(SS_CHALLOC, rll_lchan_signal, NULL); -} diff --git a/openbsc/src/bsc/bsc_vty.c b/openbsc/src/bsc/bsc_vty.c deleted file mode 100644 index ed45afd4d..000000000 --- a/openbsc/src/bsc/bsc_vty.c +++ /dev/null @@ -1,2773 +0,0 @@ -/* OpenBSC interface to quagga VTY */ -/* (C) 2009-2010 by Harald Welte - * All Rights Reserved - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation; either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - * - */ - -#include -#include -#include - -#include -#include -#include -#include -#include - -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "../bscconfig.h" - -/* 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 } -}; - -struct cmd_node net_node = { - GSMNET_NODE, - "%s(network)#", - 1, -}; - -struct cmd_node bts_node = { - BTS_NODE, - "%s(bts)#", - 1, -}; - -struct cmd_node trx_node = { - TRX_NODE, - "%s(trx)#", - 1, -}; - -struct cmd_node ts_node = { - TS_NODE, - "%s(ts)#", - 1, -}; - -extern struct gsm_network *bsc_gsmnet; - -struct gsm_network *gsmnet_from_vty(struct vty *v) -{ - /* In case we read from the config file, the vty->priv cannot - * point to a struct telnet_connection, and thus conn->priv - * will not point to the gsm_network structure */ -#if 0 - struct telnet_connection *conn = v->priv; - return (struct gsm_network *) conn->priv; -#else - return bsc_gsmnet; -#endif -} - -static int dummy_config_write(struct vty *v) -{ - return CMD_SUCCESS; -} - -static void net_dump_nmstate(struct vty *vty, struct gsm_nm_state *nms) -{ - vty_out(vty,"Oper '%s', Admin %u, Avail '%s'%s", - nm_opstate_name(nms->operational), nms->administrative, - nm_avail_name(nms->availability), VTY_NEWLINE); -} - -static void dump_pchan_load_vty(struct vty *vty, char *prefix, - const struct pchan_load *pl) -{ - int i; - - for (i = 0; i < ARRAY_SIZE(pl->pchan); i++) { - const struct load_counter *lc = &pl->pchan[i]; - unsigned int percent; - - if (lc->total == 0) - continue; - - percent = (lc->used * 100) / lc->total; - - vty_out(vty, "%s%20s: %3u%% (%u/%u)%s", prefix, - gsm_pchan_name(i), percent, lc->used, lc->total, - VTY_NEWLINE); - } -} - -static void net_dump_vty(struct vty *vty, struct gsm_network *net) -{ - struct pchan_load pl; - - vty_out(vty, "BSC is on Country Code %u, Network Code %u " - "and has %u BTS%s", net->country_code, net->network_code, - net->num_bts, VTY_NEWLINE); - vty_out(vty, " Long network name: '%s'%s", - net->name_long, VTY_NEWLINE); - vty_out(vty, " Short network name: '%s'%s", - net->name_short, VTY_NEWLINE); - vty_out(vty, " Authentication policy: %s%s", - gsm_auth_policy_name(net->auth_policy), VTY_NEWLINE); - vty_out(vty, " Location updating reject cause: %u%s", - net->reject_cause, VTY_NEWLINE); - vty_out(vty, " Encryption: A5/%u%s", net->a5_encryption, - VTY_NEWLINE); - vty_out(vty, " NECI (TCH/H): %u%s", net->neci, - VTY_NEWLINE); - vty_out(vty, " Use TCH for Paging any: %d%s", net->pag_any_tch, - VTY_NEWLINE); - vty_out(vty, " RRLP Mode: %s%s", rrlp_mode_name(net->rrlp.mode), - VTY_NEWLINE); - vty_out(vty, " MM Info: %s%s", net->send_mm_info ? "On" : "Off", - VTY_NEWLINE); - vty_out(vty, " Handover: %s%s", net->handover.active ? "On" : "Off", - VTY_NEWLINE); - network_chan_load(&pl, net); - vty_out(vty, " Current Channel Load:%s", VTY_NEWLINE); - dump_pchan_load_vty(vty, " ", &pl); - - /* show rf */ - if (net->msc_data) - vty_out(vty, " Last RF Command: %s%s", - net->msc_data->rf_ctl->last_state_command, - VTY_NEWLINE); -} - -DEFUN(show_net, show_net_cmd, "show network", - SHOW_STR "Display information about a GSM NETWORK\n") -{ - struct gsm_network *net = gsmnet_from_vty(vty); - net_dump_vty(vty, net); - - return CMD_SUCCESS; -} - -static void e1isl_dump_vty(struct vty *vty, struct e1inp_sign_link *e1l) -{ - struct e1inp_line *line; - - if (!e1l) { - vty_out(vty, " None%s", VTY_NEWLINE); - return; - } - - line = e1l->ts->line; - - vty_out(vty, " E1 Line %u, Type %s: Timeslot %u, Mode %s%s", - line->num, line->driver->name, e1l->ts->num, - e1inp_signtype_name(e1l->type), VTY_NEWLINE); - vty_out(vty, " E1 TEI %u, SAPI %u%s", - e1l->tei, e1l->sapi, VTY_NEWLINE); -} - -static void bts_dump_vty(struct vty *vty, struct gsm_bts *bts) -{ - struct pchan_load pl; - - vty_out(vty, "BTS %u is of %s type in band %s, has CI %u LAC %u, " - "BSIC %u, TSC %u and %u TRX%s", - bts->nr, btstype2str(bts->type), gsm_band_name(bts->band), - bts->cell_identity, - bts->location_area_code, bts->bsic, bts->tsc, - bts->num_trx, VTY_NEWLINE); - vty_out(vty, "Description: %s%s", - bts->description ? bts->description : "(null)", VTY_NEWLINE); - vty_out(vty, "MS Max power: %u dBm%s", bts->ms_max_power, VTY_NEWLINE); - vty_out(vty, "Minimum Rx Level for Access: %i dBm%s", - rxlev2dbm(bts->si_common.cell_sel_par.rxlev_acc_min), - VTY_NEWLINE); - vty_out(vty, "Cell Reselection Hysteresis: %u dBm%s", - bts->si_common.cell_sel_par.cell_resel_hyst*2, VTY_NEWLINE); - vty_out(vty, "RACH TX-Integer: %u%s", bts->si_common.rach_control.tx_integer, - VTY_NEWLINE); - vty_out(vty, "RACH Max transmissions: %u%s", - rach_max_trans_raw2val(bts->si_common.rach_control.max_trans), - VTY_NEWLINE); - if (bts->si_common.rach_control.cell_bar) - vty_out(vty, " CELL IS BARRED%s", VTY_NEWLINE); - vty_out(vty, "System Information present: 0x%08x, static: 0x%08x%s", - bts->si_valid, bts->si_mode_static, VTY_NEWLINE); - if (is_ipaccess_bts(bts)) - vty_out(vty, " Unit ID: %u/%u/0, OML Stream ID 0x%02x%s", - bts->ip_access.site_id, bts->ip_access.bts_id, - bts->oml_tei, VTY_NEWLINE); - vty_out(vty, " NM State: "); - net_dump_nmstate(vty, &bts->nm_state); - vty_out(vty, " Site Mgr NM State: "); - net_dump_nmstate(vty, &bts->site_mgr.nm_state); - vty_out(vty, " Paging: FIXME pending requests, %u free slots%s", - 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 [number]", - 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->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->nm_state.administrative == NM_STATE_LOCKED ? 1 : 0, - VTY_NEWLINE); - vty_out(vty, " arfcn %u%s", trx->arfcn, VTY_NEWLINE); - vty_out(vty, " nominal power %u%s", trx->nominal_power, VTY_NEWLINE); - vty_out(vty, " max_power_red %u%s", trx->max_power_red, VTY_NEWLINE); - config_write_e1_link(vty, &trx->rsl_e1_link, " rsl "); - vty_out(vty, " rsl e1 tei %u%s", trx->rsl_tei, VTY_NEWLINE); - - if (trx->bts->model->config_write_trx) - trx->bts->model->config_write_trx(vty, trx); - - for (i = 0; i < TRX_NR_TS; i++) - config_write_ts_single(vty, &trx->ts[i]); -} - -static void config_write_bts_gprs(struct vty *vty, struct gsm_bts *bts) -{ - unsigned int i; - vty_out(vty, " gprs mode %s%s", bts_gprs_mode_name(bts->gprs.mode), - VTY_NEWLINE); - if (bts->gprs.mode == BTS_GPRS_NONE) - return; - - vty_out(vty, " gprs routing area %u%s", bts->gprs.rac, - VTY_NEWLINE); - vty_out(vty, " gprs 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); - } -} - -static void config_write_bts_single(struct vty *vty, struct gsm_bts *bts) -{ - struct gsm_bts_trx *trx; - int i; - - vty_out(vty, " bts %u%s", bts->nr, VTY_NEWLINE); - vty_out(vty, " type %s%s", btstype2str(bts->type), VTY_NEWLINE); - if (bts->description) - vty_out(vty, " description %s%s", bts->description, VTY_NEWLINE); - vty_out(vty, " band %s%s", gsm_band_name(bts->band), VTY_NEWLINE); - vty_out(vty, " cell_identity %u%s", bts->cell_identity, VTY_NEWLINE); - vty_out(vty, " location_area_code %u%s", bts->location_area_code, - VTY_NEWLINE); - vty_out(vty, " training_sequence_code %u%s", bts->tsc, 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); - } - - if (bts->si_common.chan_desc.t3212) - vty_out(vty, " periodic location update %u%s", - bts->si_common.chan_desc.t3212 * 6, 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); - - 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); - 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), - hexdump_nospc(bts->si_buf[i], sizeof(bts->si_buf[i])), - VTY_NEWLINE); - } - } - if (is_ipaccess_bts(bts)) { - vty_out(vty, " ip.access unit_id %u %u%s", - bts->ip_access.site_id, bts->ip_access.bts_id, VTY_NEWLINE); - vty_out(vty, " oml ip.access stream_id %u%s", bts->oml_tei, VTY_NEWLINE); - } else { - config_write_e1_link(vty, &bts->oml_e1_link, " oml "); - vty_out(vty, " oml e1 tei %u%s", bts->oml_tei, VTY_NEWLINE); - } - - /* 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); - } - } - - config_write_bts_gprs(vty, bts); - - 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 int config_write_bts(struct vty *v) -{ - struct gsm_network *gsmnet = gsmnet_from_vty(v); - struct gsm_bts *bts; - - llist_for_each_entry(bts, &gsmnet->bts_list, list) - config_write_bts_single(v, bts); - - return CMD_SUCCESS; -} - -static int config_write_net(struct vty *vty) -{ - struct gsm_network *gsmnet = gsmnet_from_vty(vty); - - vty_out(vty, "network%s", VTY_NEWLINE); - vty_out(vty, " network country code %u%s", gsmnet->country_code, VTY_NEWLINE); - vty_out(vty, " mobile network code %u%s", gsmnet->network_code, VTY_NEWLINE); - vty_out(vty, " short name %s%s", gsmnet->name_short, VTY_NEWLINE); - vty_out(vty, " long name %s%s", gsmnet->name_long, VTY_NEWLINE); - vty_out(vty, " auth policy %s%s", gsm_auth_policy_name(gsmnet->auth_policy), VTY_NEWLINE); - vty_out(vty, " location updating reject cause %u%s", - gsmnet->reject_cause, VTY_NEWLINE); - vty_out(vty, " encryption a5 %u%s", gsmnet->a5_encryption, VTY_NEWLINE); - vty_out(vty, " neci %u%s", gsmnet->neci, VTY_NEWLINE); - vty_out(vty, " paging any use tch %d%s", gsmnet->pag_any_tch, VTY_NEWLINE); - vty_out(vty, " rrlp mode %s%s", rrlp_mode_name(gsmnet->rrlp.mode), - VTY_NEWLINE); - vty_out(vty, " mm info %u%s", gsmnet->send_mm_info, VTY_NEWLINE); - vty_out(vty, " handover %u%s", gsmnet->handover.active, VTY_NEWLINE); - vty_out(vty, " handover window rxlev averaging %u%s", - gsmnet->handover.win_rxlev_avg, VTY_NEWLINE); - vty_out(vty, " handover window rxqual averaging %u%s", - gsmnet->handover.win_rxqual_avg, VTY_NEWLINE); - vty_out(vty, " handover window rxlev neighbor averaging %u%s", - gsmnet->handover.win_rxlev_avg_neigh, VTY_NEWLINE); - vty_out(vty, " handover power budget interval %u%s", - gsmnet->handover.pwr_interval, VTY_NEWLINE); - vty_out(vty, " handover power budget hysteresis %u%s", - gsmnet->handover.pwr_hysteresis, VTY_NEWLINE); - vty_out(vty, " handover maximum distance %u%s", - gsmnet->handover.max_distance, VTY_NEWLINE); - vty_out(vty, " timer t3101 %u%s", gsmnet->T3101, VTY_NEWLINE); - vty_out(vty, " timer t3103 %u%s", gsmnet->T3103, VTY_NEWLINE); - vty_out(vty, " timer t3105 %u%s", gsmnet->T3105, VTY_NEWLINE); - vty_out(vty, " timer t3107 %u%s", gsmnet->T3107, VTY_NEWLINE); - vty_out(vty, " timer t3109 %u%s", gsmnet->T3109, VTY_NEWLINE); - vty_out(vty, " timer t3111 %u%s", gsmnet->T3111, VTY_NEWLINE); - vty_out(vty, " timer t3113 %u%s", gsmnet->T3113, VTY_NEWLINE); - vty_out(vty, " timer t3115 %u%s", gsmnet->T3115, VTY_NEWLINE); - vty_out(vty, " timer t3117 %u%s", gsmnet->T3117, VTY_NEWLINE); - vty_out(vty, " timer t3119 %u%s", gsmnet->T3119, VTY_NEWLINE); - vty_out(vty, " timer t3122 %u%s", gsmnet->T3122, VTY_NEWLINE); - vty_out(vty, " timer t3141 %u%s", gsmnet->T3141, VTY_NEWLINE); - vty_out(vty, " dtx-used %u%s", gsmnet->dtx_enabled, VTY_NEWLINE); - vty_out(vty, " subscriber-keep-in-ram %d%s", - gsmnet->keep_subscr, VTY_NEWLINE); - - return CMD_SUCCESS; -} - -static void trx_dump_vty(struct vty *vty, struct gsm_bts_trx *trx) -{ - vty_out(vty, "TRX %u of BTS %u is on ARFCN %u%s", - trx->nr, trx->bts->nr, trx->arfcn, VTY_NEWLINE); - vty_out(vty, "Description: %s%s", - trx->description ? trx->description : "(null)", VTY_NEWLINE); - vty_out(vty, " RF Nominal Power: %d dBm, reduced by %u dB, " - "resulting BS power: %d dBm%s", - trx->nominal_power, trx->max_power_red, - trx->nominal_power - trx->max_power_red, VTY_NEWLINE); - vty_out(vty, " NM State: "); - net_dump_nmstate(vty, &trx->nm_state); - vty_out(vty, " Baseband Transceiver NM State: "); - net_dump_nmstate(vty, &trx->bb_transc.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 [bts_nr] [trx_nr]", - 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", - ts->trx->bts->nr, ts->trx->nr, ts->nr, - gsm_pchan_name(ts->pchan)); - if (ts->pchan == GSM_PCHAN_TCH_F_PDCH) - vty_out(vty, " (%s mode)", - ts->flags & TS_F_PDCH_MODE ? "PDCH" : "TCH/F"); - vty_out(vty, "%s", VTY_NEWLINE); - vty_out(vty, " NM State: "); - net_dump_nmstate(vty, &ts->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 [bts_nr] [trx_nr] [ts_nr]", - 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 (subscr->name) - vty_out(vty, " Name: '%s'%s", subscr->name, VTY_NEWLINE); - if (subscr->extension) - vty_out(vty, " Extension: %s%s", subscr->extension, - VTY_NEWLINE); - if (subscr->imsi) - vty_out(vty, " IMSI: %s%s", subscr->imsi, VTY_NEWLINE); - if (subscr->tmsi != GSM_RESERVED_TMSI) - vty_out(vty, " TMSI: %08X%s", subscr->tmsi, - VTY_NEWLINE); - - vty_out(vty, " Use count: %u%s", subscr->use_count, VTY_NEWLINE); -} - -static void meas_rep_dump_uni_vty(struct vty *vty, - struct gsm_meas_rep_unidir *mru, - const char *prefix, - const char *dir) -{ - vty_out(vty, "%s RXL-FULL-%s: %4d dBm, RXL-SUB-%s: %4d dBm ", - prefix, dir, rxlev2dbm(mru->full.rx_lev), - dir, rxlev2dbm(mru->sub.rx_lev)); - vty_out(vty, "RXQ-FULL-%s: %d, RXQ-SUB-%s: %d%s", - dir, mru->full.rx_qual, dir, mru->sub.rx_qual, - VTY_NEWLINE); -} - -static void meas_rep_dump_vty(struct vty *vty, struct gsm_meas_rep *mr, - const char *prefix) -{ - vty_out(vty, "%sMeasurement Report:%s", prefix, VTY_NEWLINE); - vty_out(vty, "%s Flags: %s%s%s%s%s", prefix, - mr->flags & MEAS_REP_F_UL_DTX ? "DTXu " : "", - mr->flags & MEAS_REP_F_DL_DTX ? "DTXd " : "", - mr->flags & MEAS_REP_F_FPC ? "FPC " : "", - mr->flags & MEAS_REP_F_DL_VALID ? " " : "DLinval ", - VTY_NEWLINE); - if (mr->flags & MEAS_REP_F_MS_TO) - vty_out(vty, "%s MS Timing Offset: %u%s", prefix, - mr->ms_timing_offset, VTY_NEWLINE); - if (mr->flags & MEAS_REP_F_MS_L1) - vty_out(vty, "%s L1 MS Power: %u dBm, Timing Advance: %u%s", - prefix, mr->ms_l1.pwr, mr->ms_l1.ta, VTY_NEWLINE); - if (mr->flags & MEAS_REP_F_DL_VALID) - meas_rep_dump_uni_vty(vty, &mr->dl, prefix, "dl"); - meas_rep_dump_uni_vty(vty, &mr->ul, prefix, "ul"); -} - -static void lchan_dump_full_vty(struct vty *vty, struct gsm_lchan *lchan) -{ - int idx; - - vty_out(vty, "BTS %u, TRX %u, Timeslot %u, Lchan %u: Type %s%s", - lchan->ts->trx->bts->nr, lchan->ts->trx->nr, lchan->ts->nr, - lchan->nr, gsm_lchant_name(lchan->type), VTY_NEWLINE); - vty_out(vty, " Connection: %u, State: %s%s", - lchan->conn ? 1: 0, - gsm_lchans_name(lchan->state), VTY_NEWLINE); - vty_out(vty, " BS Power: %u dBm, MS Power: %u dBm%s", - lchan->ts->trx->nominal_power - lchan->ts->trx->max_power_red - - lchan->bs_power*2, - ms_pwr_dbm(lchan->ts->trx->bts->band, lchan->ms_power), - VTY_NEWLINE); - if (lchan->conn && lchan->conn->subscr) { - vty_out(vty, " Subscriber:%s", VTY_NEWLINE); - subscr_dump_vty(vty, lchan->conn->subscr); - } else - vty_out(vty, " No Subscriber%s", VTY_NEWLINE); - if (is_ipaccess_bts(lchan->ts->trx->bts)) { - struct in_addr ia; - ia.s_addr = htonl(lchan->abis_ip.bound_ip); - vty_out(vty, " Bound IP: %s Port %u RTP_TYPE2=%u CONN_ID=%u%s", - inet_ntoa(ia), lchan->abis_ip.bound_port, - lchan->abis_ip.rtp_payload2, lchan->abis_ip.conn_id, - VTY_NEWLINE); - } - - /* we want to report the last measurement report */ - idx = calc_initial_idx(ARRAY_SIZE(lchan->meas_rep), - lchan->meas_rep_idx, 1); - meas_rep_dump_vty(vty, &lchan->meas_rep[idx], " "); -} - -static void lchan_dump_short_vty(struct vty *vty, struct gsm_lchan *lchan) -{ - struct gsm_meas_rep *mr; - int idx; - - /* we want to report the last measurement report */ - idx = calc_initial_idx(ARRAY_SIZE(lchan->meas_rep), - lchan->meas_rep_idx, 1); - mr = &lchan->meas_rep[idx]; - - vty_out(vty, "BTS %u, TRX %u, Timeslot %u, Lchan %u, Type %s - " - "L1 MS Power: %u dBm RXL-FULL-dl: %4d dBm RXL-FULL-ul: %4d dBm%s", - lchan->ts->trx->bts->nr, lchan->ts->trx->nr, lchan->ts->nr, - lchan->nr, gsm_lchant_name(lchan->type), mr->ms_l1.pwr, - rxlev2dbm(mr->dl.full.rx_lev), - rxlev2dbm(mr->ul.full.rx_lev), - VTY_NEWLINE); -} - -static int 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 >= 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; - } - ts = &trx->ts[ts_nr]; - } - 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); - 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]; - for (lchan_nr = 0; lchan_nr < TS_MAX_LCHAN; - lchan_nr++) { - lchan = &ts->lchan[lchan_nr]; - if (lchan->type == GSM_LCHAN_NONE) - continue; - dump_cb(vty, lchan); - } - } - } - } - - return CMD_SUCCESS; -} - - -DEFUN(show_lchan, - show_lchan_cmd, - "show lchan [bts_nr] [trx_nr] [ts_nr] [lchan_nr]", - SHOW_STR "Display information about a logical channel\n" - "BTS Number\n" "TRX Number\n" "Timeslot Number\n" - "Logical Channel Number\n") - -{ - return lchan_summary(vty, argc, argv, lchan_dump_full_vty); -} - -DEFUN(show_lchan_summary, - show_lchan_summary_cmd, - "show lchan summary [bts_nr] [trx_nr] [ts_nr] [lchan_nr]", - SHOW_STR "Display information about a logical channel\n" - "BTS Number\n" "TRX Number\n" "Timeslot Number\n" - "Logical Channel Number\n") -{ - return lchan_summary(vty, argc, argv, lchan_dump_short_vty); -} - -static void e1drv_dump_vty(struct vty *vty, struct e1inp_driver *drv) -{ - vty_out(vty, "E1 Input Driver %s%s", drv->name, VTY_NEWLINE); -} - -DEFUN(show_e1drv, - show_e1drv_cmd, - "show e1_driver", - SHOW_STR "Display information about available E1 drivers\n") -{ - struct e1inp_driver *drv; - - llist_for_each_entry(drv, &e1inp_driver_list, list) - e1drv_dump_vty(vty, drv); - - return CMD_SUCCESS; -} - -static void e1line_dump_vty(struct vty *vty, struct e1inp_line *line) -{ - vty_out(vty, "E1 Line Number %u, Name %s, Driver %s%s", - line->num, line->name ? line->name : "", - line->driver->name, VTY_NEWLINE); -} - -DEFUN(show_e1line, - show_e1line_cmd, - "show e1_line [line_nr]", - SHOW_STR "Display information about a E1 line\n" - "E1 Line Number\n") -{ - struct e1inp_line *line; - - if (argc >= 1) { - int num = atoi(argv[0]); - llist_for_each_entry(line, &e1inp_line_list, list) { - if (line->num == num) { - e1line_dump_vty(vty, line); - return CMD_SUCCESS; - } - } - return CMD_WARNING; - } - - llist_for_each_entry(line, &e1inp_line_list, list) - e1line_dump_vty(vty, line); - - return CMD_SUCCESS; -} - -static void e1ts_dump_vty(struct vty *vty, struct e1inp_ts *ts) -{ - if (ts->type == E1INP_TS_TYPE_NONE) - return; - vty_out(vty, "E1 Timeslot %2u of Line %u is Type %s%s", - ts->num, ts->line->num, e1inp_tstype_name(ts->type), - VTY_NEWLINE); -} - -DEFUN(show_e1ts, - show_e1ts_cmd, - "show e1_timeslot [line_nr] [ts_nr]", - SHOW_STR "Display information about a E1 timeslot\n" - "E1 Line Number\n" "E1 Timeslot Number\n") -{ - struct e1inp_line *line = NULL; - struct e1inp_ts *ts; - int ts_nr; - - if (argc == 0) { - llist_for_each_entry(line, &e1inp_line_list, list) { - for (ts_nr = 0; ts_nr < NUM_E1_TS; ts_nr++) { - ts = &line->ts[ts_nr]; - e1ts_dump_vty(vty, ts); - } - } - return CMD_SUCCESS; - } - if (argc >= 1) { - int num = atoi(argv[0]); - llist_for_each_entry(line, &e1inp_line_list, list) { - if (line->num == num) - break; - } - if (!line || line->num != num) { - vty_out(vty, "E1 line %s is invalid%s", - argv[0], VTY_NEWLINE); - return CMD_WARNING; - } - } - if (argc >= 2) { - ts_nr = atoi(argv[1]); - if (ts_nr > NUM_E1_TS) { - vty_out(vty, "E1 timeslot %s is invalid%s", - argv[1], VTY_NEWLINE); - return CMD_WARNING; - } - ts = &line->ts[ts_nr]; - e1ts_dump_vty(vty, ts); - return CMD_SUCCESS; - } else { - for (ts_nr = 0; ts_nr < NUM_E1_TS; ts_nr++) { - ts = &line->ts[ts_nr]; - e1ts_dump_vty(vty, ts); - } - return CMD_SUCCESS; - } - return CMD_SUCCESS; -} - -static void paging_dump_vty(struct vty *vty, struct gsm_paging_request *pag) -{ - vty_out(vty, "Paging on BTS %u%s", pag->bts->nr, VTY_NEWLINE); - subscr_dump_vty(vty, pag->subscr); -} - -static void bts_paging_dump_vty(struct vty *vty, struct gsm_bts *bts) -{ - struct gsm_paging_request *pag; - - llist_for_each_entry(pag, &bts->paging.pending_requests, entry) - paging_dump_vty(vty, pag); -} - -DEFUN(show_paging, - show_paging_cmd, - "show paging [bts_nr]", - 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; -} - -#define NETWORK_STR "Configure the GSM network\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") -{ - 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 <1-999>", - "Set the GSM mobile network code") -{ - 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") -{ - struct gsm_network *gsmnet = gsmnet_from_vty(vty); - - bsc_replace_string(gsmnet, &gsmnet->name_short, argv[0]); - return CMD_SUCCESS; -} - -DEFUN(cfg_net_name_long, - cfg_net_name_long_cmd, - "long name NAME", - "Set the long GSM network name") -{ - struct gsm_network *gsmnet = gsmnet_from_vty(vty); - - bsc_replace_string(gsmnet, &gsmnet->name_long, argv[0]); - return CMD_SUCCESS; -} - -DEFUN(cfg_net_auth_policy, - cfg_net_auth_policy_cmd, - "auth policy (closed|accept-all|token)", - "Authentication (not cryptographic)\n" - "Set the GSM network authentication policy\n" - "Require the MS to be activated in HLR\n" - "Accept all MS, whether in HLR or not\n" - "Use SMS-token based authentication\n") -{ - enum gsm_auth_policy policy = gsm_auth_policy_parse(argv[0]); - struct gsm_network *gsmnet = gsmnet_from_vty(vty); - - gsmnet->auth_policy = policy; - - return CMD_SUCCESS; -} - -DEFUN(cfg_net_reject_cause, - cfg_net_reject_cause_cmd, - "location updating reject cause <2-111>", - "Set the reject cause of location updating reject\n") -{ - 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)", - "Encryption options\n" - "A5 encryption\n" "A5/0: No encryption\n" - "A5/1: Encryption\n" "A5/2: Export-grade Encryption\n") -{ - struct gsm_network *gsmnet = gsmnet_from_vty(vty); - - gsmnet->a5_encryption= atoi(argv[0]); - - return CMD_SUCCESS; -} - -DEFUN(cfg_net_neci, - cfg_net_neci_cmd, - "neci (0|1)", - "New Establish Cause Indication\n" - "Don't set the NECI bit\n" "Set the NECI bit\n") -{ - struct gsm_network *gsmnet = gsmnet_from_vty(vty); - - gsmnet->neci = atoi(argv[0]); - gsm_net_update_ctype(gsmnet); - return CMD_SUCCESS; -} - -DEFUN(cfg_net_rrlp_mode, cfg_net_rrlp_mode_cmd, - "rrlp mode (none|ms-based|ms-preferred|ass-preferred)", - "Radio Resource Location Protocol\n" - "Set the Radio Resource Location Protocol Mode\n" - "Don't send RRLP request\n" - "Request MS-based location\n" - "Request any location, prefer MS-based\n" - "Request any location, prefer MS-assisted\n") -{ - struct gsm_network *gsmnet = gsmnet_from_vty(vty); - - gsmnet->rrlp.mode = rrlp_mode_parse(argv[0]); - - return CMD_SUCCESS; -} - -DEFUN(cfg_net_mm_info, cfg_net_mm_info_cmd, - "mm info (0|1)", - "Whether to send MM INFO after LOC UPD ACCEPT") -{ - struct gsm_network *gsmnet = gsmnet_from_vty(vty); - - gsmnet->send_mm_info = atoi(argv[0]); - - return CMD_SUCCESS; -} - -#define HANDOVER_STR "Handover Options\n" - -DEFUN(cfg_net_handover, cfg_net_handover_cmd, - "handover (0|1)", - HANDOVER_STR - "Don't perform in-call handover\n" - "Perform in-call handover\n") -{ - int enable = atoi(argv[0]); - struct gsm_network *gsmnet = gsmnet_from_vty(vty); - - if (enable && ipacc_rtp_direct) { - vty_out(vty, "%% Cannot enable handover unless RTP Proxy mode " - "is enabled by using the -P command line option%s", - VTY_NEWLINE); - return CMD_WARNING; - } - gsmnet->handover.active = enable; - - return CMD_SUCCESS; -} - -#define HO_WIN_STR HANDOVER_STR "Measurement Window\n" -#define HO_WIN_RXLEV_STR HO_WIN_STR "Received Level Averaging\n" -#define HO_WIN_RXQUAL_STR HO_WIN_STR "Received Quality Averaging\n" -#define HO_PBUDGET_STR HANDOVER_STR "Power Budget\n" - -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") -{ - 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") -{ - 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 - "How many RxQual measurements are used for averaging") -{ - 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)") -{ - 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") -{ - 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") -{ - 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") -{ - 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) \ -{ \ - 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.") -DECLARE_TIMER(3103, "Set the timeout value for HANDOVER.") -DECLARE_TIMER(3105, "Currently not used.") -DECLARE_TIMER(3107, "Currently not used.") -DECLARE_TIMER(3109, "Currently not used.") -DECLARE_TIMER(3111, "Set the RSL timeout to wait before releasing the RF Channel.") -DECLARE_TIMER(3113, "Set the time to try paging a subscriber.") -DECLARE_TIMER(3115, "Currently not used.") -DECLARE_TIMER(3117, "Currently not used.") -DECLARE_TIMER(3119, "Currently not used.") -DECLARE_TIMER(3122, "Waiting time (seconds) after IMM ASS REJECT") -DECLARE_TIMER(3141, "Currently not used.") - -DEFUN(cfg_net_dtx, - cfg_net_dtx_cmd, - "dtx-used (0|1)", - "Enable the usage of DTX.\n" - "DTX is enabled/disabled") -{ - struct gsm_network *gsmnet = gsmnet_from_vty(vty); - gsmnet->dtx_enabled = atoi(argv[0]); - return CMD_SUCCESS; -} - -DEFUN(cfg_net_subscr_keep, - cfg_net_subscr_keep_cmd, - "subscriber-keep-in-ram (0|1)", - "Keep unused subscribers in RAM.\n" - "Delete unused subscribers\n" "Keep unused subscribers\n") -{ - struct gsm_network *gsmnet = gsmnet_from_vty(vty); - gsmnet->keep_subscr = atoi(argv[0]); - return CMD_SUCCESS; -} - -/* per-BTS configuration */ -DEFUN(cfg_bts, - cfg_bts_cmd, - "bts BTS_NR", - "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(gsmnet, GSM_BTS_TYPE_UNKNOWN, - HARDCODED_TSC, HARDCODED_BSIC); - } else - bts = gsm_bts_num(gsmnet, bts_nr); - - if (!bts) { - vty_out(vty, "%% Unable to allocate BTS %u%s", - gsmnet->num_bts, VTY_NEWLINE); - return CMD_WARNING; - } - - vty->index = bts; - vty->index_sub = &bts->description; - vty->node = BTS_NODE; - - return CMD_SUCCESS; -} - -DEFUN(cfg_bts_type, - cfg_bts_type_cmd, - "type TYPE", - "Set the BTS type\n") -{ - struct gsm_bts *bts = vty->index; - int rc; - - rc = gsm_set_bts_type(bts, parse_btstype(argv[0])); - if (rc < 0) - return CMD_WARNING; - - return CMD_SUCCESS; -} - -DEFUN(cfg_bts_band, - cfg_bts_band_cmd, - "band BAND", - "Set the frequency band of this BTS\n") -{ - struct gsm_bts *bts = vty->index; - int band = gsm_band_parse(argv[0]); - - if (band < 0) { - vty_out(vty, "%% BAND %d is not a valid GSM band%s", - band, VTY_NEWLINE); - return CMD_WARNING; - } - - bts->band = band; - - return CMD_SUCCESS; -} - -DEFUN(cfg_bts_ci, - cfg_bts_ci_cmd, - "cell_identity <0-65535>", - "Set the Cell identity of this BTS\n") -{ - 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") -{ - struct gsm_bts *bts = vty->index; - int lac = atoi(argv[0]); - - if (lac < 0 || lac > 0xffff) { - vty_out(vty, "%% LAC %d is not in the valid range (0-65535)%s", - lac, VTY_NEWLINE); - return CMD_WARNING; - } - - if (lac == GSM_LAC_RESERVED_DETACHED || lac == GSM_LAC_RESERVED_ALL_BTS) { - vty_out(vty, "%% LAC %d is reserved by GSM 04.08%s", - lac, VTY_NEWLINE); - return CMD_WARNING; - } - - bts->location_area_code = lac; - - return CMD_SUCCESS; -} - - -DEFUN(cfg_bts_tsc, - cfg_bts_tsc_cmd, - "training_sequence_code <0-255>", - "Set the Training Sequence Code (TSC) of this BTS\n") -{ - struct gsm_bts *bts = vty->index; - int tsc = atoi(argv[0]); - - if (tsc < 0 || tsc > 0xff) { - vty_out(vty, "%% TSC %d is not in the valid range (0-255)%s", - tsc, VTY_NEWLINE); - return CMD_WARNING; - } - bts->tsc = tsc; - - return CMD_SUCCESS; -} - -DEFUN(cfg_bts_bsic, - cfg_bts_bsic_cmd, - "base_station_id_code <0-63>", - "Set the Base Station Identity Code (BSIC) of this BTS\n") -{ - 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>", - "Set the ip.access BTS Unit ID of this 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; -} - -#define OML_STR "Organization & Maintenance Link\n" -#define IPA_STR "ip.access Specific Options\n" - -DEFUN(cfg_bts_stream_id, - cfg_bts_stream_id_cmd, - "oml ip.access stream_id <0-255>", - OML_STR IPA_STR - "Set the ip.access Stream ID of the OML link of this BTS\n") -{ - struct gsm_bts *bts = vty->index; - int stream_id = atoi(argv[0]); - - 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; - - return CMD_SUCCESS; -} - -#define OML_E1_STR OML_STR "E1 Line\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 interface to be used for OML\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") -{ - 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") -{ - 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") -{ - struct gsm_bts *bts = vty->index; - bts->si_common.rach_control.max_trans = rach_max_trans_val2raw(atoi(argv[0])); - 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 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") -{ - 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?") -{ - 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)", - "Should this cell allow emergency calls?") -{ - 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_ms_max_power, cfg_bts_ms_max_power_cmd, - "ms max power <0-40>", - "Maximum transmit power of the MS") -{ - struct gsm_bts *bts = vty->index; - - bts->ms_max_power = atoi(argv[0]); - - return CMD_SUCCESS; -} - -DEFUN(cfg_bts_cell_resel_hyst, cfg_bts_cell_resel_hyst_cmd, - "cell reselection hysteresis <0-14>", - "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 (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 Bar Qualify") -{ - 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 Re-Selection 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.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 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", - "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 in seconds (by 20s increments)") -{ - 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", - "Set cell selection penalty time to reserved value 31\n" - "(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 <0-1530>", - "Periodic Location Updating Interval in Minutes") -{ - struct gsm_bts *bts = vty->index; - - bts->si_common.chan_desc.t3212 = atoi(argv[0]) / 6; - - 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") -{ - 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") -{ - 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") -{ - 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") -{ - 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") -{ - 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 FREE_NR", - "Only page when having a certain amount of free slots. -1 to disable") -{ - 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") -{ - 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_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]); - - if (mode != BTS_GPRS_NONE && - !gsm_bts_has_feature(bts, BTS_FEAT_GPRS)) { - vty_out(vty, "This BTS type does not support %s%s", argv[0], - VTY_NEWLINE); - return CMD_WARNING; - } - if (mode == BTS_GPRS_EGPRS && - !gsm_bts_has_feature(bts, BTS_FEAT_EGPRS)) { - vty_out(vty, "This BTS type does not support %s%s", argv[0], - VTY_NEWLINE); - return CMD_WARNING; - } - - bts->gprs.mode = mode; - - return CMD_SUCCESS; -} - -#define SI_TEXT "System Information Messages\n" -#define SI_TYPE_TEXT "(1|2|3|4|5|6|7|8|9|10|13|16|17|18|19|20|2bis|2ter|2quater|5bis|5ter)" -#define SI_TYPE_HELP "System Information Type 1\n" \ - "System Information Type 2\n" \ - "System Information Type 3\n" \ - "System Information Type 4\n" \ - "System Information Type 5\n" \ - "System Information Type 6\n" \ - "System Information Type 7\n" \ - "System Information Type 8\n" \ - "System Information Type 9\n" \ - "System Information Type 10\n" \ - "System Information Type 13\n" \ - "System Information Type 16\n" \ - "System Information Type 17\n" \ - "System Information Type 18\n" \ - "System Information Type 19\n" \ - "System Information Type 20\n" \ - "System Information Type 2bis\n" \ - "System Information Type 2ter\n" \ - "System Information Type 2quater\n" \ - "System Information Type 5bis\n" \ - "System Information Type 5ter\n" - -DEFUN(cfg_bts_si_mode, cfg_bts_si_mode_cmd, - "system-information " SI_TYPE_TEXT " mode (static|computed)", - SI_TEXT SI_TYPE_HELP - "System Information Mode\n" - "Static user-specified\n" - "Dynamic, BSC-computed\n") -{ - struct gsm_bts *bts = vty->index; - int type; - - type = get_string_value(osmo_sitype_strs, argv[0]); - if (type < 0) { - vty_out(vty, "Error SI Type%s", VTY_NEWLINE); - return CMD_WARNING; - } - - if (!strcmp(argv[1], "static")) - bts->si_mode_static |= (1 << type); - else - bts->si_mode_static &= ~(1 << type); - - return CMD_SUCCESS; -} - -DEFUN(cfg_bts_si_static, cfg_bts_si_static_cmd, - "system-information " SI_TYPE_TEXT " static HEXSTRING", - SI_TEXT SI_TYPE_HELP - "Static System Information filling\n" - "Static user-specified SI content in HEX notation\n") -{ - struct gsm_bts *bts = vty->index; - int rc, type; - - type = get_string_value(osmo_sitype_strs, argv[0]); - if (type < 0) { - vty_out(vty, "Error SI Type%s", VTY_NEWLINE); - return CMD_WARNING; - } - - if (!(bts->si_mode_static & (1 << type))) { - vty_out(vty, "SI Type %s is not configured in static mode%s", - get_value_string(osmo_sitype_strs, type), VTY_NEWLINE); - return CMD_WARNING; - } - - /* Fill buffer with padding pattern */ - memset(bts->si_buf[type], 0x2b, sizeof(bts->si_buf[type])); - - /* Parse the user-specified SI in hex format, [partially] overwriting padding */ - rc = hexparse(argv[1], bts->si_buf[type], sizeof(bts->si_buf[0])); - if (rc < 0 || rc > sizeof(bts->si_buf[0])) { - vty_out(vty, "Error parsing HEXSTRING%s", VTY_NEWLINE); - return CMD_WARNING; - } - - /* Mark this SI as present */ - bts->si_valid |= (1 << type); - - return CMD_SUCCESS; -} - -DEFUN(cfg_bts_neigh_mode, cfg_bts_neigh_mode_cmd, - "neighbor-list mode (automatic|manual|manual-si5)", - "Neighbor List\n" "Mode of Neighbor List generation\n" - "Automatically from all BTS in this OpenBSC\n" "Manual\n" - "Manual with different lists for SI2 and SI5\n") -{ - struct gsm_bts *bts = vty->index; - int mode = get_string_value(bts_neigh_mode_strs, argv[0]); - - switch (mode) { - case NL_MODE_MANUAL_SI5SEP: - case NL_MODE_MANUAL: - /* make sure we clear the current list when switching to - * manual mode */ - if (bts->neigh_list_manual_mode == 0) - memset(&bts->si_common.data.neigh_list, 0, - sizeof(bts->si_common.data.neigh_list)); - break; - default: - break; - } - - bts->neigh_list_manual_mode = mode; - - return CMD_SUCCESS; -} - -DEFUN(cfg_bts_neigh, cfg_bts_neigh_cmd, - "neighbor-list (add|del) arfcn <0-1024>", - "Neighbor List\n" "Add to manual neighbor list\n" - "Delete from manual neighbor list\n" "ARFCN of neighbor\n" - "ARFCN of neighbor\n") -{ - struct gsm_bts *bts = vty->index; - struct bitvec *bv = &bts->si_common.neigh_list; - uint16_t arfcn = atoi(argv[1]); - - if (!bts->neigh_list_manual_mode) { - vty_out(vty, "%% Cannot configure neighbor list in " - "automatic mode%s", VTY_NEWLINE); - return CMD_WARNING; - } - - if (!strcmp(argv[0], "add")) - bitvec_set_bit_pos(bv, arfcn, 1); - else - bitvec_set_bit_pos(bv, arfcn, 0); - - return CMD_SUCCESS; -} - -DEFUN(cfg_bts_si5_neigh, cfg_bts_si5_neigh_cmd, - "si5 neighbor-list (add|del) arfcn <0-1024>", - "SI5 Neighbor List\n" "Add to manual SI5 neighbor list\n" - "Delete from SI5 manual neighbor list\n" "ARFCN of neighbor\n" - "ARFCN of neighbor\n") -{ - struct gsm_bts *bts = vty->index; - struct bitvec *bv = &bts->si_common.si5_neigh_list; - uint16_t arfcn = atoi(argv[1]); - - if (!bts->neigh_list_manual_mode) { - vty_out(vty, "%% Cannot configure neighbor list in " - "automatic mode%s", VTY_NEWLINE); - return CMD_WARNING; - } - - if (!strcmp(argv[0], "add")) - bitvec_set_bit_pos(bv, arfcn, 1); - else - bitvec_set_bit_pos(bv, arfcn, 0); - - return CMD_SUCCESS; -} - -#define TRX_TEXT "Radio Transceiver\n" - -/* per TRX configuration */ -DEFUN(cfg_trx, - cfg_trx_cmd, - "trx TRX_NR", - 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-1024>", - "Set the ARFCN for this TRX\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 dB\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 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)", - "E1 interface 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>", - "Set the TEI to be used for RSL") -{ - 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)", - "Turn off RF of the TRX.\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") -{ - 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", - "Physical Channel configuration (TCH/SDCCH/...)") -{ - 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; -} - -#define HOPPING_STR "Configure frequency hopping\n" - -DEFUN(cfg_ts_hopping, - cfg_ts_hopping_cmd, - "hopping enabled (0|1)", - HOPPING_STR "Enable or disable frequency hopping\n" - "Disable frequency hopping\n" "Enable frequency hopping\n") -{ - struct gsm_bts_trx_ts *ts = vty->index; - int enabled = atoi(argv[0]); - - if (enabled && !gsm_bts_has_feature(ts->trx->bts, BTS_FEAT_HOPPING)) { - vty_out(vty, "BTS model does not support hopping%s", - VTY_NEWLINE); - return CMD_WARNING; - } - - ts->hopping.enabled = enabled; - - return CMD_SUCCESS; -} - -DEFUN(cfg_ts_hsn, - cfg_ts_hsn_cmd, - "hopping sequence-number <0-63>", - HOPPING_STR - "Which hopping sequence to use for this channel") -{ - 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") -{ - 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 sub-slot connected to this on-air timeslot") -{ - 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", - counter_get(net->stats.chreq.total), - counter_get(net->stats.chreq.no_channel), VTY_NEWLINE); - vty_out(vty, "Channel Failures : %lu rf_failures, %lu rll failures%s", - counter_get(net->stats.chan.rf_fail), - counter_get(net->stats.chan.rll_err), VTY_NEWLINE); - vty_out(vty, "Paging : %lu attempted, %lu complete, %lu expired%s", - counter_get(net->stats.paging.attempted), - counter_get(net->stats.paging.completed), - counter_get(net->stats.paging.expired), VTY_NEWLINE); - vty_out(vty, "BTS failures : %lu OML, %lu RSL%s", - counter_get(net->stats.bts.oml_fail), - counter_get(net->stats.bts.rsl_fail), VTY_NEWLINE); -} - -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 log_target *tgt = osmo_log_vty2tgt(vty); - - if (!tgt) - return CMD_WARNING; - - log_set_imsi_filter(tgt, argv[0]); - return CMD_SUCCESS; -} - - -DEFUN(drop_bts, - drop_bts_cmd, - "drop bts connection <0-65535> (oml|rsl)", - "Debug/Simulation command to drop ipaccess BTS\n" - "BTS NR\n" "Connection Type\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(pdch_act, pdch_act_cmd, - "bts <0-255> trx <0-255> timeslot <0-7> pdch (activate|deactivate)", - "BTS related commands\n" "BTS Number\n" "Transceiver\n" "Transceiver Number\n" - "TRX Timeslot\n" "Timeslot Number\n" "Packet Data Channel\n" - "Activate Dynamic PDCH/TCH (-> PDCH mode)\n" - "Deactivate Dynamic PDCH/TCH (-> TCH mode)\n") -{ - struct gsm_bts *bts; - struct gsm_bts_trx *trx; - struct gsm_bts_trx_ts *ts; - int bts_nr = atoi(argv[0]); - int trx_nr = atoi(argv[1]); - int ts_nr = atoi(argv[2]); - int activate; - - bts = gsm_bts_num(bsc_gsmnet, bts_nr); - if (!bts) { - vty_out(vty, "%% No such BTS (%d)%s", bts_nr, VTY_NEWLINE); - return CMD_WARNING; - } - - if (!is_ipaccess_bts(bts)) { - vty_out(vty, "%% This command only works for ipaccess BTS%s", - VTY_NEWLINE); - return CMD_WARNING; - } - - trx = gsm_bts_trx_num(bts, trx_nr); - if (!trx) { - vty_out(vty, "%% No such TRX (%d)%s", trx_nr, VTY_NEWLINE); - return CMD_WARNING; - } - - ts = &trx->ts[ts_nr]; - if (ts->pchan != GSM_PCHAN_TCH_F_PDCH) { - vty_out(vty, "%% Timeslot %u is not in dynamic TCH_F/PDCH " - "mode%s", ts_nr, VTY_NEWLINE); - return CMD_WARNING; - } - - if (!strcmp(argv[3], "activate")) - activate = 1; - else - activate = 0; - - rsl_ipacc_pdch_activate(ts, activate); - - return CMD_SUCCESS; - -} - -extern int bsc_vty_init_extra(void); -extern const char *openbsc_copyright; - -int bsc_vty_init(void) -{ - install_element_ve(&show_net_cmd); - install_element_ve(&show_bts_cmd); - install_element_ve(&show_trx_cmd); - install_element_ve(&show_ts_cmd); - install_element_ve(&show_lchan_cmd); - install_element_ve(&show_lchan_summary_cmd); - install_element_ve(&logging_fltr_imsi_cmd); - - install_element_ve(&show_e1drv_cmd); - install_element_ve(&show_e1line_cmd); - install_element_ve(&show_e1ts_cmd); - - install_element_ve(&show_paging_cmd); - - logging_vty_add_cmds(); - install_element(CFG_LOG_NODE, &logging_fltr_imsi_cmd); - - install_element(CONFIG_NODE, &cfg_net_cmd); - install_node(&net_node, config_write_net); - install_default(GSMNET_NODE); - install_element(GSMNET_NODE, &ournode_exit_cmd); - install_element(GSMNET_NODE, &ournode_end_cmd); - install_element(GSMNET_NODE, &cfg_net_ncc_cmd); - install_element(GSMNET_NODE, &cfg_net_mnc_cmd); - install_element(GSMNET_NODE, &cfg_net_name_short_cmd); - install_element(GSMNET_NODE, &cfg_net_name_long_cmd); - install_element(GSMNET_NODE, &cfg_net_auth_policy_cmd); - install_element(GSMNET_NODE, &cfg_net_reject_cause_cmd); - install_element(GSMNET_NODE, &cfg_net_encryption_cmd); - install_element(GSMNET_NODE, &cfg_net_neci_cmd); - install_element(GSMNET_NODE, &cfg_net_rrlp_mode_cmd); - install_element(GSMNET_NODE, &cfg_net_mm_info_cmd); - install_element(GSMNET_NODE, &cfg_net_handover_cmd); - install_element(GSMNET_NODE, &cfg_net_ho_win_rxlev_avg_cmd); - install_element(GSMNET_NODE, &cfg_net_ho_win_rxqual_avg_cmd); - install_element(GSMNET_NODE, &cfg_net_ho_win_rxlev_avg_neigh_cmd); - install_element(GSMNET_NODE, &cfg_net_ho_pwr_interval_cmd); - install_element(GSMNET_NODE, &cfg_net_ho_pwr_hysteresis_cmd); - install_element(GSMNET_NODE, &cfg_net_ho_max_distance_cmd); - install_element(GSMNET_NODE, &cfg_net_T3101_cmd); - install_element(GSMNET_NODE, &cfg_net_T3103_cmd); - install_element(GSMNET_NODE, &cfg_net_T3105_cmd); - install_element(GSMNET_NODE, &cfg_net_T3107_cmd); - install_element(GSMNET_NODE, &cfg_net_T3109_cmd); - install_element(GSMNET_NODE, &cfg_net_T3111_cmd); - install_element(GSMNET_NODE, &cfg_net_T3113_cmd); - install_element(GSMNET_NODE, &cfg_net_T3115_cmd); - install_element(GSMNET_NODE, &cfg_net_T3117_cmd); - install_element(GSMNET_NODE, &cfg_net_T3119_cmd); - install_element(GSMNET_NODE, &cfg_net_T3122_cmd); - install_element(GSMNET_NODE, &cfg_net_T3141_cmd); - install_element(GSMNET_NODE, &cfg_net_dtx_cmd); - install_element(GSMNET_NODE, &cfg_net_subscr_keep_cmd); - install_element(GSMNET_NODE, &cfg_net_pag_any_tch_cmd); - - install_element(GSMNET_NODE, &cfg_bts_cmd); - install_node(&bts_node, config_write_bts); - install_default(BTS_NODE); - install_element(BTS_NODE, &ournode_exit_cmd); - install_element(BTS_NODE, &ournode_end_cmd); - install_element(BTS_NODE, &cfg_bts_type_cmd); - install_element(BTS_NODE, &cfg_description_cmd); - install_element(BTS_NODE, &cfg_no_description_cmd); - install_element(BTS_NODE, &cfg_bts_band_cmd); - install_element(BTS_NODE, &cfg_bts_ci_cmd); - install_element(BTS_NODE, &cfg_bts_lac_cmd); - install_element(BTS_NODE, &cfg_bts_tsc_cmd); - install_element(BTS_NODE, &cfg_bts_bsic_cmd); - install_element(BTS_NODE, &cfg_bts_unit_id_cmd); - install_element(BTS_NODE, &cfg_bts_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_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_ms_max_power_cmd); - install_element(BTS_NODE, &cfg_bts_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_gprs_mode_cmd); - install_element(BTS_NODE, &cfg_bts_gprs_ns_timer_cmd); - install_element(BTS_NODE, &cfg_bts_gprs_rac_cmd); - install_element(BTS_NODE, &cfg_bts_gprs_bvci_cmd); - install_element(BTS_NODE, &cfg_bts_gprs_cell_timer_cmd); - install_element(BTS_NODE, &cfg_bts_gprs_nsei_cmd); - install_element(BTS_NODE, &cfg_bts_gprs_nsvci_cmd); - install_element(BTS_NODE, &cfg_bts_gprs_nsvc_lport_cmd); - install_element(BTS_NODE, &cfg_bts_gprs_nsvc_rport_cmd); - install_element(BTS_NODE, &cfg_bts_gprs_nsvc_rip_cmd); - install_element(BTS_NODE, &cfg_bts_pag_free_cmd); - install_element(BTS_NODE, &cfg_bts_si_mode_cmd); - install_element(BTS_NODE, &cfg_bts_si_static_cmd); - install_element(BTS_NODE, &cfg_bts_neigh_mode_cmd); - install_element(BTS_NODE, &cfg_bts_neigh_cmd); - install_element(BTS_NODE, &cfg_bts_si5_neigh_cmd); - - install_element(BTS_NODE, &cfg_trx_cmd); - install_node(&trx_node, dummy_config_write); - install_default(TRX_NODE); - install_element(TRX_NODE, &ournode_exit_cmd); - install_element(TRX_NODE, &ournode_end_cmd); - 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); - install_default(TS_NODE); - install_element(TS_NODE, &ournode_exit_cmd); - install_element(TS_NODE, &ournode_end_cmd); - install_element(TS_NODE, &cfg_ts_pchan_cmd); - install_element(TS_NODE, &cfg_ts_hopping_cmd); - install_element(TS_NODE, &cfg_ts_hsn_cmd); - install_element(TS_NODE, &cfg_ts_maio_cmd); - install_element(TS_NODE, &cfg_ts_arfcn_add_cmd); - install_element(TS_NODE, &cfg_ts_arfcn_del_cmd); - install_element(TS_NODE, &cfg_ts_e1_subslot_cmd); - - install_element(ENABLE_NODE, &drop_bts_cmd); - install_element(ENABLE_NODE, &pdch_act_cmd); - - abis_nm_vty_init(); - abis_om2k_vty_init(); - e1inp_vty_init(); - - bsc_vty_init_extra(); - - return 0; -} diff --git a/openbsc/src/bsc/bts_ericsson_rbs2000.c b/openbsc/src/bsc/bts_ericsson_rbs2000.c deleted file mode 100644 index def42b544..000000000 --- a/openbsc/src/bsc/bts_ericsson_rbs2000.c +++ /dev/null @@ -1,164 +0,0 @@ -/* Ericsson RBS-2xxx specific code */ - -/* (C) 2011 by Harald Welte - * - * All Rights Reserved - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation; either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - * - */ - -#include - -#include - -#include -#include -#include -#include -#include - -#include "../abis/input/lapd.h" - -static void bootstrap_om_bts(struct gsm_bts *bts) -{ - LOGP(DNM, LOGL_NOTICE, "bootstrapping OML for BTS %u\n", bts->nr); - abis_om2k_tx_start_req(bts, &om2k_mo_cf); - /* FIXME */ -} - -static void bootstrap_om_trx(struct gsm_bts_trx *trx) -{ - 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 (start) - lapd_sap_start(ts->driver.dahdi.lapd, link->tei, link->sapi); - else - lapd_sap_stop(ts->driver.dahdi.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_GLOBAL) - return 0; - - switch (signal) { - case S_GLOBAL_BTS_CLOSE_OM: - bts = signal_data; - if (bts->type == GSM_BTS_TYPE_RBS2000) - shutdown_om(signal_data); - break; - } - - return 0; -} - -/* Callback function to be called every time we receive a signal from INPUT */ -static int inp_sig_cb(unsigned int subsys, unsigned int signal, - void *handler_data, void *signal_data) -{ - struct input_signal_data *isd = signal_data; - - if (subsys != SS_INPUT) - return 0; - - switch (signal) { - case S_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_INP_LINE_INIT: - /* Right now Ericsson RBS are only supported on DAHDI */ - if (strcasecmp(isd->line->driver->name, "DAHDI")) - break; - start_sabm_in_line(isd->line, 1); - break; - case S_INP_LINE_ALARM: - if (strcasecmp(isd->line->driver->name, "DAHDI")) - break; - start_sabm_in_line(isd->line, 0); - break; - case S_INP_LINE_NOALARM: - if (strcasecmp(isd->line->driver->name, "DAHDI")) - break; - start_sabm_in_line(isd->line, 1); - break; - } - - return 0; -} - -static void config_write_bts(struct vty *vty, struct gsm_bts *bts) -{ - abis_om2k_config_write_bts(vty, bts); -} - -static struct gsm_bts_model model_rbs2k = { - .type = GSM_BTS_TYPE_RBS2000, - .name = "rbs2000", - .oml_rcvmsg = &abis_om2k_rcvmsg, - .config_write_bts = &config_write_bts, -}; - -int bts_model_rbs2k_init(void) -{ - model_rbs2k.features.data = &model_rbs2k._features_data[0]; - model_rbs2k.features.data_len = sizeof(model_rbs2k._features_data); - - gsm_btsmodel_set_feature(&model_rbs2k, BTS_FEAT_HOPPING); - gsm_btsmodel_set_feature(&model_rbs2k, BTS_FEAT_HSCSD); - - register_signal_handler(SS_INPUT, inp_sig_cb, NULL); - register_signal_handler(SS_GLOBAL, gbl_sig_cb, NULL); - - return gsm_bts_model_register(&model_rbs2k); -} diff --git a/openbsc/src/bsc/bts_ipaccess_nanobts.c b/openbsc/src/bsc/bts_ipaccess_nanobts.c deleted file mode 100644 index 25dc0c8a2..000000000 --- a/openbsc/src/bsc/bts_ipaccess_nanobts.c +++ /dev/null @@ -1,447 +0,0 @@ -/* ip.access nanoBTS specific code */ - -/* (C) 2009-2010 by Harald Welte - * - * All Rights Reserved - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation; either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - * - */ - -#include -#include - -#include - -#include -#include -#include - -static struct gsm_bts_model model_nanobts = { - .type = GSM_BTS_TYPE_NANOBTS, - .name = "nanobts", - .oml_rcvmsg = &abis_nm_rcvmsg, - .nm_att_tlvdef = { - .def = { - /* ip.access specifics */ - [NM_ATT_IPACC_DST_IP] = { TLV_TYPE_FIXED, 4 }, - [NM_ATT_IPACC_DST_IP_PORT] = { TLV_TYPE_FIXED, 2 }, - [NM_ATT_IPACC_STREAM_ID] = { TLV_TYPE_TV, }, - [NM_ATT_IPACC_SEC_OML_CFG] = { TLV_TYPE_FIXED, 6 }, - [NM_ATT_IPACC_IP_IF_CFG] = { TLV_TYPE_FIXED, 8 }, - [NM_ATT_IPACC_IP_GW_CFG] = { TLV_TYPE_FIXED, 12 }, - [NM_ATT_IPACC_IN_SERV_TIME] = { TLV_TYPE_FIXED, 4 }, - [NM_ATT_IPACC_LOCATION] = { TLV_TYPE_TL16V }, - [NM_ATT_IPACC_PAGING_CFG] = { TLV_TYPE_FIXED, 2 }, - [NM_ATT_IPACC_UNIT_ID] = { TLV_TYPE_TL16V }, - [NM_ATT_IPACC_UNIT_NAME] = { TLV_TYPE_TL16V }, - [NM_ATT_IPACC_SNMP_CFG] = { TLV_TYPE_TL16V }, - [NM_ATT_IPACC_PRIM_OML_CFG_LIST] = { TLV_TYPE_TL16V }, - [NM_ATT_IPACC_NV_FLAGS] = { TLV_TYPE_TL16V }, - [NM_ATT_IPACC_FREQ_CTRL] = { TLV_TYPE_FIXED, 2 }, - [NM_ATT_IPACC_PRIM_OML_FB_TOUT] = { TLV_TYPE_TL16V }, - [NM_ATT_IPACC_CUR_SW_CFG] = { TLV_TYPE_TL16V }, - [NM_ATT_IPACC_TIMING_BUS] = { TLV_TYPE_TL16V }, - [NM_ATT_IPACC_CGI] = { TLV_TYPE_TL16V }, - [NM_ATT_IPACC_RAC] = { TLV_TYPE_TL16V }, - [NM_ATT_IPACC_OBJ_VERSION] = { TLV_TYPE_TL16V }, - [NM_ATT_IPACC_GPRS_PAGING_CFG]= { TLV_TYPE_TL16V }, - [NM_ATT_IPACC_NSEI] = { TLV_TYPE_TL16V }, - [NM_ATT_IPACC_BVCI] = { TLV_TYPE_TL16V }, - [NM_ATT_IPACC_NSVCI] = { TLV_TYPE_TL16V }, - [NM_ATT_IPACC_NS_CFG] = { TLV_TYPE_TL16V }, - [NM_ATT_IPACC_BSSGP_CFG] = { TLV_TYPE_TL16V }, - [NM_ATT_IPACC_NS_LINK_CFG] = { TLV_TYPE_TL16V }, - [NM_ATT_IPACC_RLC_CFG] = { TLV_TYPE_TL16V }, - [NM_ATT_IPACC_ALM_THRESH_LIST]= { TLV_TYPE_TL16V }, - [NM_ATT_IPACC_MONIT_VAL_LIST] = { TLV_TYPE_TL16V }, - [NM_ATT_IPACC_TIB_CONTROL] = { TLV_TYPE_TL16V }, - [NM_ATT_IPACC_SUPP_FEATURES] = { TLV_TYPE_TL16V }, - [NM_ATT_IPACC_CODING_SCHEMES] = { TLV_TYPE_TL16V }, - [NM_ATT_IPACC_RLC_CFG_2] = { TLV_TYPE_TL16V }, - [NM_ATT_IPACC_HEARTB_TOUT] = { TLV_TYPE_TL16V }, - [NM_ATT_IPACC_UPTIME] = { TLV_TYPE_TL16V }, - [NM_ATT_IPACC_RLC_CFG_3] = { TLV_TYPE_TL16V }, - [NM_ATT_IPACC_SSL_CFG] = { TLV_TYPE_TL16V }, - [NM_ATT_IPACC_SEC_POSSIBLE] = { TLV_TYPE_TL16V }, - [NM_ATT_IPACC_IML_SSL_STATE] = { TLV_TYPE_TL16V }, - [NM_ATT_IPACC_REVOC_DATE] = { TLV_TYPE_TL16V }, - }, - }, -}; - -static unsigned char nanobts_attr_bts[] = { - NM_ATT_INTERF_BOUND, 0x55, 0x5b, 0x61, 0x67, 0x6d, 0x73, - /* interference avg. period in numbers of SACCH multifr */ - NM_ATT_INTAVE_PARAM, 0x06, - /* conn fail based on SACCH error rate */ - NM_ATT_CONN_FAIL_CRIT, 0x00, 0x02, 0x01, 0x10, - NM_ATT_T200, 0x1e, 0x24, 0x24, 0xa8, 0x34, 0x21, 0xa8, - NM_ATT_MAX_TA, 0x3f, - NM_ATT_OVERL_PERIOD, 0x00, 0x01, 10, /* seconds */ - NM_ATT_CCCH_L_T, 10, /* percent */ - NM_ATT_CCCH_L_I_P, 1, /* seconds */ - NM_ATT_RACH_B_THRESH, 10, /* busy threshold in - dBm */ - NM_ATT_LDAVG_SLOTS, 0x03, 0xe8, /* rach load averaging 1000 slots */ - NM_ATT_BTS_AIR_TIMER, 128, /* miliseconds */ - NM_ATT_NY1, 10, /* 10 retransmissions of physical config */ - NM_ATT_BCCH_ARFCN, HARDCODED_ARFCN >> 8, HARDCODED_ARFCN & 0xff, - NM_ATT_BSIC, HARDCODED_BSIC, - NM_ATT_IPACC_CGI, 0, 7, 0x00, 0xf1, 0x10, 0x00, 0x01, 0x00, 0x00, -}; - -static unsigned char nanobts_attr_radio[] = { - NM_ATT_RF_MAXPOWR_R, 0x0c, /* number of -2dB reduction steps / Pn */ - NM_ATT_ARFCN_LIST, 0x00, 0x02, HARDCODED_ARFCN >> 8, HARDCODED_ARFCN & 0xff, -}; - -static unsigned char nanobts_attr_nse[] = { - NM_ATT_IPACC_NSEI, 0, 2, 0x03, 0x9d, /* NSEI 925 */ - NM_ATT_IPACC_NS_CFG, 0, 7, 3, /* (un)blocking timer (Tns-block) */ - 3, /* (un)blocking retries */ - 3, /* reset timer (Tns-reset) */ - 3, /* reset retries */ - 30, /* test timer (Tns-test) */ - 3, /* alive timer (Tns-alive) */ - 10, /* alive retrires */ - NM_ATT_IPACC_BSSGP_CFG, 0, 11, - 3, /* blockimg timer (T1) */ - 3, /* blocking retries */ - 3, /* unblocking retries */ - 3, /* reset timer */ - 3, /* reset retries */ - 10, /* suspend timer (T3) in 100ms */ - 3, /* suspend retries */ - 10, /* resume timer (T4) in 100ms */ - 3, /* resume retries */ - 10, /* capability update timer (T5) */ - 3, /* capability update retries */ -}; - -static unsigned char nanobts_attr_cell[] = { - NM_ATT_IPACC_RAC, 0, 1, 1, /* routing area code */ - NM_ATT_IPACC_GPRS_PAGING_CFG, 0, 2, - 5, /* repeat time (50ms) */ - 3, /* repeat count */ - NM_ATT_IPACC_BVCI, 0, 2, 0x03, 0x9d, /* BVCI 925 */ - NM_ATT_IPACC_RLC_CFG, 0, 9, - 20, /* T3142 */ - 5, /* T3169 */ - 5, /* T3191 */ - 200, /* T3193 */ - 5, /* T3195 */ - 10, /* N3101 */ - 4, /* N3103 */ - 8, /* N3105 */ - 15, /* RLC CV countdown */ - NM_ATT_IPACC_CODING_SCHEMES, 0, 2, 0x0f, 0x00, /* CS1..CS4 */ - NM_ATT_IPACC_RLC_CFG_2, 0, 5, - 0x00, 250, /* T downlink TBF extension (0..500) */ - 0x00, 250, /* T uplink TBF extension (0..500) */ - 2, /* CS2 */ -#if 0 - /* EDGE model only, breaks older models. - * Should inquire the BTS capabilities */ - NM_ATT_IPACC_RLC_CFG_3, 0, 1, - 2, /* MCS2 */ -#endif -}; - -static unsigned char nanobts_attr_nsvc0[] = { - NM_ATT_IPACC_NSVCI, 0, 2, 0x03, 0x9d, /* 925 */ - NM_ATT_IPACC_NS_LINK_CFG, 0, 8, - 0x59, 0xd8, /* remote udp port (23000) */ - 192, 168, 100, 11, /* remote ip address */ - 0x59, 0xd8, /* local udp port (23000) */ -}; - -static void patch_16(uint8_t *data, const uint16_t val) -{ - memcpy(data, &val, sizeof(val)); -} - -static void patch_32(uint8_t *data, const uint32_t val) -{ - memcpy(data, &val, sizeof(val)); -} - -/* - * Patch the various SYSTEM INFORMATION tables to update - * the LAI - */ -static void patch_nm_tables(struct gsm_bts *bts) -{ - u_int8_t arfcn_low = bts->c0->arfcn & 0xff; - u_int8_t arfcn_high = (bts->c0->arfcn >> 8) & 0x0f; - - /* patch ARFCN into BTS Attributes */ - nanobts_attr_bts[42] &= 0xf0; - nanobts_attr_bts[42] |= arfcn_high; - nanobts_attr_bts[43] = arfcn_low; - - /* patch the RACH attributes */ - if (bts->rach_b_thresh != -1) { - nanobts_attr_bts[33] = bts->rach_b_thresh & 0xff; - } - - if (bts->rach_ldavg_slots != -1) { - u_int8_t avg_high = bts->rach_ldavg_slots & 0xff; - u_int8_t avg_low = (bts->rach_ldavg_slots >> 8) & 0x0f; - - nanobts_attr_bts[35] = avg_high; - nanobts_attr_bts[36] = avg_low; - } - - /* patch BSIC */ - nanobts_attr_bts[sizeof(nanobts_attr_bts)-11] = bts->bsic; - - /* patch CGI */ - abis_nm_ipaccess_cgi(nanobts_attr_bts+sizeof(nanobts_attr_bts)-7, bts); - - /* patch the power reduction */ - nanobts_attr_radio[1] = bts->c0->max_power_red / 2; - - /* patch NSEI */ - nanobts_attr_nse[3] = bts->gprs.nse.nsei >> 8; - nanobts_attr_nse[4] = bts->gprs.nse.nsei & 0xff; - memcpy(nanobts_attr_nse+8, bts->gprs.nse.timer, - ARRAY_SIZE(bts->gprs.nse.timer)); - memcpy(nanobts_attr_nse+18, bts->gprs.cell.timer, - ARRAY_SIZE(bts->gprs.cell.timer)); - - /* patch NSVCI */ - nanobts_attr_nsvc0[3] = bts->gprs.nsvc[0].nsvci >> 8; - nanobts_attr_nsvc0[4] = bts->gprs.nsvc[0].nsvci & 0xff; - - /* patch IP address as SGSN IP */ - patch_16(nanobts_attr_nsvc0 + 8, - htons(bts->gprs.nsvc[0].remote_port)); - patch_32(nanobts_attr_nsvc0 + 10, - htonl(bts->gprs.nsvc[0].remote_ip)); - patch_16(nanobts_attr_nsvc0 + 14, - htons(bts->gprs.nsvc[0].local_port)); - - /* patch BVCI */ - nanobts_attr_cell[12] = bts->gprs.cell.bvci >> 8; - nanobts_attr_cell[13] = bts->gprs.cell.bvci & 0xff; - /* patch RAC */ - nanobts_attr_cell[3] = bts->gprs.rac; - - if (bts->gprs.mode == BTS_GPRS_EGPRS) { - /* patch EGPRS coding schemes MCS 1..9 */ - nanobts_attr_cell[29] = 0x8f; - nanobts_attr_cell[30] = 0xff; - } -} - - -/* 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) -{ - u_int8_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; - - /* 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) { - patch_nm_tables(bts); - abis_nm_set_bts_attr(bts, nanobts_attr_bts, - sizeof(nanobts_attr_bts)); - 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) { - patch_nm_tables(trx->bts); - enum abis_nm_chan_comb ccomb = - abis_nm_chcomb4pchan(ts->pchan); - abis_nm_set_channel_attr(ts, ccomb); - abis_nm_chg_adm_state(trx->bts, obj_class, - trx->bts->bts_nr, trx->nr, ts->nr, - NM_STATE_UNLOCKED); - abis_nm_opstart(trx->bts, obj_class, - trx->bts->bts_nr, trx->nr, ts->nr); - } - break; - case NM_OC_RADIO_CARRIER: - trx = obj; - if (new_state->operational == NM_OPSTATE_DISABLED && - new_state->availability == NM_AVSTATE_OK) - abis_nm_opstart(trx->bts, obj_class, trx->bts->bts_nr, - trx->nr, 0xff); - break; - case NM_OC_GPRS_NSE: - bts = container_of(obj, struct gsm_bts, gprs.nse); - if (bts->gprs.mode == BTS_GPRS_NONE) - break; - if (new_state->availability == NM_AVSTATE_DEPENDENCY) { - abis_nm_ipaccess_set_attr(bts, obj_class, bts->bts_nr, - 0xff, 0xff, nanobts_attr_nse, - sizeof(nanobts_attr_nse)); - 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) { - abis_nm_ipaccess_set_attr(bts, obj_class, bts->bts_nr, - 0, 0xff, nanobts_attr_cell, - sizeof(nanobts_attr_cell)); - 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) { - abis_nm_ipaccess_set_attr(bts, obj_class, bts->bts_nr, - nsvc->id, 0xff, - nanobts_attr_nsvc0, - sizeof(nanobts_attr_nsvc0)); - 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 gsm_bts *bts = mb->trx->bts; - struct gsm_bts_trx *trx = gsm_bts_trx_num(bts, foh->obj_inst.trx_nr); - - if (!trx) - return -EINVAL; - - if (trx->bts->type != GSM_BTS_TYPE_NANOBTS) - 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, 0, 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->nm_state.administrative; - /* Patch ARFCN into radio attribute */ - nanobts_attr_radio[5] &= 0xf0; - nanobts_attr_radio[5] |= trx->arfcn >> 8; - nanobts_attr_radio[6] = trx->arfcn & 0xff; - abis_nm_set_radio_attr(trx, nanobts_attr_radio, - sizeof(nanobts_attr_radio)); - 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 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; -} - -int bts_model_nanobts_init(void) -{ - model_nanobts.features.data = &model_nanobts._features_data[0]; - model_nanobts.features.data_len = sizeof(model_nanobts._features_data); - - gsm_btsmodel_set_feature(&model_nanobts, BTS_FEAT_GPRS); - gsm_btsmodel_set_feature(&model_nanobts, BTS_FEAT_EGPRS); - - register_signal_handler(SS_NM, nm_sig_cb, NULL); - - return gsm_bts_model_register(&model_nanobts); -} diff --git a/openbsc/src/bsc/bts_siemens_bs11.c b/openbsc/src/bsc/bts_siemens_bs11.c deleted file mode 100644 index 5a5f88306..000000000 --- a/openbsc/src/bsc/bts_siemens_bs11.c +++ /dev/null @@ -1,591 +0,0 @@ -/* Siemens BS-11 specific code */ - -/* (C) 2009-2010 by Harald Welte - * - * All Rights Reserved - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation; either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - * - */ - -#include - -#include - -#include -#include -#include -#include -#include - -static struct gsm_bts_model model_bs11 = { - .type = GSM_BTS_TYPE_BS11, - .name = "bs11", - .oml_rcvmsg = &abis_nm_rcvmsg, - .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) -{ - u_int8_t arfcn_low = bts->c0->arfcn & 0xff; - u_int8_t arfcn_high = (bts->c0->arfcn >> 8) & 0x0f; - - /* 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) { - u_int8_t avg_high = bts->rach_ldavg_slots & 0xff; - u_int8_t avg_low = (bts->rach_ldavg_slots >> 8) & 0x0f; - - bs11_attr_bts[35] = avg_high; - bs11_attr_bts[36] = avg_low; - } - - /* patch BSIC */ - bs11_attr_bts[1] = bts->bsic; - - /* patch the power reduction */ - bs11_attr_radio[5] = bts->c0->max_power_red / 2; -} - - -static void nm_reconfig_ts(struct gsm_bts_trx_ts *ts) -{ - enum abis_nm_chan_comb ccomb = abis_nm_chcomb4pchan(ts->pchan); - struct gsm_e1_subslot *e1l = &ts->e1_link; - - abis_nm_set_channel_attr(ts, ccomb); - - if (is_ipaccess_bts(ts->trx->bts)) - return; - - switch (ts->pchan) { - case GSM_PCHAN_TCH_F: - case GSM_PCHAN_TCH_H: - abis_nm_conn_terr_traf(ts, e1l->e1_nr, e1l->e1_ts, - e1l->e1_ts_ss); - break; - default: - break; - } -} - -static void nm_reconfig_trx(struct gsm_bts_trx *trx) -{ - struct gsm_e1_subslot *e1l = &trx->rsl_e1_link; - int i; - - patch_nm_tables(trx->bts); - - switch (trx->bts->type) { - case GSM_BTS_TYPE_BS11: - /* FIXME: discover this by fetching an attribute */ -#if 0 - trx->nominal_power = 15; /* 15dBm == 30mW PA configuration */ -#else - trx->nominal_power = 24; /* 24dBm == 250mW PA configuration */ -#endif - abis_nm_conn_terr_sign(trx, e1l->e1_nr, e1l->e1_ts, - e1l->e1_ts_ss); - abis_nm_establish_tei(trx->bts, trx->nr, e1l->e1_nr, - e1l->e1_ts, e1l->e1_ts_ss, trx->rsl_tei); - - /* Set Radio Attributes */ - if (trx == trx->bts->c0) - abis_nm_set_radio_attr(trx, bs11_attr_radio, - sizeof(bs11_attr_radio)); - else { - u_int8_t trx1_attr_radio[sizeof(bs11_attr_radio)]; - u_int8_t arfcn_low = trx->arfcn & 0xff; - u_int8_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_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_INPUT) - return 0; - - switch (signal) { - case S_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; -} - -int bts_model_bs11_init(void) -{ - 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); - - register_signal_handler(SS_INPUT, inp_sig_cb, NULL); - register_signal_handler(SS_GLOBAL, gbl_sig_cb, NULL); - - return gsm_bts_model_register(&model_bs11); -} diff --git a/openbsc/src/bsc/bts_unknown.c b/openbsc/src/bsc/bts_unknown.c deleted file mode 100644 index f95459959..000000000 --- a/openbsc/src/bsc/bts_unknown.c +++ /dev/null @@ -1,41 +0,0 @@ -/* Generic BTS - VTY code tries to allocate this BTS before type is known */ - -/* (C) 2010 by Daniel Willmann - * - * All Rights Reserved - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation; either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - * - */ - -#include - -#include -#include -#include - -static struct gsm_bts_model model_unknown = { - .type = GSM_BTS_TYPE_UNKNOWN, - .name = "unknown", - .oml_rcvmsg = &abis_nm_rcvmsg, - .nm_att_tlvdef = { - .def = { - }, - }, -}; - -int bts_model_unknown_init(void) -{ - return gsm_bts_model_register(&model_unknown); -} diff --git a/openbsc/src/bsc/chan_alloc.c b/openbsc/src/bsc/chan_alloc.c deleted file mode 100644 index 167381b37..000000000 --- a/openbsc/src/bsc/chan_alloc.c +++ /dev/null @@ -1,507 +0,0 @@ -/* GSM Channel allocation routines - * - * (C) 2008 by Harald Welte - * (C) 2008, 2009 by Holger Hans Peter Freyther - * - * All Rights Reserved - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation; either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - * - */ - -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include - -#include - -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->nm_state)) - return 0; - } - - return 1; -} - -int trx_is_usable(struct gsm_bts_trx *trx) -{ - /* FIXME: How does this behave for BS-11 ? */ - if (is_ipaccess_bts(trx->bts)) { - if (!nm_is_running(&trx->nm_state) || - !nm_is_running(&trx->bb_transc.nm_state)) - return 0; - } - - return 1; -} - -struct gsm_bts_trx_ts *ts_c0_alloc(struct gsm_bts *bts, - enum gsm_phys_chan_config pchan) -{ - struct gsm_bts_trx *trx = bts->c0; - struct gsm_bts_trx_ts *ts = &trx->ts[0]; - - if (pchan != GSM_PCHAN_CCCH && - pchan != GSM_PCHAN_CCCH_SDCCH4) - return NULL; - - if (ts->pchan != GSM_PCHAN_NONE) - return NULL; - - ts->pchan = pchan; - - return ts; -} - -/* Allocate a physical channel (TS) */ -struct gsm_bts_trx_ts *ts_alloc(struct gsm_bts *bts, - enum gsm_phys_chan_config pchan) -{ - int j; - struct gsm_bts_trx *trx; - - llist_for_each_entry(trx, &bts->trx_list, list) { - int from, to; - - if (!trx_is_usable(trx)) - continue; - - /* the following constraints are pure policy, - * no requirement to put this restriction in place */ - if (trx == bts->c0) { - /* On the first TRX we run one CCCH and one SDCCH8 */ - switch (pchan) { - case GSM_PCHAN_CCCH: - case GSM_PCHAN_CCCH_SDCCH4: - from = 0; to = 0; - break; - case GSM_PCHAN_TCH_F: - case GSM_PCHAN_TCH_H: - from = 1; to = 7; - break; - case GSM_PCHAN_SDCCH8_SACCH8C: - default: - return NULL; - } - } else { - /* Every secondary TRX is configured for TCH/F - * and TCH/H only */ - switch (pchan) { - case GSM_PCHAN_SDCCH8_SACCH8C: - from = 1; to = 1; - case GSM_PCHAN_TCH_F: - case GSM_PCHAN_TCH_H: - from = 1; to = 7; - break; - default: - return NULL; - } - } - - for (j = from; j <= to; j++) { - struct gsm_bts_trx_ts *ts = &trx->ts[j]; - - if (!ts_is_usable(ts)) - continue; - - if (ts->pchan == GSM_PCHAN_NONE) { - ts->pchan = pchan; - /* set channel attribute on OML */ - abis_nm_set_channel_attr(ts, abis_nm_chcomb4pchan(pchan)); - return ts; - } - } - } - return NULL; -} - -/* Free a physical channel (TS) */ -void ts_free(struct gsm_bts_trx_ts *ts) -{ - ts->pchan = GSM_PCHAN_NONE; -} - -static const u_int8_t subslots_per_pchan[] = { - [GSM_PCHAN_NONE] = 0, - [GSM_PCHAN_CCCH] = 0, - [GSM_PCHAN_CCCH_SDCCH4] = 4, - [GSM_PCHAN_TCH_F] = 1, - [GSM_PCHAN_TCH_H] = 2, - [GSM_PCHAN_SDCCH8_SACCH8C] = 8, - /* FIXME: what about dynamic TCH_F_TCH_H ? */ - [GSM_PCHAN_TCH_F_PDCH] = 1, -}; - -static struct gsm_lchan * -_lc_find_trx(struct gsm_bts_trx *trx, enum gsm_phys_chan_config pchan) -{ - struct gsm_bts_trx_ts *ts; - int j, ss; - - if (!trx_is_usable(trx)) - return NULL; - - for (j = 0; j < 8; j++) { - ts = &trx->ts[j]; - if (!ts_is_usable(ts)) - continue; - /* ip.access dynamic TCH/F + PDCH combination */ - if (ts->pchan == GSM_PCHAN_TCH_F_PDCH && - pchan == GSM_PCHAN_TCH_F) { - /* we can only consider such a dynamic channel - * if the PDCH is currently inactive */ - if (ts->flags & TS_F_PDCH_MODE) - continue; - } else if (ts->pchan != pchan) - continue; - /* check if all sub-slots are allocated yet */ - for (ss = 0; ss < subslots_per_pchan[pchan]; ss++) { - struct gsm_lchan *lc = &ts->lchan[ss]; - if (lc->type == GSM_LCHAN_NONE && - lc->state == LCHAN_S_NONE) - return lc; - } - } - - return NULL; -} - -static struct gsm_lchan * -_lc_find_bts(struct gsm_bts *bts, enum gsm_phys_chan_config pchan) -{ - struct gsm_bts_trx *trx; - struct gsm_bts_trx_ts *ts; - struct gsm_lchan *lc; - - if (bts->chan_alloc_reverse) { - llist_for_each_entry_reverse(trx, &bts->trx_list, list) { - lc = _lc_find_trx(trx, pchan); - if (lc) - return lc; - } - } else { - llist_for_each_entry(trx, &bts->trx_list, list) { - lc = _lc_find_trx(trx, pchan); - if (lc) - return lc; - } - } - - /* we cannot allocate more of these */ - if (pchan == GSM_PCHAN_CCCH_SDCCH4) - return NULL; - - /* if we've reached here, we need to allocate a new physical - * channel for the logical channel type requested */ - ts = ts_alloc(bts, pchan); - if (!ts) { - /* no more radio resources */ - return NULL; - } - return &ts->lchan[0]; -} - -/* Allocate a logical channel */ -struct gsm_lchan *lchan_alloc(struct gsm_bts *bts, enum gsm_chan_t type, - int allow_bigger) -{ - struct gsm_lchan *lchan = NULL; - enum gsm_phys_chan_config first, second; - - switch (type) { - case GSM_LCHAN_SDCCH: - if (bts->chan_alloc_reverse) { - first = GSM_PCHAN_SDCCH8_SACCH8C; - second = GSM_PCHAN_CCCH_SDCCH4; - } else { - first = GSM_PCHAN_CCCH_SDCCH4; - second = GSM_PCHAN_SDCCH8_SACCH8C; - } - - lchan = _lc_find_bts(bts, first); - if (lchan == NULL) - lchan = _lc_find_bts(bts, second); - - /* allow to assign bigger channels */ - if (allow_bigger) { - if (lchan == NULL) { - lchan = _lc_find_bts(bts, GSM_PCHAN_TCH_H); - type = GSM_LCHAN_TCH_H; - } - - if (lchan == NULL) { - lchan = _lc_find_bts(bts, GSM_PCHAN_TCH_F); - type = GSM_LCHAN_TCH_F; - } - } - break; - case GSM_LCHAN_TCH_F: - lchan = _lc_find_bts(bts, GSM_PCHAN_TCH_F); - break; - case GSM_LCHAN_TCH_H: - lchan =_lc_find_bts(bts, GSM_PCHAN_TCH_H); - /* If we don't have TCH/H available, fall-back to TCH/F */ - if (!lchan) { - lchan = _lc_find_bts(bts, GSM_PCHAN_TCH_F); - type = GSM_LCHAN_TCH_F; - } - break; - default: - LOGP(DRLL, LOGL_ERROR, "Unknown gsm_chan_t %u\n", type); - } - - if (lchan) { - lchan->type = type; - - /* clear sapis */ - memset(lchan->sapis, 0, ARRAY_SIZE(lchan->sapis)); - - /* clear multi rate config */ - memset(&lchan->mr_conf, 0, sizeof(lchan->mr_conf)); - } else { - struct challoc_signal_data sig; - sig.bts = bts; - sig.type = type; - dispatch_signal(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; - dispatch_signal(SS_LCHAN, S_LCHAN_UNEXPECTED_RELEASE, &sig); - } - - - /* stop the timer */ - bsc_del_timer(&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; - dispatch_signal(SS_CHALLOC, S_CHALLOC_FREED, &sig); - - if (lchan->conn) { - LOGP(DRLL, LOGL_ERROR, "the subscriber connection should be gone.\n"); - lchan->conn = NULL; - } - - lchan->sach_deact = 0; - lchan->release_reason = 0; - - /* 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) -{ - bsc_del_timer(&lchan->T3101); - bsc_del_timer(&lchan->T3111); - bsc_del_timer(&lchan->error_timer); - - lchan->type = GSM_LCHAN_NONE; - lchan->state = LCHAN_S_NONE; -} - -/* release the next allocated SAPI or return 0 */ -static int _lchan_release_next_sapi(struct gsm_lchan *lchan) -{ - int sapi; - - for (sapi = 1; sapi < ARRAY_SIZE(lchan->sapis); ++sapi) { - u_int8_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, lchan->release_reason); - return 0; - } - - return 1; -} - -/* Drive the release process of the lchan */ -static void _lchan_handle_release(struct gsm_lchan *lchan) -{ - /* Ask for SAPI != 0 to be freed first and stop if we need to wait */ - if (_lchan_release_next_sapi(lchan) == 0) - return; - - if (lchan->sach_deact) { - gsm48_send_rr_release(lchan); - return; - } - - rsl_release_request(lchan, 0, lchan->release_reason); - rsl_lchan_set_state(lchan, LCHAN_S_REL_REQ); -} - -/* called from abis rsl */ -int rsl_lchan_rll_release(struct gsm_lchan *lchan, u_int8_t link_id) -{ - if (lchan->state != LCHAN_S_REL_REQ) - return -1; - - if ((link_id & 0x7) != 0) - _lchan_handle_release(lchan); - return 0; -} - -/* Consider releasing the channel now */ -int lchan_release(struct gsm_lchan *lchan, int sach_deact, int reason) -{ - DEBUGP(DRLL, "%s starting release sequence\n", gsm_lchan_name(lchan)); - rsl_lchan_set_state(lchan, LCHAN_S_REL_REQ); - - lchan->conn = NULL; - lchan->release_reason = reason; - lchan->sach_deact = sach_deact; - _lchan_handle_release(lchan); - return 1; -} - -static struct gsm_lchan* lchan_find(struct gsm_bts *bts, struct gsm_subscriber *subscr) { - struct gsm_bts_trx *trx; - int ts_no, lchan_no; - - llist_for_each_entry(trx, &bts->trx_list, list) { - for (ts_no = 0; ts_no < 8; ++ts_no) { - for (lchan_no = 0; lchan_no < TS_MAX_LCHAN; ++lchan_no) { - struct gsm_lchan *lchan = - &trx->ts[ts_no].lchan[lchan_no]; - if (lchan->conn && subscr == lchan->conn->subscr) - return lchan; - } - } - } - - return NULL; -} - -struct gsm_subscriber_connection *connection_for_subscr(struct gsm_subscriber *subscr) -{ - struct gsm_bts *bts; - struct gsm_network *net = subscr->net; - struct gsm_lchan *lchan; - - llist_for_each_entry(bts, &net->bts_list, list) { - lchan = lchan_find(bts, subscr); - if (lchan) - return lchan->conn; - } - - return NULL; -} - -void bts_chan_load(struct pchan_load *cl, const struct gsm_bts *bts) -{ - struct gsm_bts_trx *trx; - - llist_for_each_entry(trx, &bts->trx_list, list) { - int i; - - /* skip administratively deactivated tranxsceivers */ - if (!nm_is_running(&trx->nm_state) || - !nm_is_running(&trx->bb_transc.nm_state)) - continue; - - for (i = 0; i < ARRAY_SIZE(trx->ts); i++) { - struct gsm_bts_trx_ts *ts = &trx->ts[i]; - struct load_counter *pl = &cl->pchan[ts->pchan]; - int j; - - /* skip administratively deactivated timeslots */ - if (!nm_is_running(&ts->nm_state)) - continue; - - for (j = 0; j < subslots_per_pchan[ts->pchan]; j++) { - struct gsm_lchan *lchan = &ts->lchan[j]; - - pl->total++; - - switch (lchan->state) { - case LCHAN_S_NONE: - break; - default: - pl->used++; - break; - } - } - } - } -} - -void network_chan_load(struct pchan_load *pl, struct gsm_network *net) -{ - struct gsm_bts *bts; - - memset(pl, 0, sizeof(*pl)); - - llist_for_each_entry(bts, &net->bts_list, list) - bts_chan_load(pl, bts); -} - diff --git a/openbsc/src/bsc/e1_config.c b/openbsc/src/bsc/e1_config.c deleted file mode 100644 index 958839dcc..000000000 --- a/openbsc/src/bsc/e1_config.c +++ /dev/null @@ -1,296 +0,0 @@ -/* OpenBSC E1 Input code */ - -/* (C) 2008-2010 by Harald Welte - * All Rights Reserved - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation; either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - * - */ - -#include -#include - -#include - -#include -#include -#include -#include -#include -#include -#include -#include - -#define SAPI_L2ML 0 -#define SAPI_OML 62 -#define SAPI_RSL 0 /* 63 ? */ - -/* The e1_reconfig_*() functions below tale the configuration present in the - * bts/trx/ts data structures and ensure the E1 configuration reflects the - * timeslot/subslot/TEI configuration */ - -int e1_reconfig_ts(struct gsm_bts_trx_ts *ts) -{ - struct gsm_e1_subslot *e1_link = &ts->e1_link; - struct e1inp_line *line; - struct e1inp_ts *e1_ts; - - DEBUGP(DMI, "e1_reconfig_ts(%u,%u,%u)\n", ts->trx->bts->nr, ts->trx->nr, ts->nr); - - if (!e1_link->e1_ts) { - LOGP(DINP, LOGL_ERROR, "TS (%u/%u/%u) without E1 timeslot?\n", - ts->nr, ts->trx->nr, ts->trx->bts->nr); - return 0; - } - - line = e1inp_line_get(e1_link->e1_nr); - if (!line) { - LOGP(DINP, LOGL_ERROR, "TS (%u/%u/%u) referring to " - "non-existing E1 line %u\n", ts->nr, ts->trx->nr, - ts->trx->bts->nr, e1_link->e1_nr); - return -ENOMEM; - } - - switch (ts->pchan) { - case GSM_PCHAN_TCH_F: - case GSM_PCHAN_TCH_H: - e1_ts = &line->ts[e1_link->e1_ts-1]; - e1inp_ts_config(e1_ts, line, E1INP_TS_TYPE_TRAU); - subch_demux_activate(&e1_ts->trau.demux, e1_link->e1_ts_ss); - break; - default: - break; - } - - return 0; -} - -int e1_reconfig_trx(struct gsm_bts_trx *trx) -{ - struct gsm_e1_subslot *e1_link = &trx->rsl_e1_link; - struct e1inp_ts *sign_ts; - struct e1inp_line *line; - struct e1inp_sign_link *rsl_link; - int i; - - if (!e1_link->e1_ts) { - LOGP(DINP, LOGL_ERROR, "TRX (%u/%u) RSL link without " - "timeslot?\n", trx->bts->nr, trx->nr); - return -EINVAL; - } - - /* RSL Link */ - line = e1inp_line_get(e1_link->e1_nr); - if (!line) { - LOGP(DINP, 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_ts, line, E1INP_TS_TYPE_SIGN); - /* 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(DINP, 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(DINP, 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; -} - -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(DMI, "e1_reconfig_bts(%u)\n", bts->nr); - - if (!e1_link->e1_ts) { - LOGP(DINP, LOGL_ERROR, "BTS %u OML link without timeslot?\n", - bts->nr); - return -EINVAL; - } - - /* OML link */ - line = e1inp_line_get(e1_link->e1_nr); - if (!line) { - LOGP(DINP, LOGL_ERROR, "BTS %u OML link referring to " - "non-existing E1 line %u\n", bts->nr, e1_link->e1_nr); - return -ENOMEM; - } - sign_ts = &line->ts[e1_link->e1_ts-1]; - e1inp_ts_config(sign_ts, line, E1INP_TS_TYPE_SIGN); - oml_link = e1inp_sign_link_create(sign_ts, E1INP_SIGN_OML, - bts->c0, bts->oml_tei, SAPI_OML); - if (!oml_link) { - LOGP(DINP, 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 - -/* configure pseudo E1 line in ip.access style and connect to BTS */ -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; - - /* create E1 timeslots for signalling and TRAU frames */ - e1inp_ts_config(&line->ts[1-1], line, E1INP_TS_TYPE_SIGN); - e1inp_ts_config(&line->ts[2-1], line, E1INP_TS_TYPE_SIGN); - - /* 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); -} diff --git a/openbsc/src/bsc/gsm_04_08_utils.c b/openbsc/src/bsc/gsm_04_08_utils.c deleted file mode 100644 index 6d12cc08e..000000000 --- a/openbsc/src/bsc/gsm_04_08_utils.c +++ /dev/null @@ -1,655 +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 - * (C) 2008, 2009 by Holger Hans Peter Freyther - * - * All Rights Reserved - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation; either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - * - */ -#include -#include -#include -#include - -#include -#include - -#include -#include -#include -#include -#include -#include - -/* 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->trx = msg->lchan->ts->trx; - - msg->l3h = msg->data; - return rsl_data_request(msg, 0); -} - -/* Section 9.1.8 / Table 9.9 */ -struct chreq { - u_int8_t val; - u_int8_t mask; - enum chreq_type type; -}; - -/* If SYSTEM INFORMATION TYPE 4 NECI bit == 1 */ -static const struct chreq chreq_type_neci1[] = { - { 0xa0, 0xe0, CHREQ_T_EMERG_CALL }, - { 0xc0, 0xe0, CHREQ_T_CALL_REEST_TCH_F }, - { 0x68, 0xfc, CHREQ_T_CALL_REEST_TCH_H }, - { 0x6c, 0xfc, CHREQ_T_CALL_REEST_TCH_H_DBL }, - { 0xe0, 0xe0, CHREQ_T_TCH_F }, - { 0x40, 0xf0, CHREQ_T_VOICE_CALL_TCH_H }, - { 0x50, 0xf0, CHREQ_T_DATA_CALL_TCH_H }, - { 0x00, 0xf0, CHREQ_T_LOCATION_UPD }, - { 0x10, 0xf0, CHREQ_T_SDCCH }, - { 0x80, 0xe0, CHREQ_T_PAG_R_ANY_NECI1 }, - { 0x20, 0xf0, CHREQ_T_PAG_R_TCH_F }, - { 0x30, 0xf0, CHREQ_T_PAG_R_TCH_FH }, - { 0x67, 0xff, CHREQ_T_LMU }, - { 0x60, 0xf9, CHREQ_T_RESERVED_SDCCH }, - { 0x61, 0xfb, CHREQ_T_RESERVED_SDCCH }, - { 0x63, 0xff, CHREQ_T_RESERVED_SDCCH }, - { 0x7f, 0xff, CHREQ_T_RESERVED_IGNORE }, -}; - -/* If SYSTEM INFORMATION TYPE 4 NECI bit == 0 */ -static const struct chreq chreq_type_neci0[] = { - { 0xa0, 0xe0, CHREQ_T_EMERG_CALL }, - { 0xc0, 0xe0, CHREQ_T_CALL_REEST_TCH_H }, - { 0xe0, 0xe0, CHREQ_T_TCH_F }, - { 0x50, 0xf0, CHREQ_T_DATA_CALL_TCH_H }, - { 0x00, 0xe0, CHREQ_T_LOCATION_UPD }, - { 0x80, 0xe0, CHREQ_T_PAG_R_ANY_NECI0 }, - { 0x20, 0xf0, CHREQ_T_PAG_R_TCH_F }, - { 0x30, 0xf0, CHREQ_T_PAG_R_TCH_FH }, - { 0x67, 0xff, CHREQ_T_LMU }, - { 0x60, 0xf9, CHREQ_T_RESERVED_SDCCH }, - { 0x61, 0xfb, CHREQ_T_RESERVED_SDCCH }, - { 0x63, 0xff, CHREQ_T_RESERVED_SDCCH }, - { 0x7f, 0xff, CHREQ_T_RESERVED_IGNORE }, -}; - -static const enum gsm_chan_t ctype_by_chreq[] = { - [CHREQ_T_EMERG_CALL] = GSM_LCHAN_TCH_F, - [CHREQ_T_CALL_REEST_TCH_F] = GSM_LCHAN_TCH_F, - [CHREQ_T_CALL_REEST_TCH_H] = GSM_LCHAN_TCH_H, - [CHREQ_T_CALL_REEST_TCH_H_DBL] = GSM_LCHAN_TCH_H, - [CHREQ_T_SDCCH] = GSM_LCHAN_SDCCH, - [CHREQ_T_TCH_F] = GSM_LCHAN_TCH_F, - [CHREQ_T_VOICE_CALL_TCH_H] = GSM_LCHAN_TCH_H, - [CHREQ_T_DATA_CALL_TCH_H] = GSM_LCHAN_TCH_H, - [CHREQ_T_LOCATION_UPD] = GSM_LCHAN_SDCCH, - [CHREQ_T_PAG_R_ANY_NECI1] = GSM_LCHAN_SDCCH, - [CHREQ_T_PAG_R_ANY_NECI0] = GSM_LCHAN_SDCCH, - [CHREQ_T_PAG_R_TCH_F] = GSM_LCHAN_TCH_F, - [CHREQ_T_PAG_R_TCH_FH] = GSM_LCHAN_TCH_F, - [CHREQ_T_LMU] = GSM_LCHAN_SDCCH, - [CHREQ_T_RESERVED_SDCCH] = GSM_LCHAN_SDCCH, - [CHREQ_T_RESERVED_IGNORE] = GSM_LCHAN_UNKNOWN, -}; - -static const enum gsm_chreq_reason_t reason_by_chreq[] = { - [CHREQ_T_EMERG_CALL] = GSM_CHREQ_REASON_EMERG, - [CHREQ_T_CALL_REEST_TCH_F] = GSM_CHREQ_REASON_CALL, - [CHREQ_T_CALL_REEST_TCH_H] = GSM_CHREQ_REASON_CALL, - [CHREQ_T_CALL_REEST_TCH_H_DBL] = GSM_CHREQ_REASON_CALL, - [CHREQ_T_SDCCH] = GSM_CHREQ_REASON_OTHER, - [CHREQ_T_TCH_F] = GSM_CHREQ_REASON_OTHER, - [CHREQ_T_VOICE_CALL_TCH_H] = GSM_CHREQ_REASON_CALL, - [CHREQ_T_DATA_CALL_TCH_H] = GSM_CHREQ_REASON_OTHER, - [CHREQ_T_LOCATION_UPD] = GSM_CHREQ_REASON_LOCATION_UPD, - [CHREQ_T_PAG_R_ANY_NECI1] = GSM_CHREQ_REASON_PAG, - [CHREQ_T_PAG_R_ANY_NECI0] = GSM_CHREQ_REASON_PAG, - [CHREQ_T_PAG_R_TCH_F] = GSM_CHREQ_REASON_PAG, - [CHREQ_T_PAG_R_TCH_FH] = GSM_CHREQ_REASON_PAG, - [CHREQ_T_LMU] = GSM_CHREQ_REASON_OTHER, - [CHREQ_T_RESERVED_SDCCH] = GSM_CHREQ_REASON_OTHER, - [CHREQ_T_RESERVED_IGNORE] = GSM_CHREQ_REASON_OTHER, -}; - -/* verify that the two tables match */ -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, u_int8_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; -} - -enum gsm_chreq_reason_t get_reason_by_chreq(u_int8_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; -} - -/* 7.1.7 and 9.1.7: RR CHANnel RELease */ -int gsm48_send_rr_release(struct gsm_lchan *lchan) -{ - struct msgb *msg = gsm48_msgb_alloc(); - struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh)); - u_int8_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 */ - gsm48_sendmsg(msg); - /* FIXME: Start Timer T3109 */ - - /* Deactivate the SACCH on the BTS side */ - return rsl_deact_sacch(lchan); -} - -int send_siemens_mrpci(struct gsm_lchan *lchan, - u_int8_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; - - u_int8_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, u_int8_t *mi_type) -{ - static const uint32_t classmark_offset = - offsetof(struct gsm48_pag_resp, classmark2); - u_int8_t *classmark2_lv = (uint8_t *) &resp->classmark2; - return gsm48_extract_mi(classmark2_lv, length - classmark_offset, - mi_string, mi_type); -} - -int gsm48_handle_paging_resp(struct gsm_subscriber_connection *conn, - struct msgb *msg, struct gsm_subscriber *subscr) -{ - struct gsm_bts *bts = msg->lchan->ts->trx->bts; - struct gsm48_hdr *gh = msgb_l3(msg); - u_int8_t *classmark2_lv = gh->data + 1; - - if (is_siemens_bts(bts)) - send_siemens_mrpci(msg->lchan, classmark2_lv); - - if (!conn->subscr) { - conn->subscr = subscr; - } else if (conn->subscr != subscr) { - LOGP(DRR, LOGL_ERROR, "<- Channel already owned by someone else?\n"); - subscr_put(subscr); - return -EINVAL; - } else { - DEBUGP(DRR, "<- Channel already owned by us\n"); - subscr_put(subscr); - subscr = conn->subscr; - } - - counter_inc(bts->network->stats.paging.completed); - - /* Stop paging on the bts we received the paging response */ - paging_request_stop(conn->bts, subscr, conn, msg); - return 0; -} - -/* Chapter 9.1.9: Ciphering Mode Command */ -int gsm48_send_rr_ciph_mode(struct gsm_lchan *lchan, int want_imeisv) -{ - struct msgb *msg = gsm48_msgb_alloc(); - struct gsm48_hdr *gh; - u_int8_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) -{ - u_int16_t arfcn = lchan->ts->trx->arfcn & 0x3ff; - - cd->chan_nr = lchan2chan_nr(lchan); - if (!lchan->ts->hopping.enabled) { - cd->h0.tsc = lchan->ts->trx->bts->tsc; - cd->h0.h = 0; - cd->h0.arfcn_high = arfcn >> 8; - cd->h0.arfcn_low = arfcn & 0xff; - } else { - cd->h1.tsc = lchan->ts->trx->bts->tsc; - 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; - } -} - -#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, - u_int8_t power_command, u_int8_t ho_ref) -{ - struct msgb *msg = gsm48_msgb_alloc(); - struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh)); - struct gsm48_ho_cmd *ho = - (struct gsm48_ho_cmd *) msgb_put(msg, sizeof(*ho)); - - msg->lchan = old_lchan; - gh->proto_discr = GSM48_PDISC_RR; - gh->msg_type = GSM48_MT_RR_HANDO_CMD; - - /* mandatory bits */ - gsm48_cell_desc(&ho->cell_desc, new_lchan->ts->trx->bts); - gsm48_lchan2chan_desc(&ho->chan_desc, new_lchan); - ho->ho_ref = ho_ref; - ho->power_command = power_command; - - if (new_lchan->ts->hopping.enabled) { - struct gsm_bts *bts = new_lchan->ts->trx->bts; - struct gsm48_system_information_type_1 *si1; - uint8_t *cur; - - si1 = GSM_BTS_SI(bts, SYSINFO_TYPE_1); - /* Copy the Cell Chan Desc (ARFCNS in this cell) */ - msgb_put_u8(msg, GSM48_IE_CELL_CH_DESC); - cur = msgb_put(msg, GSM48_HOCMD_CCHDESC_LEN); - memcpy(cur, si1->cell_channel_description, - GSM48_HOCMD_CCHDESC_LEN); - /* Copy the Mobile Allocation */ - msgb_tlv_put(msg, GSM48_IE_MA_BEFORE, - new_lchan->ts->hopping.ma_len, - new_lchan->ts->hopping.ma_data); - } - /* FIXME: optional bits for type of synchronization? */ - - return gsm48_sendmsg(msg); -} - -/* Chapter 9.1.2: Assignment Command */ -int gsm48_send_rr_ass_cmd(struct gsm_lchan *dest_lchan, struct gsm_lchan *lchan, u_int8_t power_command) -{ - struct msgb *msg = gsm48_msgb_alloc(); - struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh)); - struct gsm48_ass_cmd *ass = - (struct gsm48_ass_cmd *) msgb_put(msg, sizeof(*ass)); - - DEBUGP(DRR, "-> ASSIGNMENT COMMAND tch_mode=0x%02x\n", lchan->tch_mode); - - msg->lchan = dest_lchan; - gh->proto_discr = GSM48_PDISC_RR; - gh->msg_type = GSM48_MT_RR_ASS_CMD; - - /* - * fill the channel information element, this code - * should probably be shared with rsl_rx_chan_rqd(), - * gsm48_tx_chan_mode_modify. But beware that 10.5.2.5 - * 10.5.2.5.a have slightly different semantic for - * the chan_desc. But as long as multi-slot configurations - * are not used we seem to be fine. - */ - gsm48_lchan2chan_desc(&ass->chan_desc, lchan); - ass->power_command = power_command; - - /* optional: cell channel description */ - - msgb_tv_put(msg, GSM48_IE_CHANMODE_1, lchan->tch_mode); - - /* mobile allocation in case of hopping */ - if (lchan->ts->hopping.enabled) { - msgb_tlv_put(msg, GSM48_IE_MA_BEFORE, lchan->ts->hopping.ma_len, - lchan->ts->hopping.ma_data); - } - - /* in case of multi rate we need to attach a config */ - if (lchan->tch_mode == GSM48_CMODE_SPEECH_AMR) { - if (lchan->mr_conf.ver == 0) { - LOGP(DRR, LOGL_ERROR, "BUG: Using multirate codec " - "without multirate config.\n"); - } else { - u_int8_t *data = msgb_put(msg, 4); - data[0] = GSM48_IE_MUL_RATE_CFG; - data[1] = 0x2; - memcpy(&data[2], &lchan->mr_conf, 2); - } - } - - return gsm48_sendmsg(msg); -} - -/* 9.1.5 Channel mode modify: Modify the mode on the MS side */ -int gsm48_tx_chan_mode_modify(struct gsm_lchan *lchan, u_int8_t mode) -{ - struct msgb *msg = gsm48_msgb_alloc(); - struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh)); - struct gsm48_chan_mode_modify *cmm = - (struct gsm48_chan_mode_modify *) msgb_put(msg, sizeof(*cmm)); - - DEBUGP(DRR, "-> CHANNEL MODE MODIFY mode=0x%02x\n", mode); - - lchan->tch_mode = mode; - msg->lchan = lchan; - gh->proto_discr = GSM48_PDISC_RR; - gh->msg_type = GSM48_MT_RR_CHAN_MODE_MODIF; - - /* fill the channel information element, this code - * should probably be shared with rsl_rx_chan_rqd() */ - gsm48_lchan2chan_desc(&cmm->chan_desc, lchan); - cmm->mode = mode; - - /* in case of multi rate we need to attach a config */ - if (lchan->tch_mode == GSM48_CMODE_SPEECH_AMR) { - if (lchan->mr_conf.ver == 0) { - LOGP(DRR, LOGL_ERROR, "BUG: Using multirate codec " - "without multirate config.\n"); - } else { - u_int8_t *data = msgb_put(msg, 4); - data[0] = GSM48_IE_MUL_RATE_CFG; - data[1] = 0x2; - memcpy(&data[2], &lchan->mr_conf, 2); - } - } - - return gsm48_sendmsg(msg); -} - -int gsm48_lchan_modify(struct gsm_lchan *lchan, u_int8_t lchan_mode) -{ - int rc; - - rc = gsm48_tx_chan_mode_modify(lchan, lchan_mode); - if (rc < 0) - return rc; - - return rc; -} - -int gsm48_rx_rr_modif_ack(struct msgb *msg) -{ - int rc; - struct gsm48_hdr *gh = msgb_l3(msg); - struct gsm48_chan_mode_modify *mod = - (struct gsm48_chan_mode_modify *) gh->data; - - DEBUGP(DRR, "CHANNEL MODE MODIFY ACK\n"); - - if (mod->mode != msg->lchan->tch_mode) { - LOGP(DRR, LOGL_ERROR, "CHANNEL MODE change failed. Wanted: %d Got: %d\n", - msg->lchan->tch_mode, mod->mode); - return -1; - } - - /* update the channel type */ - switch (mod->mode) { - case GSM48_CMODE_SIGN: - msg->lchan->rsl_cmode = RSL_CMOD_SPD_SIGN; - break; - case GSM48_CMODE_SPEECH_V1: - case GSM48_CMODE_SPEECH_EFR: - case GSM48_CMODE_SPEECH_AMR: - msg->lchan->rsl_cmode = RSL_CMOD_SPD_SPEECH; - break; - case GSM48_CMODE_DATA_14k5: - case GSM48_CMODE_DATA_12k0: - case GSM48_CMODE_DATA_6k0: - case GSM48_CMODE_DATA_3k6: - msg->lchan->rsl_cmode = RSL_CMOD_SPD_DATA; - break; - } - - /* We've successfully modified the MS side of the channel, - * now go on to modify the BTS side of the channel */ - rc = rsl_chan_mode_modify_req(msg->lchan); - - /* FIXME: we not only need to do this after mode modify, but - * also after channel activation */ - if (is_ipaccess_bts(msg->lchan->ts->trx->bts) && mod->mode != GSM48_CMODE_SIGN) - rsl_ipacc_crcx(msg->lchan); - return rc; -} - -int gsm48_parse_meas_rep(struct gsm_meas_rep *rep, struct msgb *msg) -{ - struct gsm48_hdr *gh = msgb_l3(msg); - u_int8_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[3] >> 4) & 0x7; - rep->dl.sub.rx_qual = (data[3] >> 1) & 0x7; - - rep->num_cell = ((data[3] >> 6) & 0x3) | ((data[2] & 0x01) << 2); - if (rep->num_cell < 1 || rep->num_cell > 6) - return 0; - - /* an encoding nightmare in perfection */ - mrc = &rep->cell[0]; - mrc->rxlev = data[3] & 0x3f; - mrc->neigh_idx = data[4] >> 3; - mrc->arfcn = bitvec_get_nth_set_bit(nbv, mrc->neigh_idx + 1); - mrc->bsic = ((data[4] & 0x07) << 3) | (data[5] >> 5); - if (rep->num_cell < 2) - return 0; - - mrc = &rep->cell[1]; - mrc->rxlev = ((data[5] & 0x1f) << 1) | (data[6] >> 7); - mrc->neigh_idx = (data[6] >> 2) & 0x1f; - mrc->arfcn = bitvec_get_nth_set_bit(nbv, mrc->neigh_idx + 1); - mrc->bsic = ((data[6] & 0x03) << 4) | (data[7] >> 4); - if (rep->num_cell < 3) - return 0; - - mrc = &rep->cell[2]; - mrc->rxlev = ((data[7] & 0x0f) << 2) | (data[8] >> 6); - mrc->neigh_idx = (data[8] >> 1) & 0x1f; - mrc->arfcn = bitvec_get_nth_set_bit(nbv, mrc->neigh_idx + 1); - mrc->bsic = ((data[8] & 0x01) << 5) | (data[9] >> 3); - if (rep->num_cell < 4) - return 0; - - mrc = &rep->cell[3]; - mrc->rxlev = ((data[9] & 0x07) << 3) | (data[10] >> 5); - mrc->neigh_idx = data[10] & 0x1f; - mrc->arfcn = bitvec_get_nth_set_bit(nbv, mrc->neigh_idx + 1); - mrc->bsic = data[11] >> 2; - if (rep->num_cell < 5) - return 0; - - mrc = &rep->cell[4]; - mrc->rxlev = ((data[11] & 0x03) << 4) | (data[12] >> 4); - mrc->neigh_idx = ((data[12] & 0xf) << 1) | (data[13] >> 7); - mrc->arfcn = bitvec_get_nth_set_bit(nbv, mrc->neigh_idx + 1); - mrc->bsic = (data[13] >> 1) & 0x3f; - if (rep->num_cell < 6) - return 0; - - mrc = &rep->cell[5]; - mrc->rxlev = ((data[13] & 0x01) << 5) | (data[14] >> 3); - mrc->neigh_idx = ((data[14] & 0x07) << 2) | (data[15] >> 6); - mrc->arfcn = bitvec_get_nth_set_bit(nbv, mrc->neigh_idx + 1); - mrc->bsic = data[15] & 0x3f; - - return 0; -} - -struct msgb *gsm48_create_mm_serv_rej(enum gsm48_reject_value value) -{ - struct msgb *msg; - struct gsm48_hdr *gh; - - msg = gsm48_msgb_alloc(); - if (!msg) - return NULL; - - gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh) + 1); - gh->proto_discr = GSM48_PDISC_MM; - gh->msg_type = GSM48_MT_MM_CM_SERV_REJ; - gh->data[0] = value; - - return msg; -} - -struct msgb *gsm48_create_loc_upd_rej(uint8_t cause) -{ - struct gsm48_hdr *gh; - struct msgb *msg; - - msg = gsm48_msgb_alloc(); - if (!msg) - return NULL; - - gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh) + 1); - gh->proto_discr = GSM48_PDISC_MM; - gh->msg_type = GSM48_MT_MM_LOC_UPD_REJECT; - gh->data[0] = cause; - return msg; -} diff --git a/openbsc/src/bsc/gsm_subscriber_base.c b/openbsc/src/bsc/gsm_subscriber_base.c deleted file mode 100644 index caf84e7bb..000000000 --- a/openbsc/src/bsc/gsm_subscriber_base.c +++ /dev/null @@ -1,149 +0,0 @@ -/* The concept of a subscriber as seen by the BSC */ - -/* (C) 2008 by Harald Welte - * (C) 2009-2010 by Holger Hans Peter Freyther - * (C) 2010 by On-Waves - * - * All Rights Reserved - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation; either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - * - */ - -#include -#include -#include -#include -#include - -#include -#include -#include - -LLIST_HEAD(active_subscribers); -void *tall_subscr_ctx; - -/* for the gsm_subscriber.c */ -struct llist_head *subscr_bsc_active_subscriber(void) -{ - return &active_subscribers; -} - - -char *subscr_name(struct gsm_subscriber *subscr) -{ - if (strlen(subscr->name)) - return subscr->name; - - return subscr->imsi; -} - -struct gsm_subscriber *subscr_alloc(void) -{ - struct gsm_subscriber *s; - - s = talloc_zero(tall_subscr_ctx, struct gsm_subscriber); - if (!s) - return NULL; - - llist_add_tail(&s->entry, &active_subscribers); - s->use_count = 1; - s->tmsi = GSM_RESERVED_TMSI; - - INIT_LLIST_HEAD(&s->requests); - - return s; -} - -static void subscr_free(struct gsm_subscriber *subscr) -{ - llist_del(&subscr->entry); - talloc_free(subscr); -} - -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->net->keep_subscr) - subscr_free(subscr); - return NULL; -} - -struct gsm_subscriber *subscr_get_or_create(struct gsm_network *net, - const char *imsi) -{ - struct gsm_subscriber *subscr; - - llist_for_each_entry(subscr, subscr_bsc_active_subscriber(), entry) { - if (strcmp(subscr->imsi, imsi) == 0 && subscr->net == net) - return subscr_get(subscr); - } - - subscr = subscr_alloc(); - if (!subscr) - return NULL; - - strcpy(subscr->imsi, imsi); - subscr->net = net; - return subscr; -} - -struct gsm_subscriber *subscr_active_by_tmsi(struct gsm_network *net, uint32_t tmsi) -{ - struct gsm_subscriber *subscr; - - llist_for_each_entry(subscr, subscr_bsc_active_subscriber(), entry) { - if (subscr->tmsi == tmsi && subscr->net == net) - return subscr_get(subscr); - } - - return NULL; -} - -struct gsm_subscriber *subscr_active_by_imsi(struct gsm_network *net, const char *imsi) -{ - struct gsm_subscriber *subscr; - - llist_for_each_entry(subscr, subscr_bsc_active_subscriber(), entry) { - if (strcmp(subscr->imsi, imsi) == 0 && subscr->net == net) - return subscr_get(subscr); - } - - return NULL; -} - -int subscr_purge_inactive(struct gsm_network *net) -{ - struct gsm_subscriber *subscr, *tmp; - int purged = 0; - - llist_for_each_entry_safe(subscr, tmp, subscr_bsc_active_subscriber(), entry) { - if (subscr->net == net && subscr->use_count <= 0) { - subscr_free(subscr); - purged += 1; - } - } - - return purged; -} diff --git a/openbsc/src/bsc/handover_decision.c b/openbsc/src/bsc/handover_decision.c deleted file mode 100644 index d3f843afb..000000000 --- a/openbsc/src/bsc/handover_decision.c +++ /dev/null @@ -1,297 +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 - * - * All Rights Reserved - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation; either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - * - */ - -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include - -/* issue handover to a cell identified by ARFCN and BSIC */ -static int handover_to_arfcn_bsic(struct gsm_lchan *lchan, - u_int16_t arfcn, u_int8_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, - u_int16_t arfcn, u_int8_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; - - /* 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 (avg < worst) { - worst = avg; - nmp_worst = nmp; - } - } - - return nmp_worst; -} - -/* process neighbor cell measurement reports */ -static void process_meas_neigh(struct gsm_meas_rep *mr) -{ - int i, j, idx; - - /* for each reported cell, try to update global state */ - for (j = 0; j < ARRAY_SIZE(mr->lchan->neigh_meas); j++) { - struct neigh_meas_proc *nmp = &mr->lchan->neigh_meas[j]; - unsigned int idx; - int rxlev; - - /* skip unused entries */ - if (!nmp->arfcn) - continue; - - rxlev = rxlev_for_cell_in_rep(mr, nmp->arfcn, nmp->bsic); - idx = nmp->rxlev_cnt % ARRAY_SIZE(nmp->rxlev); - if (rxlev >= 0) { - nmp->rxlev[idx] = rxlev; - nmp->last_seen_nr = mr->nr; - } else - nmp->rxlev[idx] = 0; - nmp->rxlev_cnt++; - } - - /* iterate over list of reported cells, check if we did not - * process all of them */ - for (i = 0; i < mr->num_cell; i++) { - struct gsm_meas_rep_cell *mrc = &mr->cell[i]; - struct neigh_meas_proc *nmp; - - if (mrc->flags & MRC_F_PROCESSED) - continue; - - nmp = find_evict_neigh(mr->lchan); - - nmp->arfcn = mrc->arfcn; - nmp->bsic = mrc->bsic; - - idx = nmp->rxlev_cnt % ARRAY_SIZE(nmp->rxlev); - nmp->rxlev[idx] = mrc->rxlev; - nmp->rxlev_cnt++; - nmp->last_seen_nr = mr->nr; - - mrc->flags |= MRC_F_PROCESSED; - } -} - -/* attempt to do a handover */ -static int attempt_handover(struct gsm_meas_rep *mr) -{ - struct gsm_network *net = mr->lchan->ts->trx->bts->network; - struct neigh_meas_proc *best_cell = NULL; - unsigned int best_better_db = 0; - int i, rc; - - /* find the best cell in this report that is at least RXLEV_HYST - * better than the current serving cell */ - - for (i = 0; i < ARRAY_SIZE(mr->lchan->neigh_meas); i++) { - struct neigh_meas_proc *nmp = &mr->lchan->neigh_meas[i]; - int avg, better; - - /* skip empty slots */ - if (nmp->arfcn == 0) - continue; - - /* caculate average rxlev for this cell over the window */ - avg = neigh_meas_avg(nmp, net->handover.win_rxlev_avg_neigh); - - /* check if hysteresis is fulfilled */ - if (avg < mr->dl.full.rx_lev + net->handover.pwr_hysteresis) - continue; - - better = avg - mr->dl.full.rx_lev; - if (better > best_better_db) { - best_cell = nmp; - best_better_db = better; - } - } - - if (!best_cell) - return 0; - - LOGP(DHO, LOGL_INFO, "%s: Cell on ARFCN %u is better: ", - gsm_ts_name(mr->lchan->ts), best_cell->arfcn); - if (!net->handover.active) { - LOGPC(DHO, LOGL_INFO, "Skipping, Handover disabled\n"); - return 0; - } - - rc = handover_to_arfcn_bsic(mr->lchan, best_cell->arfcn, best_cell->bsic); - switch (rc) { - case 0: - LOGPC(DHO, LOGL_INFO, "Starting handover\n"); - break; - case -ENOSPC: - LOGPC(DHO, LOGL_INFO, "No channel available\n"); - break; - case -EBUSY: - LOGPC(DHO, LOGL_INFO, "Handover already active\n"); - break; - default: - LOGPC(DHO, LOGL_ERROR, "Unknown error\n"); - } - return rc; -} - -/* process an already parsed measurement report and decide if we want to - * attempt a handover */ -static int process_meas_rep(struct gsm_meas_rep *mr) -{ - struct gsm_network *net = mr->lchan->ts->trx->bts->network; - int av_rxlev; - - /* we currently only do handover for TCH channels */ - switch (mr->lchan->type) { - case GSM_LCHAN_TCH_F: - case GSM_LCHAN_TCH_H: - break; - default: - return 0; - } - - /* parse actual neighbor cell info */ - if (mr->num_cell > 0 && mr->num_cell < 7) - process_meas_neigh(mr); - - av_rxlev = get_meas_rep_avg(mr->lchan, MEAS_REP_DL_RXLEV_FULL, - net->handover.win_rxlev_avg); - - /* Interference HO */ - if (rxlev2dbm(av_rxlev) > -85 && - meas_rep_n_out_of_m_be(mr->lchan, MEAS_REP_DL_RXQUAL_FULL, - 3, 4, 5)) - return attempt_handover(mr); - - /* Bad Quality */ - if (meas_rep_n_out_of_m_be(mr->lchan, MEAS_REP_DL_RXQUAL_FULL, - 3, 4, 5)) - return attempt_handover(mr); - - /* Low Level */ - if (rxlev2dbm(av_rxlev) <= -110) - return attempt_handover(mr); - - /* Distance */ - if (mr->ms_l1.ta > net->handover.max_distance) - return attempt_handover(mr); - - /* Power Budget AKA Better Cell */ - if ((mr->nr % net->handover.pwr_interval) == 0) - return attempt_handover(mr); - - return 0; - -} - -static int ho_dec_sig_cb(unsigned int subsys, unsigned int signal, - void *handler_data, void *signal_data) -{ - struct lchan_signal_data *lchan_data; - - if (subsys != SS_LCHAN) - return 0; - - lchan_data = signal_data; - switch (signal) { - case S_LCHAN_MEAS_REP: - process_meas_rep(lchan_data->mr); - break; - } - - return 0; -} - -void on_dso_load_ho_dec(void) -{ - register_signal_handler(SS_LCHAN, ho_dec_sig_cb, NULL); -} diff --git a/openbsc/src/bsc/handover_logic.c b/openbsc/src/bsc/handover_logic.c deleted file mode 100644 index c2e3f8c72..000000000 --- a/openbsc/src/bsc/handover_logic.c +++ /dev/null @@ -1,393 +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 - * - * All Rights Reserved - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation; either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - * - */ - -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -struct bsc_handover { - struct llist_head list; - - struct gsm_lchan *old_lchan; - struct gsm_lchan *new_lchan; - - struct timer_list T3103; - - u_int8_t ho_ref; -}; - -static LLIST_HEAD(bsc_handovers); - -static struct bsc_handover *bsc_ho_by_new_lchan(struct gsm_lchan *new_lchan) -{ - struct bsc_handover *ho; - - llist_for_each_entry(ho, &bsc_handovers, list) { - if (ho->new_lchan == new_lchan) - return ho; - } - - return NULL; -} - -static struct bsc_handover *bsc_ho_by_old_lchan(struct gsm_lchan *old_lchan) -{ - struct bsc_handover *ho; - - llist_for_each_entry(ho, &bsc_handovers, list) { - if (ho->old_lchan == old_lchan) - return ho; - } - - return NULL; -} - -/* Hand over the specified logical channel to the specified new BTS. - * This is the main entry point for the actual handover algorithm, - * after it has decided it wants to initiate HO to a specific BTS */ -int bsc_handover_start(struct gsm_lchan *old_lchan, struct gsm_bts *bts) -{ - struct gsm_lchan *new_lchan; - struct bsc_handover *ho; - static u_int8_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); - - counter_inc(bts->network->stats.handover.attempted); - - if (!old_lchan->conn) { - LOGP(DHO, LOGL_ERROR, "Old lchan lacks connection data.\n"); - return -ENOSPC; - } - - new_lchan = lchan_alloc(bts, old_lchan->type, 0); - if (!new_lchan) { - LOGP(DHO, LOGL_NOTICE, "No free channel\n"); - counter_inc(bts->network->stats.handover.no_channel); - return -ENOSPC; - } - - ho = talloc_zero(tall_bsc_ctx, struct bsc_handover); - if (!ho) { - LOGP(DHO, LOGL_FATAL, "Out of Memory\n"); - lchan_free(new_lchan); - return -ENOMEM; - } - ho->old_lchan = old_lchan; - ho->new_lchan = new_lchan; - ho->ho_ref = ho_ref++; - - /* copy some parameters from old lchan */ - memcpy(&new_lchan->encr, &old_lchan->encr, sizeof(new_lchan->encr)); - new_lchan->ms_power = old_lchan->ms_power; - new_lchan->bs_power = old_lchan->bs_power; - new_lchan->rsl_cmode = old_lchan->rsl_cmode; - new_lchan->tch_mode = old_lchan->tch_mode; - - 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, 0, - 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, 1); - - bsc_del_timer(&ho->T3103); - llist_del(&ho->list); - talloc_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"); - counter_inc(net->stats.handover.timeout); - - ho->new_lchan->conn->ho_lchan = NULL; - ho->new_lchan->conn = NULL; - lchan_release(ho->new_lchan, 0, 1); - llist_del(&ho->list); - talloc_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; - int rc; - - /* 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 */ - - rc = gsm48_send_ho_cmd(ho->old_lchan, new_lchan, 0, ho->ho_ref); - - /* start T3103. We can continue either with T3103 expiration, - * 04.08 HANDOVER COMPLETE or 04.08 HANDOVER FAIL */ - ho->T3103.cb = ho_T3103_cb; - ho->T3103.data = ho; - bsc_schedule_timer(&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_ERROR, "unable to find HO record\n"); - return -ENODEV; - } - - new_lchan->conn->ho_lchan = NULL; - new_lchan->conn = NULL; - llist_del(&ho->list); - talloc_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); - - counter_inc(net->stats.handover.completed); - - bsc_del_timer(&ho->T3103); - - /* Replace the ho lchan with the primary one */ - if (ho->old_lchan != new_lchan->conn->lchan) - LOGP(DHO, LOGL_ERROR, "Primary lchan changed during handover.\n"); - - if (new_lchan != new_lchan->conn->ho_lchan) - LOGP(DHO, LOGL_ERROR, "Handover channel changed during this handover.\n"); - - new_lchan->conn->ho_lchan = NULL; - new_lchan->conn->lchan = new_lchan; - ho->old_lchan->conn = NULL; - - rsl_lchan_set_state(ho->old_lchan, LCHAN_S_INACTIVE); - lchan_release(ho->old_lchan, 0, 1); - - /* do something to re-route the actual speech frames ! */ - - llist_del(&ho->list); - talloc_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; - - ho = bsc_ho_by_old_lchan(old_lchan); - if (!ho) { - LOGP(DHO, LOGL_ERROR, "unable to find HO record\n"); - return -ENODEV; - } - - counter_inc(net->stats.handover.failed); - - bsc_del_timer(&ho->T3103); - llist_del(&ho->list); - - /* release the channel and forget about it */ - ho->new_lchan->conn->ho_lchan = NULL; - ho->new_lchan->conn = NULL; - lchan_release(ho->new_lchan, 0, 1); - - talloc_free(ho); - - 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_ipac_crcx_ack(struct gsm_lchan *new_lchan) -{ - struct bsc_handover *ho; - struct ho_signal_data sig_ho; - - ho = bsc_ho_by_new_lchan(new_lchan); - if (!ho) { - /* it is perfectly normal, we have CRCX even in non-HO cases */ - return 0; - } - - sig_ho.old_lchan = ho->old_lchan; - sig_ho.new_lchan = new_lchan; - dispatch_signal(SS_HO, S_HANDOVER_ACK, &sig_ho); - 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; - case SS_ABISIP: - lchan = signal_data; - switch (signal) { - case S_ABISIP_CRCX_ACK: - return ho_ipac_crcx_ack(lchan); - break; - } - break; - default: - break; - } - - return 0; -} - -static __attribute__((constructor)) void on_dso_load_ho_logic(void) -{ - register_signal_handler(SS_LCHAN, ho_logic_sig_cb, NULL); - register_signal_handler(SS_ABISIP, ho_logic_sig_cb, NULL); -} diff --git a/openbsc/src/bsc/meas_proc.c b/openbsc/src/bsc/meas_proc.c deleted file mode 100644 index ade1f2e0c..000000000 --- a/openbsc/src/bsc/meas_proc.c +++ /dev/null @@ -1,84 +0,0 @@ -/* Measurement Processing */ - -/* (C) 2009 by Harald Welte - * - * All Rights Reserved - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation; either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - * - */ - -#include -#include - -#include -#include -#include -#include -#include -#include - -/* process an already parsed measurement report */ -static int process_meas_rep(struct gsm_meas_rep *mr) -{ - struct gsm_meas_rep_cell *mr_cell = NULL; - unsigned int best_better_db; - int i; - - /* FIXME: implement actual averaging over multiple measurement - * reports */ - - /* find the best cell in this report that is at least RXLEV_HYST - * better than the current serving cell */ - for (i = 0; i < mr->num_cell; i++) { - unsigned int better; - if (mr->cell[i].rxlev < mr->dl.full.rx_lev + RXLEV_HYST) - continue; - - better = mr->cell[i].rxlev - mr->dl.full.rx_lev; - if (better > best_better_db) { - mr_cell = &mr->cell[i]; - best_better_db = better; - } - } - - if (mr_cell) - return handover_to_arfcn_bsic(mr->lchan, mr_cell->arfcn, - mr_cell->bsic); - return 0; -} - -static int meas_proc_sig_cb(unsigned int subsys, unsigned int signal, - void *handler_data, void *signal_data) -{ - struct gsm_lchan *lchan; - struct gsm_meas_rep *mr; - - if (subsys != SS_LCHAN) - return 0; - - switch (signal) { - case S_LCHAN_MEAS_REP: - mr = signal_data; - process_meas_rep(mr); - break; - } - - return 0; -} - -static __attribute__((constructor)) void on_dso_load_meas(void) -{ - register_signal_handler(SS_LCHAN, meas_proc_sig_cb, NULL); -} diff --git a/openbsc/src/bsc/meas_rep.c b/openbsc/src/bsc/meas_rep.c deleted file mode 100644 index 788a9baed..000000000 --- a/openbsc/src/bsc/meas_rep.c +++ /dev/null @@ -1,116 +0,0 @@ -/* Measurement Report Processing */ - -/* (C) 2009 by Harald Welte - * - * All Rights Reserved - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation; either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - * - */ - -#include - -#include -#include - -static int get_field(const struct gsm_meas_rep *rep, - enum meas_rep_field field) -{ - switch (field) { - case MEAS_REP_DL_RXLEV_FULL: - return rep->dl.full.rx_lev; - case MEAS_REP_DL_RXLEV_SUB: - return rep->dl.sub.rx_lev; - case MEAS_REP_DL_RXQUAL_FULL: - return rep->dl.full.rx_qual; - case MEAS_REP_DL_RXQUAL_SUB: - return rep->dl.sub.rx_qual; - case MEAS_REP_UL_RXLEV_FULL: - return rep->ul.full.rx_lev; - case MEAS_REP_UL_RXLEV_SUB: - return rep->ul.sub.rx_lev; - case MEAS_REP_UL_RXQUAL_FULL: - return rep->ul.full.rx_qual; - case MEAS_REP_UL_RXQUAL_SUB: - return rep->ul.sub.rx_qual; - } - - return 0; -} - - -unsigned int calc_initial_idx(unsigned int array_size, - unsigned int meas_rep_idx, - unsigned int num_values) -{ - int offs, idx; - - /* from which element do we need to start if we're interested - * in an average of 'num' elements */ - offs = meas_rep_idx - num_values; - - if (offs < 0) - idx = array_size + offs; - else - idx = offs; - - return idx; -} - -/* obtain an average over the last 'num' fields in the meas reps */ -int get_meas_rep_avg(const struct gsm_lchan *lchan, - enum meas_rep_field field, unsigned int num) -{ - unsigned int i, idx; - int avg = 0; - - if (num < 1) - return 0; - - idx = calc_initial_idx(ARRAY_SIZE(lchan->meas_rep), - lchan->meas_rep_idx, num); - - for (i = 0; i < num; i++) { - int j = (idx+i) % ARRAY_SIZE(lchan->meas_rep); - - avg += get_field(&lchan->meas_rep[j], field); - } - - return avg / num; -} - -/* Check if N out of M last values for FIELD are >= bd */ -int meas_rep_n_out_of_m_be(const struct gsm_lchan *lchan, - enum meas_rep_field field, - unsigned int n, unsigned int m, int be) -{ - unsigned int i, idx; - int count = 0; - - idx = calc_initial_idx(ARRAY_SIZE(lchan->meas_rep), - lchan->meas_rep_idx, m); - - for (i = 0; i < m; i++) { - int j = (idx + i) % ARRAY_SIZE(lchan->meas_rep); - int val = get_field(&lchan->meas_rep[j], field); - - if (val >= be) - count++; - - if (count >= n) - return 1; - } - - return 0; -} diff --git a/openbsc/src/bsc/paging.c b/openbsc/src/bsc/paging.c deleted file mode 100644 index 650254575..000000000 --- a/openbsc/src/bsc/paging.c +++ /dev/null @@ -1,395 +0,0 @@ -/* Paging helper and manager.... */ -/* (C) 2009 by Holger Hans Peter Freyther - * All Rights Reserved - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation; either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - * - */ - -/* - * Relevant specs: - * 12.21: - * - 9.4.12 for CCCH Local Threshold - * - * 05.58: - * - 8.5.2 CCCH Load indication - * - 9.3.15 Paging Load - * - * Approach: - * - Send paging command to subscriber - * - On Channel Request we will remember the reason - * - After the ACK we will request the identity - * - Then we will send assign the gsm_subscriber and - * - and call a callback - */ - -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include - -void *tall_paging_ctx; - -#define PAGING_TIMER 0, 500000 - -static unsigned int calculate_group(struct gsm_bts *bts, struct gsm_subscriber *subscr) -{ - int ccch_conf; - int bs_cc_chans; - int blocks; - unsigned int group; - - ccch_conf = bts->si_common.chan_desc.ccch_conf; - bs_cc_chans = rsl_ccch_conf_to_bs_cc_chans(ccch_conf); - /* code word + 2, as 2 channels equals 0x0 */ - blocks = rsl_number_of_paging_subchannels(bts); - group = get_paging_group(str_to_imsi(subscr->imsi), - bs_cc_chans, blocks); - return group; -} - -/* - * 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) -{ - bsc_del_timer(&to_be_deleted->T3113); - llist_del(&to_be_deleted->entry); - subscr_put(to_be_deleted->subscr); - talloc_free(to_be_deleted); -} - -static void page_ms(struct gsm_paging_request *request) -{ - u_int8_t mi[128]; - unsigned int mi_len; - unsigned int page_group; - - LOGP(DPAG, LOGL_INFO, "Going to send paging commands: imsi: '%s' tmsi: '0x%x'\n", - request->subscr->imsi, request->subscr->tmsi); - - if (request->subscr->tmsi == GSM_RESERVED_TMSI) - mi_len = gsm48_generate_mid_from_imsi(mi, request->subscr->imsi); - else - mi_len = gsm48_generate_mid_from_tmsi(mi, request->subscr->tmsi); - - page_group = calculate_group(request->bts, request->subscr); - gsm0808_page(request->bts, page_group, mi_len, mi, request->chan_type); -} - -static void paging_schedule_if_needed(struct gsm_bts_paging_state *paging_bts) -{ - if (llist_empty(&paging_bts->pending_requests)) - return; - - if (!bsc_timer_pending(&paging_bts->work_timer)) - bsc_schedule_timer(&paging_bts->work_timer, PAGING_TIMER); -} - - -static void paging_handle_pending_requests(struct gsm_bts_paging_state *paging_bts); -static void paging_give_credit(void *data) -{ - struct gsm_bts_paging_state *paging_bts = data; - - LOGP(DPAG, LOGL_NOTICE, "No slots available on bts nr %d\n", paging_bts->bts->nr); - paging_bts->available_slots = 20; - paging_handle_pending_requests(paging_bts); -} - -static int can_send_pag_req(struct gsm_bts *bts, int rsl_type) -{ - struct pchan_load pl; - int count; - - memset(&pl, 0, sizeof(pl)); - bts_chan_load(&pl, bts); - - switch (rsl_type) { - case RSL_CHANNEED_TCH_F: - case RSL_CHANNEED_TCH_ForH: - goto count_tch; - break; - case RSL_CHANNEED_SDCCH: - goto count_sdcch; - break; - case RSL_CHANNEED_ANY: - default: - if (bts->network->pag_any_tch) - goto count_tch; - else - goto count_sdcch; - break; - } - - return 0; - - /* could available SDCCH */ -count_sdcch: - count = 0; - count += pl.pchan[GSM_PCHAN_SDCCH8_SACCH8C].total - - pl.pchan[GSM_PCHAN_SDCCH8_SACCH8C].used; - count += pl.pchan[GSM_PCHAN_CCCH_SDCCH4].total - - pl.pchan[GSM_PCHAN_CCCH_SDCCH4].used; - return bts->paging.free_chans_need > count; - -count_tch: - count = 0; - count += pl.pchan[GSM_PCHAN_TCH_F].total - - pl.pchan[GSM_PCHAN_TCH_F].used; - if (bts->network->neci) - count += pl.pchan[GSM_PCHAN_TCH_H].total - - pl.pchan[GSM_PCHAN_TCH_H].used; - return bts->paging.free_chans_need > count; -} - -/* - * This is kicked by the periodic PAGING LOAD Indicator - * coming from abis_rsl.c - * - * We attempt to iterate once over the list of items but - * only upto available_slots. - */ -static void paging_handle_pending_requests(struct gsm_bts_paging_state *paging_bts) -{ - struct gsm_paging_request *request = NULL; - - /* - * Determine if the pending_requests list is empty and - * return then. - */ - if (llist_empty(&paging_bts->pending_requests)) { - /* since the list is empty, no need to reschedule the timer */ - return; - } - - /* - * In case the BTS does not provide us with load indication and we - * ran out of slots, call an autofill routine. It might be that the - * BTS did not like our paging messages and then we have counted down - * to zero and we do not get any messages. - */ - if (paging_bts->available_slots == 0) { - paging_bts->credit_timer.cb = paging_give_credit; - paging_bts->credit_timer.data = paging_bts; - bsc_schedule_timer(&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: - bsc_schedule_timer(&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); -} - -void paging_init(struct gsm_bts *bts) -{ - bts->paging.bts = bts; - INIT_LLIST_HEAD(&bts->paging.pending_requests); - bts->paging.work_timer.cb = paging_worker; - bts->paging.work_timer.data = &bts->paging; - - /* Large number, until we get a proper message */ - bts->paging.available_slots = 20; -} - -static int paging_pending_request(struct gsm_bts_paging_state *bts, - struct gsm_subscriber *subscr) { - struct gsm_paging_request *req; - - llist_for_each_entry(req, &bts->pending_requests, entry) { - if (subscr == req->subscr) - return 1; - } - - return 0; -} - -static void paging_T3113_expired(void *data) -{ - struct gsm_paging_request *req = (struct gsm_paging_request *)data; - void *cbfn_param; - gsm_cbfn *cbfn; - int msg; - - LOGP(DPAG, LOGL_INFO, "T3113 expired for request %p (%s)\n", - req, req->subscr->imsi); - - /* must be destroyed before calling cbfn, to prevent double free */ - counter_inc(req->bts->network->stats.paging.expired); - cbfn_param = req->cbfn_param; - cbfn = req->cbfn; - - /* did we ever manage to page the subscriber */ - msg = req->attempts > 0 ? GSM_PAGING_EXPIRED : GSM_PAGING_BUSY; - - /* destroy it now. Do not access req afterwards */ - paging_remove_request(&req->bts->paging, req); - - if (cbfn) - cbfn(GSM_HOOK_RR_PAGING, msg, NULL, NULL, - cbfn_param); -} - -static int _paging_request(struct gsm_bts *bts, struct gsm_subscriber *subscr, - int type, gsm_cbfn *cbfn, void *data) -{ - struct gsm_bts_paging_state *bts_entry = &bts->paging; - struct gsm_paging_request *req; - - if (paging_pending_request(bts_entry, subscr)) { - LOGP(DPAG, LOGL_INFO, "Paging request already pending for %s\n", subscr->imsi); - return -EEXIST; - } - - LOGP(DPAG, LOGL_DEBUG, "Start paging of subscriber %llu on bts %d.\n", - subscr->id, bts->nr); - req = talloc_zero(tall_paging_ctx, struct gsm_paging_request); - req->subscr = subscr_get(subscr); - req->bts = bts; - req->chan_type = type; - req->cbfn = cbfn; - req->cbfn_param = data; - req->T3113.cb = paging_T3113_expired; - req->T3113.data = req; - bsc_schedule_timer(&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(struct gsm_network *network, struct gsm_subscriber *subscr, - int type, gsm_cbfn *cbfn, void *data) -{ - struct gsm_bts *bts = NULL; - int num_pages = 0; - - counter_inc(network->stats.paging.attempted); - - /* start paging subscriber on all BTS within Location Area */ - do { - int rc; - - bts = gsm_bts_by_lac(network, subscr->lac, bts); - if (!bts) - break; - - /* skip all currently inactive TRX */ - if (!trx_is_usable(bts->c0)) - continue; - - num_pages++; - - /* Trigger paging, pass any error to caller */ - rc = _paging_request(bts, subscr, type, cbfn, data); - if (rc < 0) - return rc; - } while (1); - - if (num_pages == 0) - counter_inc(network->stats.paging.detached); - - return num_pages; -} - - -/* we consciously ignore the type of the request here */ -static void _paging_request_stop(struct gsm_bts *bts, struct gsm_subscriber *subscr, - struct gsm_subscriber_connection *conn, - struct msgb *msg) -{ - struct gsm_bts_paging_state *bts_entry = &bts->paging; - struct gsm_paging_request *req, *req2; - - llist_for_each_entry_safe(req, req2, &bts_entry->pending_requests, - entry) { - if (req->subscr == subscr) { - if (conn && req->cbfn) { - LOGP(DPAG, LOGL_DEBUG, "Stop paging on bts %d, calling cbfn.\n", bts->nr); - req->cbfn(GSM_HOOK_RR_PAGING, GSM_PAGING_SUCCEEDED, - msg, conn, req->cbfn_param); - } else - LOGP(DPAG, LOGL_DEBUG, "Stop paging on bts %d silently.\n", bts->nr); - paging_remove_request(&bts->paging, req); - break; - } - } -} - -/* Stop paging on all other bts' */ -void paging_request_stop(struct gsm_bts *_bts, struct gsm_subscriber *subscr, - struct gsm_subscriber_connection *conn, - struct msgb *msg) -{ - struct gsm_bts *bts = NULL; - - if (_bts) - _paging_request_stop(_bts, subscr, conn, msg); - - do { - /* - * FIXME: Don't use the lac of the subscriber... - * as it might have magically changed the lac.. use the - * location area of the _bts as reconfiguration of the - * network is probably happening less often. - */ - bts = gsm_bts_by_lac(subscr->net, subscr->lac, bts); - if (!bts) - break; - - /* Stop paging */ - if (bts != _bts) - _paging_request_stop(bts, subscr, NULL, NULL); - } while (1); -} - -void paging_update_buffer_space(struct gsm_bts *bts, u_int16_t free_slots) -{ - bsc_del_timer(&bts->paging.credit_timer); - bts->paging.available_slots = free_slots; - paging_schedule_if_needed(&bts->paging); -} diff --git a/openbsc/src/bsc/rest_octets.c b/openbsc/src/bsc/rest_octets.c deleted file mode 100644 index 084f14498..000000000 --- a/openbsc/src/bsc/rest_octets.c +++ /dev/null @@ -1,432 +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 - * - * All Rights Reserved - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation; either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - * - */ - -#include -#include -#include - -#include -#include -#include - -/* generate SI1 rest octets */ -int rest_octets_si1(u_int8_t *data, u_int8_t *nch_pos) -{ - 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); - - bitvec_spare_padding(&bv, 7); - 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(u_int8_t *data, const struct gsm48_si_ro_info *si3) -{ - struct bitvec bv; - - memset(&bv, 0, sizeof(bv)); - bv.data = data; - bv.data_len = 4; - - /* Optional Selection Parameters */ - append_selection_params(&bv, &si3->selection_params); - - /* Optional Power Offset */ - append_power_offset(&bv, &si3->power_offset); - - /* Do we have a SI2ter on the BCCH? */ - if (si3->si2ter_indicator) - bitvec_set_bit(&bv, H); - else - bitvec_set_bit(&bv, L); - - /* Early Classmark Sending Control */ - if (si3->early_cm_ctrl) - bitvec_set_bit(&bv, H); - else - bitvec_set_bit(&bv, L); - - /* Do we have a SI Type 9 on the BCCH? */ - if (si3->scheduling.present) { - bitvec_set_bit(&bv, H); - bitvec_set_uint(&bv, si3->scheduling.where, 3); - } else - bitvec_set_bit(&bv, L); - - /* GPRS Indicator */ - append_gprs_ind(&bv, &si3->gprs_ind); - - bitvec_spare_padding(&bv, (bv.data_len*8)-1); - return bv.data_len; -} - -static int append_lsa_params(struct bitvec *bv, - const struct gsm48_lsa_params *lsa_params) -{ - /* FIXME */ - return -1; -} - -/* Generate SI4 Rest Octets (Chapter 10.5.2.35) */ -int rest_octets_si4(u_int8_t *data, const struct gsm48_si_ro_info *si4) -{ - struct bitvec bv; - - memset(&bv, 0, sizeof(bv)); - bv.data = data; - bv.data_len = 10; /* FIXME: up to ? */ - - /* SI4 Rest Octets O */ - append_selection_params(&bv, &si4->selection_params); - append_power_offset(&bv, &si4->power_offset); - append_gprs_ind(&bv, &si4->gprs_ind); - - if (0 /* FIXME */) { - /* H and SI4 Rest Octets S */ - bitvec_set_bit(&bv, H); - - /* LSA Parameters */ - if (si4->lsa_params.present) { - bitvec_set_bit(&bv, H); - append_lsa_params(&bv, &si4->lsa_params); - } else - bitvec_set_bit(&bv, L); - - /* Cell Identity */ - if (1) { - bitvec_set_bit(&bv, H); - bitvec_set_uint(&bv, si4->cell_id, 16); - } else - bitvec_set_bit(&bv, L); - - /* LSA ID Information */ - if (0) { - bitvec_set_bit(&bv, H); - /* FIXME */ - } else - bitvec_set_bit(&bv, L); - } else { - /* L and break indicator */ - bitvec_set_bit(&bv, L); - bitvec_set_bit(&bv, si4->break_ind ? H : L); - } - - return bv.data_len; -} - -/* GPRS Mobile Allocation as per TS 04.60 Chapter 12.10a: - < GPRS Mobile Allocation IE > ::= - < HSN : bit (6) > - { 0 | 1 < RFL number list : < RFL number list struct > > } - { 0 < MA_LENGTH : bit (6) > - < MA_BITMAP: bit (val(MA_LENGTH) + 1) > - | 1 { 0 | 1 > } } ; - - < RFL number list struct > :: = - < RFL_NUMBER : bit (4) > - { 0 | 1 < RFL number list struct > } ; - < ARFCN index list struct > ::= - < ARFCN_INDEX : bit(6) > - { 0 | 1 < ARFCN index list struct > } ; - */ -static int append_gprs_mobile_alloc(struct bitvec *bv) -{ - /* Hopping Sequence Number */ - bitvec_set_uint(bv, 0, 6); - - if (0) { - /* We want to use a RFL number list */ - bitvec_set_bit(bv, 1); - /* FIXME: RFL number list */ - } else - bitvec_set_bit(bv, 0); - - if (0) { - /* We want to use a MA_BITMAP */ - bitvec_set_bit(bv, 0); - /* FIXME: MA_LENGTH, MA_BITMAP, ... */ - } else { - bitvec_set_bit(bv, 1); - if (0) { - /* We want to provide an ARFCN index list */ - bitvec_set_bit(bv, 1); - /* FIXME */ - } else - bitvec_set_bit(bv, 0); - } - return 0; -} - -static int encode_t3192(unsigned int t3192) -{ - if (t3192 == 0) - return 3; - else if (t3192 <= 80) - return 4; - else if (t3192 <= 120) - return 5; - else if (t3192 <= 160) - return 6; - else if (t3192 <= 200) - return 7; - else if (t3192 <= 500) - return 0; - else if (t3192 <= 1000) - return 1; - else if (t3192 <= 1500) - return 2; - else - return -EINVAL; -} - -static int encode_drx_timer(unsigned int drx) -{ - if (drx == 0) - return 0; - else if (drx == 1) - return 1; - else if (drx == 2) - return 2; - else if (drx <= 4) - return 3; - else if (drx <= 8) - return 4; - else if (drx <= 16) - return 5; - else if (drx <= 32) - return 6; - else if (drx <= 64) - return 7; - else - return -EINVAL; -} - -/* GPRS Cell Options as per TS 04.60 Chapter 12.24 - < GPRS Cell Options IE > ::= - < NMO : bit(2) > - < T3168 : bit(3) > - < T3192 : bit(3) > - < DRX_TIMER_MAX: bit(3) > - < ACCESS_BURST_TYPE: bit > - < CONTROL_ACK_TYPE : bit > - < BS_CV_MAX: bit(4) > - { 0 | 1 < PAN_DEC : bit(3) > - < PAN_INC : bit(3) > - < PAN_MAX : bit(3) > - { 0 | 1 < Extension Length : bit(6) > - < bit (val(Extension Length) + 1 - & { < Extension Information > ! { bit ** = } } ; - < Extension Information > ::= - { 0 | 1 < EGPRS_PACKET_CHANNEL_REQUEST : bit > - < BEP_PERIOD : bit(4) > } - < PFC_FEATURE_MODE : bit > - < DTM_SUPPORT : bit > - - ** ; - */ -static int append_gprs_cell_opt(struct bitvec *bv, - const struct gprs_cell_options *gco) -{ - int t3192, drx_timer_max; - - t3192 = encode_t3192(gco->t3192); - if (t3192 < 0) - return t3192; - - drx_timer_max = encode_drx_timer(gco->drx_timer_max); - if (drx_timer_max < 0) - return drx_timer_max; - - bitvec_set_uint(bv, gco->nmo, 2); - bitvec_set_uint(bv, gco->t3168 / 500, 3); - bitvec_set_uint(bv, t3192, 3); - bitvec_set_uint(bv, drx_timer_max, 3); - /* ACCESS_BURST_TYPE: Hard-code 8bit */ - bitvec_set_bit(bv, 0); - /* CONTROL_ACK_TYPE: Hard-code to RLC/MAC control block */ - bitvec_set_bit(bv, 1); - bitvec_set_uint(bv, gco->bs_cv_max, 4); - - if (0) { - /* hard-code no PAN_{DEC,INC,MAX} */ - bitvec_set_bit(bv, 0); - } else { - /* copied from ip.access BSC protocol trace */ - bitvec_set_bit(bv, 1); - bitvec_set_uint(bv, 1, 3); /* DEC */ - bitvec_set_uint(bv, 1, 3); /* INC */ - bitvec_set_uint(bv, 15, 3); /* MAX */ - } - - if (!gco->ext_info_present) { - /* no extension information */ - bitvec_set_bit(bv, 0); - } else { - /* extension information */ - bitvec_set_bit(bv, 1); - if (!gco->ext_info.egprs_supported) { - /* 6bit length of extension */ - bitvec_set_uint(bv, (1 + 3)-1, 6); - /* EGPRS supported in the cell */ - bitvec_set_bit(bv, 0); - } else { - /* 6bit length of extension */ - bitvec_set_uint(bv, (1 + 5 + 3)-1, 6); - /* EGPRS supported in the cell */ - bitvec_set_bit(bv, 1); - /* 1bit EGPRS PACKET CHANNEL REQUEST */ - bitvec_set_bit(bv, gco->ext_info.use_egprs_p_ch_req); - /* 4bit BEP PERIOD */ - bitvec_set_uint(bv, gco->ext_info.bep_period, 4); - } - bitvec_set_bit(bv, gco->ext_info.pfc_supported); - bitvec_set_bit(bv, gco->ext_info.dtm_supported); - bitvec_set_bit(bv, gco->ext_info.bss_paging_coordination); - } - - return 0; -} - -static void append_gprs_pwr_ctrl_pars(struct bitvec *bv, - const struct gprs_power_ctrl_pars *pcp) -{ - bitvec_set_uint(bv, pcp->alpha, 4); - bitvec_set_uint(bv, pcp->t_avg_w, 5); - bitvec_set_uint(bv, pcp->t_avg_t, 5); - bitvec_set_uint(bv, pcp->pc_meas_chan, 1); - bitvec_set_uint(bv, pcp->n_avg_i, 4); -} - -/* Generate SI13 Rest Octests (04.08 Chapter 10.5.2.37b) */ -int rest_octets_si13(u_int8_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/bsc/system_information.c b/openbsc/src/bsc/system_information.c deleted file mode 100644 index 8a99c565e..000000000 --- a/openbsc/src/bsc/system_information.c +++ /dev/null @@ -1,606 +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 - * - * All Rights Reserved - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation; either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - * - */ - -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include - -#define GSM48_CELL_CHAN_DESC_SIZE 16 -#define GSM_MACBLOCK_PADDING 0x2b - -/* verify the sizes of the system information type structs */ - -/* rest octets are not part of the struct */ -static_assert(sizeof(struct gsm48_system_information_type_header) == 3, _si_header_size); -static_assert(sizeof(struct gsm48_rach_control) == 3, _si_rach_control); -static_assert(sizeof(struct gsm48_system_information_type_1) == 22, _si1_size); -static_assert(sizeof(struct gsm48_system_information_type_2) == 23, _si2_size); -static_assert(sizeof(struct gsm48_system_information_type_3) == 19, _si3_size); -static_assert(sizeof(struct gsm48_system_information_type_4) == 13, _si4_size); - -/* bs11 forgot the l2 len, 0-6 rest octets */ -static_assert(sizeof(struct gsm48_system_information_type_5) == 18, _si5_size); -static_assert(sizeof(struct gsm48_system_information_type_6) == 11, _si6_size); - -static_assert(sizeof(struct gsm48_system_information_type_13) == 3, _si13_size); - -/* 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(u_int8_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(u_int8_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) { - LOGP(DRR, LOGL_ERROR, "arfcn(%u) < min(%u)\n", arfcn, min_arfcn); - return -EINVAL; - } - if (arfcn > min_arfcn + 111) { - LOGP(DRR, LOGL_ERROR, "arfcn(%u) > min(%u) + 111\n", arfcn, min_arfcn); - return -EINVAL; - } - - bitno = (arfcn - min_arfcn); - byte = bitno / 8; - bit = bitno % 8; - - chan_list[2 + byte] |= 1 << (7 - bit); - - return 0; -} - -/* generate a cell channel list as per Section 10.5.2.1b of 04.08 */ -static int bitvec2freq_list(u_int8_t *chan_list, struct bitvec *bv, - const struct gsm_bts *bts) -{ - int i, rc, min = 1024, max = -1; - - memset(chan_list, 0, 16); - - /* GSM900-only handsets only support 'bit map 0 format' */ - if (bts->band == GSM_BAND_900) { - chan_list[0] = 0; - - for (i = 0; i < bv->data_len*8; i++) { - if (bitvec_get_bit_pos(bv, i)) { - rc = freq_list_bm0_set_arfcn(chan_list, i); - if (rc < 0) - return rc; - } - } - return 0; - } - - /* We currently only support the 'Variable bitmap format' */ - chan_list[0] = 0x8e; - - for (i = 0; i < bv->data_len*8; i++) { - if (bitvec_get_bit_pos(bv, i)) { - if (i < min) - min = i; - if (i > max) - max = i; - } - } - - if (max == -1) { - /* Empty set, use 'bit map 0 format' */ - chan_list[0] = 0; - return 0; - } - - if ((max - min) > 111) { - LOGP(DRR, LOGL_ERROR, "min_arfcn=%u, max_arfcn=%u, " - "distance > 111\n", min, max); - return -EINVAL; - } - - 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++) { - if (bitvec_get_bit_pos(bv, i)) { - rc = freq_list_bmrel_set_arfcn(chan_list, i); - if (rc < 0) - return rc; - } - } - - return 0; -} - -/* generate a cell channel list as per Section 10.5.2.1b of 04.08 */ -static int generate_cell_chan_list(u_int8_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); -} - -/* generate a cell channel list as per Section 10.5.2.1b of 04.08 */ -static int generate_bcch_chan_list(u_int8_t *chan_list, struct gsm_bts *bts, int si5) -{ - 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); -} - -static int generate_si1(u_int8_t *output, struct gsm_bts *bts) -{ - int rc; - struct gsm48_system_information_type_1 *si1 = - (struct gsm48_system_information_type_1 *) output; - - memset(si1, GSM_MACBLOCK_PADDING, GSM_MACBLOCK_LEN); - - si1->header.l2_plen = (21 << 2) | 1; - si1->header.rr_protocol_discriminator = GSM48_PDISC_RR; - si1->header.skip_indicator = 0; - si1->header.system_information = GSM48_MT_RR_SYSINFO_1; - - rc = generate_cell_chan_list(si1->cell_channel_description, bts); - if (rc < 0) - return rc; - - si1->rach_control = bts->si_common.rach_control; - - /* SI1 Rest Octets (10.5.2.32), contains NCH position */ - rc = rest_octets_si1(si1->rest_octets, NULL); - - return sizeof(*si1) + rc; -} - -static int generate_si2(u_int8_t *output, struct gsm_bts *bts) -{ - int rc; - struct gsm48_system_information_type_2 *si2 = - (struct gsm48_system_information_type_2 *) output; - - memset(si2, GSM_MACBLOCK_PADDING, GSM_MACBLOCK_LEN); - - si2->header.l2_plen = (22 << 2) | 1; - si2->header.rr_protocol_discriminator = GSM48_PDISC_RR; - si2->header.skip_indicator = 0; - si2->header.system_information = GSM48_MT_RR_SYSINFO_2; - - rc = generate_bcch_chan_list(si2->bcch_frequency_list, bts, 0); - if (rc < 0) - return rc; - - si2->ncc_permitted = bts->si_common.ncc_permitted; - si2->rach_control = bts->si_common.rach_control; - - return sizeof(*si2); -} - -static struct gsm48_si_ro_info si_info = { - .selection_params = { - .present = 0, - }, - .power_offset = { - .present = 0, - }, - .si2ter_indicator = 0, - .early_cm_ctrl = 1, - .scheduling = { - .present = 0, - }, - .gprs_ind = { - .si13_position = 0, - .ra_colour = 0, - .present = 1, - }, - .lsa_params = { - .present = 0, - }, - .cell_id = 0, /* FIXME: doesn't the bts have this? */ - .break_ind = 0, -}; - -static int generate_si3(u_int8_t *output, struct gsm_bts *bts) -{ - int rc; - struct gsm48_system_information_type_3 *si3 = - (struct gsm48_system_information_type_3 *) output; - - memset(si3, GSM_MACBLOCK_PADDING, GSM_MACBLOCK_LEN); - - si3->header.l2_plen = (18 << 2) | 1; - si3->header.rr_protocol_discriminator = GSM48_PDISC_RR; - si3->header.skip_indicator = 0; - si3->header.system_information = GSM48_MT_RR_SYSINFO_3; - - si3->cell_identity = htons(bts->cell_identity); - gsm48_generate_lai(&si3->lai, bts->network->country_code, - bts->network->network_code, - bts->location_area_code); - si3->control_channel_desc = bts->si_common.chan_desc; - si3->cell_options = bts->si_common.cell_options; - si3->cell_sel_par = bts->si_common.cell_sel_par; - si3->rach_control = bts->si_common.rach_control; - - /* 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(u_int8_t *output, struct gsm_bts *bts) -{ - int rc; - struct gsm48_system_information_type_4 *si4 = - (struct gsm48_system_information_type_4 *) output; - - /* 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 */ - - si4->header.l2_plen = (l2_plen << 2) | 1; - - /* SI4 Rest Octets (10.5.2.35), containing - Optional Power offset, GPRS Indicator, - Cell Identity, LSA ID, Selection Parameter */ - rc = rest_octets_si4(si4->data, &si_info); - - return sizeof(*si4) + rc; -} - -static int generate_si5(u_int8_t *output, struct gsm_bts *bts) -{ - struct gsm48_system_information_type_5 *si5; - int rc, l2_plen = 18; - - memset(output, GSM_MACBLOCK_PADDING, GSM_MACBLOCK_LEN); - - /* ip.access nanoBTS needs l2_plen!! */ - if (is_ipaccess_bts(bts)) { - *output++ = (l2_plen << 2) | 1; - l2_plen++; - } - - si5 = (struct gsm48_system_information_type_5 *) output; - - /* l2 pseudo length, not part of msg: 18 */ - si5->rr_protocol_discriminator = GSM48_PDISC_RR; - si5->skip_indicator = 0; - si5->system_information = GSM48_MT_RR_SYSINFO_5; - rc = generate_bcch_chan_list(si5->bcch_frequency_list, bts, 1); - if (rc < 0) - return rc; - - /* 04.08 9.1.37: L2 Pseudo Length of 18 */ - return l2_plen; -} - -static int generate_si6(u_int8_t *output, struct gsm_bts *bts) -{ - struct gsm48_system_information_type_6 *si6; - int l2_plen = 11; - - memset(output, GSM_MACBLOCK_PADDING, GSM_MACBLOCK_LEN); - - /* ip.access nanoBTS needs l2_plen!! */ - if (is_ipaccess_bts(bts)) { - *output++ = (l2_plen << 2) | 1; - l2_plen++; - } - - si6 = (struct gsm48_system_information_type_6 *) output; - - /* l2 pseudo length, not part of msg: 11 */ - si6->rr_protocol_discriminator = GSM48_PDISC_RR; - si6->skip_indicator = 0; - si6->system_information = GSM48_MT_RR_SYSINFO_6; - si6->cell_identity = htons(bts->cell_identity); - gsm48_generate_lai(&si6->lai, bts->network->country_code, - bts->network->network_code, - bts->location_area_code); - si6->cell_options = bts->si_common.cell_options; - si6->ncc_permitted = bts->si_common.ncc_permitted; - - /* SI6 Rest Octets: 10.5.2.35a: PCH / NCH info, VBS/VGCS options */ - - return l2_plen; -} - -static struct gsm48_si13_info si13_default = { - .cell_opts = { - .nmo = GPRS_NMO_II, - .t3168 = 2000, - .t3192 = 200, - .drx_timer_max = 3, - .bs_cv_max = 15, - .ext_info_present = 1, - .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 = 10, /* a = 1.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(u_int8_t *output, struct gsm_bts *bts) -{ - struct gsm48_system_information_type_13 *si13 = - (struct gsm48_system_information_type_13 *) output; - int ret; - - memset(si13, GSM_MACBLOCK_PADDING, GSM_MACBLOCK_LEN); - - si13->header.rr_protocol_discriminator = GSM48_PDISC_RR; - si13->header.skip_indicator = 0; - si13->header.system_information = GSM48_MT_RR_SYSINFO_13; - - si13_default.no_pbcch.rac = bts->gprs.rac; - - 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; -} - -static const uint8_t sitype2rsl[_MAX_SYSINFO_TYPE] = { - [SYSINFO_TYPE_1] = RSL_SYSTEM_INFO_1, - [SYSINFO_TYPE_2] = RSL_SYSTEM_INFO_2, - [SYSINFO_TYPE_3] = RSL_SYSTEM_INFO_3, - [SYSINFO_TYPE_4] = RSL_SYSTEM_INFO_4, - [SYSINFO_TYPE_5] = RSL_SYSTEM_INFO_5, - [SYSINFO_TYPE_6] = RSL_SYSTEM_INFO_6, - [SYSINFO_TYPE_7] = RSL_SYSTEM_INFO_7, - [SYSINFO_TYPE_8] = RSL_SYSTEM_INFO_8, - [SYSINFO_TYPE_9] = RSL_SYSTEM_INFO_9, - [SYSINFO_TYPE_10] = RSL_SYSTEM_INFO_10, - [SYSINFO_TYPE_13] = RSL_SYSTEM_INFO_13, - [SYSINFO_TYPE_16] = RSL_SYSTEM_INFO_16, - [SYSINFO_TYPE_17] = RSL_SYSTEM_INFO_17, - [SYSINFO_TYPE_18] = RSL_SYSTEM_INFO_18, - [SYSINFO_TYPE_19] = RSL_SYSTEM_INFO_19, - [SYSINFO_TYPE_20] = RSL_SYSTEM_INFO_20, - [SYSINFO_TYPE_2bis] = RSL_SYSTEM_INFO_2bis, - [SYSINFO_TYPE_2ter] = RSL_SYSTEM_INFO_2ter, - [SYSINFO_TYPE_2quater] = RSL_SYSTEM_INFO_2quater, - [SYSINFO_TYPE_5bis] = RSL_SYSTEM_INFO_5bis, - [SYSINFO_TYPE_5ter] = RSL_SYSTEM_INFO_5ter, -}; - -static const uint8_t rsl2sitype[0xff] = { - [RSL_SYSTEM_INFO_1] = SYSINFO_TYPE_1, - [RSL_SYSTEM_INFO_2] = SYSINFO_TYPE_2, - [RSL_SYSTEM_INFO_3] = SYSINFO_TYPE_3, - [RSL_SYSTEM_INFO_4] = SYSINFO_TYPE_4, - [RSL_SYSTEM_INFO_5] = SYSINFO_TYPE_5, - [RSL_SYSTEM_INFO_6] = SYSINFO_TYPE_6, - [RSL_SYSTEM_INFO_7] = SYSINFO_TYPE_7, - [RSL_SYSTEM_INFO_8] = SYSINFO_TYPE_8, - [RSL_SYSTEM_INFO_9] = SYSINFO_TYPE_9, - [RSL_SYSTEM_INFO_10] = SYSINFO_TYPE_10, - [RSL_SYSTEM_INFO_13] = SYSINFO_TYPE_13, - [RSL_SYSTEM_INFO_16] = SYSINFO_TYPE_16, - [RSL_SYSTEM_INFO_17] = SYSINFO_TYPE_17, - [RSL_SYSTEM_INFO_18] = SYSINFO_TYPE_18, - [RSL_SYSTEM_INFO_19] = SYSINFO_TYPE_19, - [RSL_SYSTEM_INFO_20] = SYSINFO_TYPE_20, - [RSL_SYSTEM_INFO_2bis] = SYSINFO_TYPE_2bis, - [RSL_SYSTEM_INFO_2ter] = SYSINFO_TYPE_2ter, - [RSL_SYSTEM_INFO_2quater] = SYSINFO_TYPE_2quater, - [RSL_SYSTEM_INFO_5bis] = SYSINFO_TYPE_5bis, - [RSL_SYSTEM_INFO_5ter] = SYSINFO_TYPE_5ter, -}; - -typedef int (*gen_si_fn_t)(uint8_t *output, struct gsm_bts *bts); - -static const gen_si_fn_t gen_si_fn[_MAX_SYSINFO_TYPE] = { - [SYSINFO_TYPE_1] = &generate_si1, - [SYSINFO_TYPE_2] = &generate_si2, - [SYSINFO_TYPE_3] = &generate_si3, - [SYSINFO_TYPE_4] = &generate_si4, - [SYSINFO_TYPE_5] = &generate_si5, - [SYSINFO_TYPE_6] = &generate_si6, - [SYSINFO_TYPE_13] = &generate_si13, -}; - -const struct value_string osmo_sitype_strs[_MAX_SYSINFO_TYPE] = { - { SYSINFO_TYPE_1, "1" }, - { SYSINFO_TYPE_2, "2" }, - { SYSINFO_TYPE_3, "3" }, - { SYSINFO_TYPE_4, "4" }, - { SYSINFO_TYPE_5, "5" }, - { SYSINFO_TYPE_6, "6" }, - { SYSINFO_TYPE_7, "7" }, - { SYSINFO_TYPE_8, "8" }, - { SYSINFO_TYPE_9, "9" }, - { SYSINFO_TYPE_10, "10" }, - { SYSINFO_TYPE_13, "13" }, - { SYSINFO_TYPE_16, "16" }, - { SYSINFO_TYPE_17, "17" }, - { SYSINFO_TYPE_18, "18" }, - { SYSINFO_TYPE_19, "19" }, - { SYSINFO_TYPE_20, "20" }, - { SYSINFO_TYPE_2bis, "2bis" }, - { SYSINFO_TYPE_2ter, "2ter" }, - { SYSINFO_TYPE_2quater, "2quater" }, - { SYSINFO_TYPE_5bis, "5bis" }, - { SYSINFO_TYPE_5ter, "5ter" }, - { 0, NULL } -}; - -uint8_t gsm_sitype2rsl(enum osmo_sysinfo_type si_type) -{ - return sitype2rsl[si_type]; -} - -const char *gsm_sitype_name(enum osmo_sysinfo_type si_type) -{ - return get_value_string(osmo_sitype_strs, si_type); -} - -int gsm_generate_si(struct gsm_bts *bts, enum osmo_sysinfo_type si_type) -{ - gen_si_fn_t gen_si; - - switch (bts->gprs.mode) { - case BTS_GPRS_EGPRS: - si13_default.cell_opts.ext_info_present = 1; - si13_default.cell_opts.ext_info.egprs_supported = 1; - /* fallthrough */ - case BTS_GPRS_GPRS: - si_info.gprs_ind.present = 1; - break; - case BTS_GPRS_NONE: - si_info.gprs_ind.present = 0; - break; - } - - memcpy(&si_info.selection_params, - &bts->si_common.cell_ro_sel_par, - sizeof(struct gsm48_si_selection_params)); - - gen_si = gen_si_fn[si_type]; - if (!gen_si) - return -EINVAL; - - return gen_si(bts->si_buf[si_type], bts); -} diff --git a/openbsc/src/bsc/transaction.c b/openbsc/src/bsc/transaction.c deleted file mode 100644 index 9b4af1aac..000000000 --- a/openbsc/src/bsc/transaction.c +++ /dev/null @@ -1,152 +0,0 @@ -/* GSM 04.07 Transaction handling */ - -/* (C) 2009 by Harald Welte - * All Rights Reserved - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation; either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - * - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -void *tall_trans_ctx; - -void _gsm48_cc_trans_free(struct gsm_trans *trans); - -struct gsm_trans *trans_find_by_id(struct gsm_subscriber *subscr, - u_int8_t proto, u_int8_t trans_id) -{ - struct gsm_trans *trans; - struct gsm_network *net = subscr->net; - - 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, - u_int32_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_subscriber *subscr, - u_int8_t protocol, u_int8_t trans_id, - u_int32_t callref) -{ - struct gsm_trans *trans; - - DEBUGP(DCC, "subscr=%p, subscr->net=%p\n", subscr, 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; - - llist_add_tail(&trans->entry, &subscr->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; - } - - /* FIXME: implement a sane way to stop this. */ - if (!trans->conn && trans->paging_request) { - LOGP(DNM, LOGL_ERROR, - "Transaction freed while paging for sub: %llu\n", - trans->subscr->id); - trans->paging_request = NULL; - } - - if (trans->subscr) - subscr_put(trans->subscr); - - llist_del(&trans->entry); - - if (trans->conn) - msc_release_connection(trans->conn); - - - 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_subscriber *subscr, - u_int8_t protocol, u_int8_t ti_flag) -{ - struct gsm_network *net = subscr->net; - 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; -} - diff --git a/openbsc/src/common/Makefile.am b/openbsc/src/common/Makefile.am deleted file mode 100644 index 2895452ea..000000000 --- a/openbsc/src/common/Makefile.am +++ /dev/null @@ -1,7 +0,0 @@ -INCLUDES = $(all_includes) -I$(top_srcdir)/include -I$(top_builddir) -AM_CFLAGS=-Wall $(LIBOSMOCORE_CFLAGS) $(LIBOSMOVTY_CFLAGS) $(COVERAGE_CFLAGS) -AM_LDFLAGS = $(LIBOSMOCORE_LIBS) $(COVERAGE_LDFLAGS) - -noinst_LIBRARIES = libcommon.a - -libcommon_a_SOURCES = bsc_version.c common_vty.c debug.c gsm_data.c socket.c talloc_ctx.c diff --git a/openbsc/src/common/bsc_version.c b/openbsc/src/common/bsc_version.c deleted file mode 100644 index cc1e76449..000000000 --- a/openbsc/src/common/bsc_version.c +++ /dev/null @@ -1,30 +0,0 @@ -/* Hold the copyright and version string */ -/* (C) 2010 by Harald Welte - * All Rights Reserved - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation; either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - * - */ - -#include "bscconfig.h" - -const char *openbsc_copyright = - "Copyright (C) 2008-2010 Harald Welte, Holger Freyther\r\n" - "Contributions by Daniel Willmann, Jan Lübbe, Stefan Schmidt\r\n" - "Dieter Spaar, Andreas Eversberg, Sylvain Munaut\r\n\r\n" - "License AGPLv3+: GNU AGPL version 3 or later \r\n" - "This is free software: you are free to change and redistribute it.\r\n" - "There is NO WARRANTY, to the extent permitted by law.\r\n"; - - diff --git a/openbsc/src/common/common_vty.c b/openbsc/src/common/common_vty.c deleted file mode 100644 index 84375a22d..000000000 --- a/openbsc/src/common/common_vty.c +++ /dev/null @@ -1,225 +0,0 @@ -/* OpenBSC VTY common helpers */ -/* (C) 2009-2010 by Harald Welte - * (C) 2009-2010 by Holger Hans Peter Freyther - * All Rights Reserved - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation; either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - * - */ - -#include -#include - -#include - -#include -#include -#include -#include -#include - -#include -#include -#include -#include - - -enum node_type 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; - } - break; - case TRX_NODE: - vty->node = BTS_NODE; - { - /* set vty->index correctly ! */ - struct gsm_bts_trx *trx = vty->index; - vty->index = trx->bts; - } - break; - case TS_NODE: - vty->node = TRX_NODE; - { - /* set vty->index correctly ! */ - struct gsm_bts_trx_ts *ts = vty->index; - vty->index = ts->trx; - } - break; - case OML_NODE: - case OM2K_NODE: - vty->node = ENABLE_NODE; - talloc_free(vty->index); - vty->index = NULL; - break; - case NAT_NODE: - vty->node = CONFIG_NODE; - vty->index = NULL; - break; - case NAT_BSC_NODE: - vty->node = NAT_NODE; - { - struct bsc_config *bsc_config = vty->index; - vty->index = bsc_config->nat; - } - break; - case MSC_NODE: - vty->node = GSMNET_NODE; - break; - case TRUNK_NODE: - vty->node = MGCP_NODE; - break; - default: - vty->node = CONFIG_NODE; - } - - return vty->node; -} - -/* Down vty node level. */ -gDEFUN(ournode_exit, - ournode_exit_cmd, "exit", "Exit current mode and down to previous mode\n") -{ - 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 NAT_BSC_NODE: - vty->node = NAT_NODE; - { - struct bsc_config *bsc_config = vty->index; - vty->index = bsc_config->nat; - } - break; - case MGCP_NODE: - case GBPROXY_NODE: - case SGSN_NODE: - case NS_NODE: - case BSSGP_NODE: - case NAT_NODE: - vty->node = CONFIG_NODE; - vty->index = NULL; - break; - case OML_NODE: - case OM2K_NODE: - vty->node = ENABLE_NODE; - talloc_free(vty->index); - vty->index = NULL; - break; - case MSC_NODE: - vty->node = GSMNET_NODE; - break; - case TRUNK_NODE: - vty->node = MGCP_NODE; - vty->index = NULL; - break; - default: - break; - } - return CMD_SUCCESS; -} - -/* End of configuration. */ -gDEFUN(ournode_end, - ournode_end_cmd, "end", "End current mode and change to enable mode.") -{ - switch (vty->node) { - case VIEW_NODE: - case ENABLE_NODE: - /* Nothing to do. */ - break; - case CONFIG_NODE: - case GSMNET_NODE: - case BTS_NODE: - case TRX_NODE: - case TS_NODE: - case MGCP_NODE: - case TRUNK_NODE: - case GBPROXY_NODE: - case SGSN_NODE: - case NS_NODE: - case VTY_NODE: - case NAT_NODE: - case NAT_BSC_NODE: - case MSC_NODE: - vty_config_unlock(vty); - vty->node = ENABLE_NODE; - vty->index = NULL; - vty->index_sub = NULL; - break; - default: - break; - } - return CMD_SUCCESS; -} - -int bsc_vty_is_config_node(struct vty *vty, int node) -{ - switch (node) { - /* add items that are not config */ - case OML_NODE: - case OM2K_NODE: - case SUBSCR_NODE: - case CONFIG_NODE: - return 0; - - default: - return 1; - } -} - -/* a talloc string replace routine */ -void bsc_replace_string(void *ctx, char **dst, const char *newstr) -{ - if (*dst) - talloc_free(*dst); - *dst = talloc_strdup(ctx, newstr); -} diff --git a/openbsc/src/common/debug.c b/openbsc/src/common/debug.c deleted file mode 100644 index ea5db49e5..000000000 --- a/openbsc/src/common/debug.c +++ /dev/null @@ -1,249 +0,0 @@ -/* OpenBSC Debugging/Logging support code */ - -/* (C) 2008-2010 by Harald Welte - * (C) 2008 by Holger Hans Peter Freyther - * All Rights Reserved - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation; either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - * - */ - -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include - -/* 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, - }, - [DSMS] = { - .name = "DSMS", - .description = "Layer3 Short Message Service (SMS)", - .color = "\033[1;37m", - .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, - }, - [DMI] = { - .name = "DMI", - .description = "A-bis Input Driver for Signalling", - .enabled = 0, .loglevel = LOGL_NOTICE, - }, - [DMIB] = { - .name = "DMIB", - .description = "A-bis Input Driver for B-Channels (voice)", - .enabled = 0, .loglevel = LOGL_NOTICE, - }, - [DMUX] = { - .name = "DMUX", - .description = "A-bis B-Subchannel TRAU Frame Multiplex", - .enabled = 1, .loglevel = LOGL_NOTICE, - }, - [DINP] = { - .name = "DINP", - .description = "A-bis Intput Subsystem", - .enabled = 1, .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/Multipkexer", - .enabled = 1, .loglevel = LOGL_NOTICE, - }, -}; - -enum log_filter { - _FLT_ALL = LOG_FILTER_ALL, /* libosmocore */ - FLT_IMSI = 1, - FLT_NSVC = 2, - FLT_BVC = 3, -}; - -static int filter_fn(const struct log_context *ctx, - struct log_target *tar) -{ - struct gsm_subscriber *subscr = ctx->ctx[BSC_CTX_SUBSCR]; - const struct gprs_nsvc *nsvc = ctx->ctx[BSC_CTX_NSVC]; - const struct gprs_nsvc *bvc = ctx->ctx[BSC_CTX_BVC]; - - if ((tar->filter_map & (1 << FLT_IMSI)) != 0 - && subscr && strcmp(subscr->imsi, tar->filter_data[FLT_IMSI]) == 0) - return 1; - - /* Filter on the NS Virtual Connection */ - if ((tar->filter_map & (1 << FLT_NSVC)) != 0 - && nsvc && (nsvc == tar->filter_data[FLT_NSVC])) - return 1; - - /* Filter on the NS Virtual Connection */ - if ((tar->filter_map & (1 << FLT_BVC)) != 0 - && bvc && (bvc == tar->filter_data[FLT_BVC])) - return 1; - - return 0; -} - -const struct log_info log_info = { - .filter_fn = filter_fn, - .cat = default_categories, - .num_cat = ARRAY_SIZE(default_categories), -}; - -void log_set_imsi_filter(struct log_target *target, const char *imsi) -{ - if (imsi) { - target->filter_map |= (1 << FLT_IMSI); - target->filter_data[FLT_IMSI] = talloc_strdup(target, imsi); - } else if (target->filter_data[FLT_IMSI]) { - target->filter_map &= ~(1 << FLT_IMSI); - talloc_free(target->filter_data[FLT_IMSI]); - target->filter_data[FLT_IMSI] = NULL; - } -} - -void log_set_nsvc_filter(struct log_target *target, struct gprs_nsvc *nsvc) -{ - if (nsvc) { - target->filter_map |= (1 << FLT_NSVC); - target->filter_data[FLT_NSVC] = nsvc; - } else if (target->filter_data[FLT_NSVC]) { - target->filter_map = ~(1 << FLT_NSVC); - target->filter_data[FLT_NSVC] = NULL; - } -} - -void log_set_bvc_filter(struct log_target *target, struct bssgp_bvc_ctx *bctx) -{ - if (bctx) { - target->filter_map |= (1 << FLT_BVC); - target->filter_data[FLT_BVC] = bctx; - } else if (target->filter_data[FLT_NSVC]) { - target->filter_map = ~(1 << FLT_BVC); - target->filter_data[FLT_BVC] = NULL; - } -} diff --git a/openbsc/src/common/gsm_data.c b/openbsc/src/common/gsm_data.c deleted file mode 100644 index f181fd162..000000000 --- a/openbsc/src/common/gsm_data.c +++ /dev/null @@ -1,589 +0,0 @@ -/* (C) 2008-2010 by Harald Welte - * - * All Rights Reserved - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation; either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - * - */ - - -#include -#include -#include -#include -#include - -#include - -#include -#include -#include -#include - -#include -#include -#include - -void *tall_bsc_ctx; - -static LLIST_HEAD(bts_models); - -void set_ts_e1link(struct gsm_bts_trx_ts *ts, u_int8_t e1_nr, - u_int8_t e1_ts, u_int8_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 const struct value_string pchan_names[] = { - { 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" }, - { 0, NULL } -}; - -const char *gsm_pchan_name(enum gsm_phys_chan_config c) -{ - return get_value_string(pchan_names, c); -} - -enum gsm_phys_chan_config gsm_pchan_parse(const char *name) -{ - return get_string_value(pchan_names, name); -} - -static const struct value_string lchant_names[] = { - { GSM_LCHAN_NONE, "NONE" }, - { GSM_LCHAN_SDCCH, "SDCCH" }, - { GSM_LCHAN_TCH_F, "TCH/F" }, - { GSM_LCHAN_TCH_H, "TCH/H" }, - { GSM_LCHAN_UNKNOWN, "UNKNOWN" }, - { 0, NULL } -}; - -const char *gsm_lchant_name(enum gsm_chan_t c) -{ - return get_value_string(lchant_names, c); -} - -static const struct value_string lchan_s_names[] = { - { LCHAN_S_NONE, "NONE" }, - { LCHAN_S_ACT_REQ, "ACTIVATION REQUESTED" }, - { LCHAN_S_ACTIVE, "ACTIVE" }, - { LCHAN_S_INACTIVE, "INACTIVE" }, - { LCHAN_S_REL_REQ, "RELEASE REQUESTED" }, - { LCHAN_S_REL_ERR, "RELEASE DUE ERROR" }, - { 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); -} - -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, &nm_att_tlvdef); - llist_add_tail(&model->list, &bts_models); - return 0; -} - - -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->nm_state.administrative = NM_STATE_UNLOCKED; - - 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->hopping.arfcns.data_len = sizeof(ts->hopping.arfcns_data); - ts->hopping.arfcns.data = ts->hopping.arfcns_data; - ts->hopping.ma.data_len = sizeof(ts->hopping.ma_data); - ts->hopping.ma.data = ts->hopping.ma_data; - - for (l = 0; l < TS_MAX_LCHAN; l++) { - struct gsm_lchan *lchan; - lchan = &ts->lchan[l]; - - lchan->ts = ts; - lchan->nr = l; - lchan->type = GSM_LCHAN_NONE; - } - } - - 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 }; - -struct gsm_bts *gsm_bts_alloc(struct gsm_network *net, enum gsm_bts_type type, - u_int8_t tsc, u_int8_t bsic) -{ - struct gsm_bts *bts = talloc_zero(net, struct gsm_bts); - struct gsm_bts_model *model = bts_model_find(type); - int i; - - if (!bts) - return NULL; - - if (!model && type != GSM_BTS_TYPE_UNKNOWN) { - talloc_free(bts); - return NULL; - } - - bts->network = net; - bts->nr = net->num_bts++; - bts->type = type; - bts->model = model; - bts->tsc = tsc; - bts->bsic = bsic; - bts->num_trx = 0; - INIT_LLIST_HEAD(&bts->trx_list); - bts->ms_max_power = 15; /* dBm */ - - bts->neigh_list_manual_mode = 0; - bts->si_common.cell_sel_par.cell_resel_hyst = 2; /* 4 dB */ - bts->si_common.cell_sel_par.rxlev_acc_min = 0; - bts->si_common.neigh_list.data = bts->si_common.data.neigh_list; - bts->si_common.neigh_list.data_len = - sizeof(bts->si_common.data.neigh_list); - bts->si_common.si5_neigh_list.data = bts->si_common.data.si5_neigh_list; - bts->si_common.si5_neigh_list.data_len = - sizeof(bts->si_common.data.si5_neigh_list); - bts->si_common.cell_alloc.data = bts->si_common.data.cell_alloc; - bts->si_common.cell_alloc.data_len = - sizeof(bts->si_common.data.cell_alloc); - bts->si_common.rach_control.re = 1; /* no re-establishment */ - bts->si_common.rach_control.tx_integer = 9; /* 12 slots spread - 217/115 slots delay */ - bts->si_common.rach_control.max_trans = 3; /* 7 retransmissions */ - bts->si_common.rach_control.t2 = 4; /* no emergency calls */ - - for (i = 0; i < ARRAY_SIZE(bts->gprs.nsvc); i++) { - bts->gprs.nsvc[i].bts = bts; - bts->gprs.nsvc[i].id = i; - } - memcpy(&bts->gprs.nse.timer, bts_nse_timer_default, - sizeof(bts->gprs.nse.timer)); - memcpy(&bts->gprs.cell.timer, bts_cell_timer_default, - sizeof(bts->gprs.cell.timer)); - - /* 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; - INIT_LLIST_HEAD(&bts->abis_queue); - - llist_add_tail(&bts->list, &net->bts_list); - - return bts; -} - -struct gsm_network *gsm_network_init(u_int16_t country_code, u_int16_t network_code, - int (*mncc_recv)(struct gsm_network *, struct msgb *)) -{ - struct gsm_network *net; - - net = talloc_zero(tall_bsc_ctx, struct gsm_network); - if (!net) - return NULL; - - net->msc_data = talloc_zero(net, struct osmo_msc_data); - if (!net->msc_data) { - talloc_free(net); - return NULL; - } - - net->country_code = country_code; - net->network_code = network_code; - net->num_bts = 0; - net->reject_cause = GSM48_REJECT_ROAMING_NOT_ALLOWED; - net->T3101 = GSM_T3101_DEFAULT; - net->T3113 = GSM_T3113_DEFAULT; - /* FIXME: initialize all other timers! */ - - /* default set of handover parameters */ - net->handover.win_rxlev_avg = 10; - net->handover.win_rxqual_avg = 1; - net->handover.win_rxlev_avg_neigh = 10; - net->handover.pwr_interval = 6; - net->handover.pwr_hysteresis = 3; - net->handover.max_distance = 9999; - - INIT_LLIST_HEAD(&net->trans_list); - INIT_LLIST_HEAD(&net->upqueue); - INIT_LLIST_HEAD(&net->bts_list); - - net->stats.chreq.total = counter_alloc("net.chreq.total"); - net->stats.chreq.no_channel = counter_alloc("net.chreq.no_channel"); - net->stats.handover.attempted = counter_alloc("net.handover.attempted"); - net->stats.handover.no_channel = counter_alloc("net.handover.no_channel"); - net->stats.handover.timeout = counter_alloc("net.handover.timeout"); - net->stats.handover.completed = counter_alloc("net.handover.completed"); - net->stats.handover.failed = counter_alloc("net.handover.failed"); - net->stats.loc_upd_type.attach = counter_alloc("net.loc_upd_type.attach"); - net->stats.loc_upd_type.normal = counter_alloc("net.loc_upd_type.normal"); - net->stats.loc_upd_type.periodic = counter_alloc("net.loc_upd_type.periodic"); - net->stats.loc_upd_type.detach = counter_alloc("net.imsi_detach.count"); - net->stats.loc_upd_resp.reject = counter_alloc("net.loc_upd_resp.reject"); - net->stats.loc_upd_resp.accept = counter_alloc("net.loc_upd_resp.accept"); - net->stats.paging.attempted = counter_alloc("net.paging.attempted"); - net->stats.paging.detached = counter_alloc("net.paging.detached"); - net->stats.paging.completed = counter_alloc("net.paging.completed"); - net->stats.paging.expired = counter_alloc("net.paging.expired"); - net->stats.sms.submitted = counter_alloc("net.sms.submitted"); - net->stats.sms.no_receiver = counter_alloc("net.sms.no_receiver"); - net->stats.sms.delivered = counter_alloc("net.sms.delivered"); - net->stats.sms.rp_err_mem = counter_alloc("net.sms.rp_err_mem"); - net->stats.sms.rp_err_other = counter_alloc("net.sms.rp_err_other"); - net->stats.call.mo_setup = counter_alloc("net.call.mo_setup"); - net->stats.call.mo_connect_ack = counter_alloc("net.call.mo_connect_ack"); - net->stats.call.mt_setup = counter_alloc("net.call.mt_setup"); - net->stats.call.mt_connect = counter_alloc("net.call.mt_connect"); - net->stats.chan.rf_fail = counter_alloc("net.chan.rf_fail"); - net->stats.chan.rll_err = counter_alloc("net.chan.rll_err"); - net->stats.bts.oml_fail = counter_alloc("net.bts.oml_fail"); - net->stats.bts.rsl_fail = counter_alloc("net.bts.rsl_fail"); - - net->mncc_recv = mncc_recv; - - net->msc_data->msc_ip = talloc_strdup(net, "127.0.0.1"); - net->msc_data->msc_port = 5000; - net->msc_data->ping_timeout = 20; - net->msc_data->pong_timeout = 5; - net->msc_data->core_ncc = -1; - net->msc_data->core_mcc = -1; - net->msc_data->rtp_base = 4000; - - gsm_net_update_ctype(net); - - return net; -} - -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; -} - -/* Get reference to a neighbor cell on a given BCCH ARFCN */ -struct gsm_bts *gsm_bts_neighbor(const struct gsm_bts *bts, - u_int16_t arfcn, u_int8_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; -} - -struct gsm_bts_trx *gsm_bts_trx_num(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(struct gsm_bts_trx *trx) -{ - snprintf(ts2str, sizeof(ts2str), "(bts=%d,trx=%d)", - trx->bts->nr, trx->nr); - - return ts2str; -} - - -char *gsm_ts_name(struct gsm_bts_trx_ts *ts) -{ - snprintf(ts2str, sizeof(ts2str), "(bts=%d,trx=%d,ts=%d)", - ts->trx->bts->nr, ts->trx->nr, ts->nr); - - return ts2str; -} - -char *gsm_lchan_name(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; -} - -static const struct value_string bts_types[] = { - { GSM_BTS_TYPE_UNKNOWN, "unknown" }, - { GSM_BTS_TYPE_BS11, "bs11" }, - { GSM_BTS_TYPE_NANOBTS, "nanobts" }, - { GSM_BTS_TYPE_RBS2000, "rbs2000" }, - { 0, NULL } -}; - -enum gsm_bts_type parse_btstype(const char *arg) -{ - return get_string_value(bts_types, arg); -} - -const char *btstype2str(enum gsm_bts_type type) -{ - return get_value_string(bts_types, type); -} - -struct gsm_bts_trx *gsm_bts_trx_by_nr(struct gsm_bts *bts, int nr) -{ - struct gsm_bts_trx *trx; - - llist_for_each_entry(trx, &bts->trx_list, list) { - if (trx->nr == nr) - return trx; - } - return NULL; -} - -/* Search for a BTS in the given Location Area; optionally start searching - * with start_bts (for continuing to search after the first result) */ -struct gsm_bts *gsm_bts_by_lac(struct gsm_network *net, unsigned int lac, - struct gsm_bts *start_bts) -{ - int i; - struct gsm_bts *bts; - int skip = 0; - - if (start_bts) - skip = 1; - - for (i = 0; i < net->num_bts; i++) { - bts = gsm_bts_num(net, i); - - if (skip) { - if (start_bts == bts) - skip = 0; - continue; - } - - if (lac == GSM_LAC_RESERVED_ALL_BTS || bts->location_area_code == lac) - return bts; - } - return NULL; -} - -static const struct value_string auth_policy_names[] = { - { GSM_AUTH_POLICY_CLOSED, "closed" }, - { GSM_AUTH_POLICY_ACCEPT_ALL, "accept-all" }, - { GSM_AUTH_POLICY_TOKEN, "token" }, - { 0, NULL } -}; - -enum gsm_auth_policy gsm_auth_policy_parse(const char *arg) -{ - return get_string_value(auth_policy_names, arg); -} - -const char *gsm_auth_policy_name(enum gsm_auth_policy policy) -{ - return get_value_string(auth_policy_names, policy); -} - -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(u_int8_t *buf, struct gsm_bts *bts) -{ - struct gprs_ra_id raid; - - gprs_ra_id_by_bts(&raid, bts); - - return gsm48_construct_ra(buf, &raid); -} - -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) -{ - return get_string_value(bts_gprs_mode_names, arg); -} - -const char *bts_gprs_mode_name(enum bts_gprs_mode mode) -{ - return get_value_string(bts_gprs_mode_names, mode); -} - -struct gsm_meas_rep *lchan_next_meas_rep(struct gsm_lchan *lchan) -{ - struct gsm_meas_rep *meas_rep; - - meas_rep = &lchan->meas_rep[lchan->meas_rep_idx]; - memset(meas_rep, 0, sizeof(*meas_rep)); - meas_rep->lchan = lchan; - lchan->meas_rep_idx = (lchan->meas_rep_idx + 1) - % ARRAY_SIZE(lchan->meas_rep); - - return meas_rep; -} - -int gsm_btsmodel_set_feature(struct gsm_bts_model *bts, enum gsm_bts_features feat) -{ - return bitvec_set_bit_pos(&bts->features, feat, 1); -} - -int gsm_bts_has_feature(struct gsm_bts *bts, enum gsm_bts_features feat) -{ - return bitvec_get_bit_pos(&bts->model->features, feat); -} - -int gsm_set_bts_type(struct gsm_bts *bts, enum gsm_bts_type type) -{ - struct gsm_bts_model *model; - - model = bts_model_find(type); - if (!model) - return -EINVAL; - - bts->type = type; - bts->model = model; - - switch (bts->type) { - case GSM_BTS_TYPE_NANOBTS: - /* Set the default OML Stream ID to 0xff */ - bts->oml_tei = 0xff; - bts->c0->nominal_power = 23; - break; - case GSM_BTS_TYPE_BS11: - case GSM_BTS_TYPE_UNKNOWN: - break; - case GSM_BTS_TYPE_RBS2000: - INIT_LLIST_HEAD(&bts->rbs2000.is.conn_groups); - INIT_LLIST_HEAD(&bts->rbs2000.con.conn_groups); - break; - } - - return 0; -} diff --git a/openbsc/src/common/socket.c b/openbsc/src/common/socket.c deleted file mode 100644 index 47778e746..000000000 --- a/openbsc/src/common/socket.c +++ /dev/null @@ -1,108 +0,0 @@ -/* OpenBSC sokcet code, taken from Abis input driver for ip.access */ - -/* (C) 2009 by Harald Welte - * (C) 2010 by Holger Hans Peter Freyther - * (C) 2010 by On-Waves - * - * All Rights Reserved - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation; either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - * - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include - -int make_sock(struct bsc_fd *bfd, int proto, u_int32_t ip, u_int16_t port, - int (*cb)(struct bsc_fd *fd, unsigned int what)) -{ - 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; - case IPPROTO_GRE: - type = SOCK_RAW; - break; - default: - return -EINVAL; - } - - bfd->fd = socket(AF_INET, type, proto); - bfd->cb = cb; - bfd->when = BSC_FD_READ; - //bfd->data = line; - - if (bfd->fd < 0) { - LOGP(DINP, 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) - 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(DINP, 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 = bsc_register_fd(bfd); - if (ret < 0) { - perror("register_listen_fd"); - close(bfd->fd); - return ret; - } - return 0; -} diff --git a/openbsc/src/common/talloc_ctx.c b/openbsc/src/common/talloc_ctx.c deleted file mode 100644 index 8e7ec230f..000000000 --- a/openbsc/src/common/talloc_ctx.c +++ /dev/null @@ -1,38 +0,0 @@ -#include -#include - -extern void *tall_msgb_ctx; -extern void *tall_fle_ctx; -extern void *tall_locop_ctx; -extern void *tall_authciphop_ctx; -extern void *tall_gsms_ctx; -extern void *tall_subscr_ctx; -extern void *tall_sub_req_ctx; -extern void *tall_call_ctx; -extern void *tall_paging_ctx; -extern void *tall_sigh_ctx; -extern void *tall_tqe_ctx; -extern void *tall_trans_ctx; -extern void *tall_map_ctx; -extern void *tall_upq_ctx; -extern void *tall_ctr_ctx; - -void talloc_ctx_init(void) -{ - tall_msgb_ctx = talloc_named_const(tall_bsc_ctx, 0, "msgb"); - tall_fle_ctx = talloc_named_const(tall_bsc_ctx, 0, - "bs11_file_list_entry"); - tall_locop_ctx = talloc_named_const(tall_bsc_ctx, 0, "loc_updating_oper"); - tall_authciphop_ctx = talloc_named_const(tall_bsc_ctx, 0, "auth_ciph_oper"); - tall_gsms_ctx = talloc_named_const(tall_bsc_ctx, 0, "sms"); - tall_subscr_ctx = talloc_named_const(tall_bsc_ctx, 0, "subscriber"); - tall_sub_req_ctx = talloc_named_const(tall_bsc_ctx, 0, "subscr_request"); - tall_call_ctx = talloc_named_const(tall_bsc_ctx, 0, "gsm_call"); - tall_paging_ctx = talloc_named_const(tall_bsc_ctx, 0, "paging_request"); - tall_sigh_ctx = talloc_named_const(tall_bsc_ctx, 0, "signal_handler"); - tall_tqe_ctx = talloc_named_const(tall_bsc_ctx, 0, "subch_txq_entry"); - tall_trans_ctx = talloc_named_const(tall_bsc_ctx, 0, "transaction"); - tall_map_ctx = talloc_named_const(tall_bsc_ctx, 0, "trau_map_entry"); - tall_upq_ctx = talloc_named_const(tall_bsc_ctx, 0, "trau_upq_entry"); - tall_ctr_ctx = talloc_named_const(tall_bsc_ctx, 0, "counter"); -} diff --git a/openbsc/src/gb/Makefile.am b/openbsc/src/gb/Makefile.am deleted file mode 100644 index b48b17791..000000000 --- a/openbsc/src/gb/Makefile.am +++ /dev/null @@ -1,9 +0,0 @@ -INCLUDES = $(all_includes) -I$(top_srcdir)/include -I$(top_builddir) -AM_CFLAGS=-Wall -fno-strict-aliasing $(LIBOSMOCORE_CFLAGS) $(LIBOSMOVTY_CFLAGS) $(COVERAGE_CFLAGS) -AM_LDFLAGS = $(LIBOSMOCORE_LIBS) $(LIBOSMOVTY_LIBS) $(COVERAGE_LDFLAGS) - -noinst_LIBRARIES = libgb.a - -libgb_a_SOURCES = gprs_ns.c gprs_ns_frgre.c gprs_ns_vty.c \ - gprs_bssgp.c gprs_bssgp_util.c gprs_bssgp_vty.c -#gprs_llc.c gprs_llc_vty.c crc24.c diff --git a/openbsc/src/gb/gprs_bssgp.c b/openbsc/src/gb/gprs_bssgp.c deleted file mode 100644 index eca34b989..000000000 --- a/openbsc/src/gb/gprs_bssgp.c +++ /dev/null @@ -1,856 +0,0 @@ -/* GPRS BSSGP protocol implementation as per 3GPP TS 08.18 */ - -/* (C) 2009-2010 by Harald Welte - * - * All Rights Reserved - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation; either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - * - * TODO: - * o properly count incoming BVC-RESET packets in counter group - * o set log context as early as possible for outgoing packets - */ - -#include -#include - -#include - -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include - -void *bssgp_tall_ctx = NULL; - -#define BVC_F_BLOCKED 0x0001 - -enum bssgp_ctr { - BSSGP_CTR_PKTS_IN, - BSSGP_CTR_PKTS_OUT, - BSSGP_CTR_BYTES_IN, - BSSGP_CTR_BYTES_OUT, - BSSGP_CTR_BLOCKED, - BSSGP_CTR_DISCARDED, -}; - -static const struct rate_ctr_desc bssgp_ctr_description[] = { - { "packets.in", "Packets at BSSGP Level ( In)" }, - { "packets.out","Packets at BSSGP Level (Out)" }, - { "bytes.in", "Bytes at BSSGP Level ( In)" }, - { "bytes.out", "Bytes at BSSGP Level (Out)" }, - { "blocked", "BVC Blocking count" }, - { "discarded", "BVC LLC Discarded count" }, -}; - -static const struct rate_ctr_group_desc bssgp_ctrg_desc = { - .group_name_prefix = "bssgp.bss_ctx", - .group_description = "BSSGP Peer Statistics", - .num_ctr = ARRAY_SIZE(bssgp_ctr_description), - .ctr_desc = bssgp_ctr_description, -}; - -LLIST_HEAD(bssgp_bvc_ctxts); - -/* Find a BTS Context based on parsed RA ID and Cell ID */ -struct bssgp_bvc_ctx *btsctx_by_raid_cid(const struct gprs_ra_id *raid, uint16_t cid) -{ - struct bssgp_bvc_ctx *bctx; - - llist_for_each_entry(bctx, &bssgp_bvc_ctxts, list) { - if (!memcmp(&bctx->ra_id, raid, sizeof(bctx->ra_id)) && - bctx->cell_id == cid) - return bctx; - } - return NULL; -} - -/* Find a BTS context based on BVCI+NSEI tuple */ -struct bssgp_bvc_ctx *btsctx_by_bvci_nsei(uint16_t bvci, uint16_t nsei) -{ - struct bssgp_bvc_ctx *bctx; - - llist_for_each_entry(bctx, &bssgp_bvc_ctxts, list) { - if (bctx->nsei == nsei && bctx->bvci == bvci) - return bctx; - } - return NULL; -} - -struct bssgp_bvc_ctx *btsctx_alloc(uint16_t bvci, uint16_t nsei) -{ - struct bssgp_bvc_ctx *ctx; - - ctx = talloc_zero(bssgp_tall_ctx, struct bssgp_bvc_ctx); - if (!ctx) - return NULL; - ctx->bvci = bvci; - ctx->nsei = nsei; - /* FIXME: BVCI is not unique, only BVCI+NSEI ?!? */ - ctx->ctrg = rate_ctr_group_alloc(ctx, &bssgp_ctrg_desc, bvci); - - llist_add(&ctx->list, &bssgp_bvc_ctxts); - - return ctx; -} - -/* Chapter 10.4.5: Flow Control BVC ACK */ -static int bssgp_tx_fc_bvc_ack(uint16_t nsei, uint8_t tag, uint16_t ns_bvci) -{ - struct msgb *msg = bssgp_msgb_alloc(); - struct bssgp_normal_hdr *bgph = - (struct bssgp_normal_hdr *) msgb_put(msg, sizeof(*bgph)); - - msgb_nsei(msg) = nsei; - msgb_bvci(msg) = ns_bvci; - - bgph->pdu_type = BSSGP_PDUT_FLOW_CONTROL_BVC_ACK; - msgb_tvlv_put(msg, BSSGP_IE_TAG, 1, &tag); - - return gprs_ns_sendmsg(bssgp_nsi, msg); -} - -/* 10.3.7 SUSPEND-ACK PDU */ -int bssgp_tx_suspend_ack(uint16_t nsei, uint32_t tlli, - const struct gprs_ra_id *ra_id, uint8_t suspend_ref) -{ - struct msgb *msg = bssgp_msgb_alloc(); - struct bssgp_normal_hdr *bgph = - (struct bssgp_normal_hdr *) msgb_put(msg, sizeof(*bgph)); - uint32_t _tlli; - uint8_t ra[6]; - - msgb_nsei(msg) = nsei; - msgb_bvci(msg) = 0; /* Signalling */ - bgph->pdu_type = BSSGP_PDUT_SUSPEND_ACK; - - _tlli = htonl(tlli); - msgb_tvlv_put(msg, BSSGP_IE_TLLI, 4, (uint8_t *) &_tlli); - gsm48_construct_ra(ra, ra_id); - msgb_tvlv_put(msg, BSSGP_IE_ROUTEING_AREA, 6, ra); - msgb_tvlv_put(msg, BSSGP_IE_SUSPEND_REF_NR, 1, &suspend_ref); - - return gprs_ns_sendmsg(bssgp_nsi, msg); -} - -/* 10.3.8 SUSPEND-NACK PDU */ -int bssgp_tx_suspend_nack(uint16_t nsei, uint32_t tlli, - const struct gprs_ra_id *ra_id, - uint8_t *cause) -{ - struct msgb *msg = bssgp_msgb_alloc(); - struct bssgp_normal_hdr *bgph = - (struct bssgp_normal_hdr *) msgb_put(msg, sizeof(*bgph)); - uint32_t _tlli; - uint8_t ra[6]; - - msgb_nsei(msg) = nsei; - msgb_bvci(msg) = 0; /* Signalling */ - bgph->pdu_type = BSSGP_PDUT_SUSPEND_NACK; - - _tlli = htonl(tlli); - msgb_tvlv_put(msg, BSSGP_IE_TLLI, 4, (uint8_t *) &_tlli); - gsm48_construct_ra(ra, ra_id); - msgb_tvlv_put(msg, BSSGP_IE_ROUTEING_AREA, 6, ra); - if (cause) - msgb_tvlv_put(msg, BSSGP_IE_CAUSE, 1, cause); - - return gprs_ns_sendmsg(bssgp_nsi, msg); -} - -/* 10.3.10 RESUME-ACK PDU */ -int bssgp_tx_resume_ack(uint16_t nsei, uint32_t tlli, - const struct gprs_ra_id *ra_id) -{ - struct msgb *msg = bssgp_msgb_alloc(); - struct bssgp_normal_hdr *bgph = - (struct bssgp_normal_hdr *) msgb_put(msg, sizeof(*bgph)); - uint32_t _tlli; - uint8_t ra[6]; - - msgb_nsei(msg) = nsei; - msgb_bvci(msg) = 0; /* Signalling */ - bgph->pdu_type = BSSGP_PDUT_RESUME_ACK; - - _tlli = htonl(tlli); - msgb_tvlv_put(msg, BSSGP_IE_TLLI, 4, (uint8_t *) &_tlli); - gsm48_construct_ra(ra, ra_id); - msgb_tvlv_put(msg, BSSGP_IE_ROUTEING_AREA, 6, ra); - - return gprs_ns_sendmsg(bssgp_nsi, msg); -} - -/* 10.3.11 RESUME-NACK PDU */ -int bssgp_tx_resume_nack(uint16_t nsei, uint32_t tlli, - const struct gprs_ra_id *ra_id, uint8_t *cause) -{ - struct msgb *msg = bssgp_msgb_alloc(); - struct bssgp_normal_hdr *bgph = - (struct bssgp_normal_hdr *) msgb_put(msg, sizeof(*bgph)); - uint32_t _tlli; - uint8_t ra[6]; - - msgb_nsei(msg) = nsei; - msgb_bvci(msg) = 0; /* Signalling */ - bgph->pdu_type = BSSGP_PDUT_SUSPEND_NACK; - - _tlli = htonl(tlli); - msgb_tvlv_put(msg, BSSGP_IE_TLLI, 4, (uint8_t *) &_tlli); - gsm48_construct_ra(ra, ra_id); - msgb_tvlv_put(msg, BSSGP_IE_ROUTEING_AREA, 6, ra); - if (cause) - msgb_tvlv_put(msg, BSSGP_IE_CAUSE, 1, cause); - - return gprs_ns_sendmsg(bssgp_nsi, msg); -} - -uint16_t bssgp_parse_cell_id(struct gprs_ra_id *raid, const uint8_t *buf) -{ - /* 6 octets RAC */ - gsm48_parse_ra(raid, buf); - /* 2 octets CID */ - return ntohs(*(uint16_t *) (buf+6)); -} - -/* Chapter 8.4 BVC-Reset Procedure */ -static int bssgp_rx_bvc_reset(struct msgb *msg, struct tlv_parsed *tp, - uint16_t ns_bvci) -{ - struct bssgp_bvc_ctx *bctx; - uint16_t nsei = msgb_nsei(msg); - uint16_t bvci; - int rc; - - bvci = ntohs(*(uint16_t *)TLVP_VAL(tp, BSSGP_IE_BVCI)); - DEBUGP(DBSSGP, "BSSGP BVCI=%u Rx RESET cause=%s\n", bvci, - bssgp_cause_str(*TLVP_VAL(tp, BSSGP_IE_CAUSE))); - - /* look-up or create the BTS context for this BVC */ - bctx = btsctx_by_bvci_nsei(bvci, nsei); - if (!bctx) - bctx = btsctx_alloc(bvci, nsei); - - /* As opposed to NS-VCs, BVCs are NOT blocked after RESET */ - bctx->state &= ~BVC_S_BLOCKED; - - /* When we receive a BVC-RESET PDU (at least of a PTP BVCI), the BSS - * informs us about its RAC + Cell ID, so we can create a mapping */ - if (bvci != 0 && bvci != 1) { - if (!TLVP_PRESENT(tp, BSSGP_IE_CELL_ID)) { - LOGP(DBSSGP, LOGL_ERROR, "BSSGP BVCI=%u RESET " - "missing mandatory IE\n", bvci); - return -EINVAL; - } - /* actually extract RAC / CID */ - bctx->cell_id = bssgp_parse_cell_id(&bctx->ra_id, - TLVP_VAL(tp, BSSGP_IE_CELL_ID)); - LOGP(DBSSGP, LOGL_NOTICE, "Cell %u-%u-%u-%u CI %u on BVCI %u\n", - bctx->ra_id.mcc, bctx->ra_id.mnc, bctx->ra_id.lac, - bctx->ra_id.rac, bctx->cell_id, bvci); - } - - /* Acknowledge the RESET to the BTS */ - rc = bssgp_tx_simple_bvci(BSSGP_PDUT_BVC_RESET_ACK, - nsei, bvci, ns_bvci); - return 0; -} - -static int bssgp_rx_bvc_block(struct msgb *msg, struct tlv_parsed *tp) -{ - uint16_t bvci; - struct bssgp_bvc_ctx *ptp_ctx; - - bvci = ntohs(*(uint16_t *)TLVP_VAL(tp, BSSGP_IE_BVCI)); - if (bvci == BVCI_SIGNALLING) { - /* 8.3.2: Signalling BVC shall never be blocked */ - LOGP(DBSSGP, LOGL_ERROR, "NSEI=%u/BVCI=%u " - "received block for signalling BVC!?!\n", - msgb_nsei(msg), msgb_bvci(msg)); - return 0; - } - - LOGP(DBSSGP, LOGL_INFO, "BSSGP BVCI=%u BVC-BLOCK\n", bvci); - - ptp_ctx = btsctx_by_bvci_nsei(bvci, msgb_nsei(msg)); - if (!ptp_ctx) - return bssgp_tx_status(BSSGP_CAUSE_UNKNOWN_BVCI, &bvci, msg); - - ptp_ctx->state |= BVC_S_BLOCKED; - rate_ctr_inc(&ptp_ctx->ctrg->ctr[BSSGP_CTR_BLOCKED]); - - /* FIXME: Send NM_BVC_BLOCK.ind to NM */ - - /* We always acknowledge the BLOCKing */ - return bssgp_tx_simple_bvci(BSSGP_PDUT_BVC_BLOCK_ACK, msgb_nsei(msg), - bvci, msgb_bvci(msg)); -}; - -static int bssgp_rx_bvc_unblock(struct msgb *msg, struct tlv_parsed *tp) -{ - uint16_t bvci; - struct bssgp_bvc_ctx *ptp_ctx; - - bvci = ntohs(*(uint16_t *)TLVP_VAL(tp, BSSGP_IE_BVCI)); - if (bvci == BVCI_SIGNALLING) { - /* 8.3.2: Signalling BVC shall never be blocked */ - LOGP(DBSSGP, LOGL_ERROR, "NSEI=%u/BVCI=%u " - "received unblock for signalling BVC!?!\n", - msgb_nsei(msg), msgb_bvci(msg)); - return 0; - } - - DEBUGP(DBSSGP, "BSSGP BVCI=%u Rx BVC-UNBLOCK\n", bvci); - - ptp_ctx = btsctx_by_bvci_nsei(bvci, msgb_nsei(msg)); - if (!ptp_ctx) - return bssgp_tx_status(BSSGP_CAUSE_UNKNOWN_BVCI, &bvci, msg); - - ptp_ctx->state &= ~BVC_S_BLOCKED; - - /* FIXME: Send NM_BVC_UNBLOCK.ind to NM */ - - /* We always acknowledge the unBLOCKing */ - return bssgp_tx_simple_bvci(BSSGP_PDUT_BVC_UNBLOCK_ACK, msgb_nsei(msg), - bvci, msgb_bvci(msg)); -}; - -/* Uplink unit-data */ -static int bssgp_rx_ul_ud(struct msgb *msg, struct tlv_parsed *tp, - struct bssgp_bvc_ctx *ctx) -{ - struct bssgp_ud_hdr *budh = (struct bssgp_ud_hdr *) msgb_bssgph(msg); - - /* extract TLLI and parse TLV IEs */ - msgb_tlli(msg) = ntohl(budh->tlli); - - DEBUGP(DBSSGP, "BSSGP TLLI=0x%08x UPLINK-UNITDATA\n", msgb_tlli(msg)); - - /* Cell ID and LLC_PDU are the only mandatory IE */ - if (!TLVP_PRESENT(tp, BSSGP_IE_CELL_ID) || - !TLVP_PRESENT(tp, BSSGP_IE_LLC_PDU)) { - LOGP(DBSSGP, LOGL_ERROR, "BSSGP TLLI=0x%08x Rx UL-UD " - "missing mandatory IE\n", msgb_tlli(msg)); - return bssgp_tx_status(BSSGP_CAUSE_MISSING_MAND_IE, NULL, msg); - } - - /* store pointer to LLC header and CELL ID in msgb->cb */ - msgb_llch(msg) = (uint8_t *) TLVP_VAL(tp, BSSGP_IE_LLC_PDU); - msgb_bcid(msg) = (uint8_t *) TLVP_VAL(tp, BSSGP_IE_CELL_ID); - - return gprs_llc_rcvmsg(msg, tp); -} - -static int bssgp_rx_suspend(struct msgb *msg, struct tlv_parsed *tp, - struct bssgp_bvc_ctx *ctx) -{ - struct bssgp_normal_hdr *bgph = - (struct bssgp_normal_hdr *) msgb_bssgph(msg); - struct gprs_ra_id raid; - uint32_t tlli; - int rc; - - if (!TLVP_PRESENT(tp, BSSGP_IE_TLLI) || - !TLVP_PRESENT(tp, BSSGP_IE_ROUTEING_AREA)) { - LOGP(DBSSGP, LOGL_ERROR, "BSSGP BVCI=%u Rx SUSPEND " - "missing mandatory IE\n", ctx->bvci); - return bssgp_tx_status(BSSGP_CAUSE_MISSING_MAND_IE, NULL, msg); - } - - tlli = ntohl(*(uint32_t *)TLVP_VAL(tp, BSSGP_IE_TLLI)); - - DEBUGP(DBSSGP, "BSSGP BVCI=%u TLLI=0x%08x Rx SUSPEND\n", - ctx->bvci, tlli); - - gsm48_parse_ra(&raid, TLVP_VAL(tp, BSSGP_IE_ROUTEING_AREA)); - - /* Inform GMM about the SUSPEND request */ - rc = gprs_gmm_rx_suspend(&raid, tlli); - if (rc < 0) - return bssgp_tx_suspend_nack(msgb_nsei(msg), tlli, &raid, NULL); - - bssgp_tx_suspend_ack(msgb_nsei(msg), tlli, &raid, 0); - - return 0; -} - -static int bssgp_rx_resume(struct msgb *msg, struct tlv_parsed *tp, - struct bssgp_bvc_ctx *ctx) -{ - struct bssgp_normal_hdr *bgph = - (struct bssgp_normal_hdr *) msgb_bssgph(msg); - struct gprs_ra_id raid; - uint32_t tlli; - uint8_t suspend_ref; - int rc; - - if (!TLVP_PRESENT(tp, BSSGP_IE_TLLI) || - !TLVP_PRESENT(tp, BSSGP_IE_ROUTEING_AREA) || - !TLVP_PRESENT(tp, BSSGP_IE_SUSPEND_REF_NR)) { - LOGP(DBSSGP, LOGL_ERROR, "BSSGP BVCI=%u Rx RESUME " - "missing mandatory IE\n", ctx->bvci); - return bssgp_tx_status(BSSGP_CAUSE_MISSING_MAND_IE, NULL, msg); - } - - tlli = ntohl(*(uint32_t *)TLVP_VAL(tp, BSSGP_IE_TLLI)); - suspend_ref = *TLVP_VAL(tp, BSSGP_IE_SUSPEND_REF_NR); - - DEBUGP(DBSSGP, "BSSGP BVCI=%u TLLI=0x%08x RESUME\n", ctx->bvci, tlli); - - gsm48_parse_ra(&raid, TLVP_VAL(tp, BSSGP_IE_ROUTEING_AREA)); - - /* Inform GMM about the RESUME request */ - rc = gprs_gmm_rx_resume(&raid, tlli, suspend_ref); - if (rc < 0) - return bssgp_tx_resume_nack(msgb_nsei(msg), tlli, &raid, - NULL); - - bssgp_tx_resume_ack(msgb_nsei(msg), tlli, &raid); - return 0; -} - - -static int bssgp_rx_llc_disc(struct msgb *msg, struct tlv_parsed *tp, - struct bssgp_bvc_ctx *ctx) -{ - uint32_t tlli = 0; - - if (!TLVP_PRESENT(tp, BSSGP_IE_TLLI) || - !TLVP_PRESENT(tp, BSSGP_IE_LLC_FRAMES_DISCARDED) || - !TLVP_PRESENT(tp, BSSGP_IE_BVCI) || - !TLVP_PRESENT(tp, BSSGP_IE_NUM_OCT_AFF)) { - LOGP(DBSSGP, LOGL_ERROR, "BSSGP BVCI=%u Rx LLC DISCARDED " - "missing mandatory IE\n", ctx->bvci); - } - - if (TLVP_PRESENT(tp, BSSGP_IE_TLLI)) - tlli = ntohl(*(uint32_t *)TLVP_VAL(tp, BSSGP_IE_TLLI)); - - DEBUGP(DBSSGP, "BSSGP BVCI=%u TLLI=%08x LLC DISCARDED\n", - ctx->bvci, tlli); - - rate_ctr_inc(&ctx->ctrg->ctr[BSSGP_CTR_DISCARDED]); - - /* FIXME: send NM_LLC_DISCARDED to NM */ - return 0; -} - -static int bssgp_rx_fc_bvc(struct msgb *msg, struct tlv_parsed *tp, - struct bssgp_bvc_ctx *bctx) -{ - - DEBUGP(DBSSGP, "BSSGP BVCI=%u Rx Flow Control BVC\n", - bctx->bvci); - - if (!TLVP_PRESENT(tp, BSSGP_IE_TAG) || - !TLVP_PRESENT(tp, BSSGP_IE_BVC_BUCKET_SIZE) || - !TLVP_PRESENT(tp, BSSGP_IE_BUCKET_LEAK_RATE) || - !TLVP_PRESENT(tp, BSSGP_IE_BMAX_DEFAULT_MS) || - !TLVP_PRESENT(tp, BSSGP_IE_R_DEFAULT_MS)) { - LOGP(DBSSGP, LOGL_ERROR, "BSSGP BVCI=%u Rx FC BVC " - "missing mandatory IE\n", bctx->bvci); - return bssgp_tx_status(BSSGP_CAUSE_MISSING_MAND_IE, NULL, msg); - } - - /* FIXME: actually implement flow control */ - - /* Send FLOW_CONTROL_BVC_ACK */ - return bssgp_tx_fc_bvc_ack(msgb_nsei(msg), *TLVP_VAL(tp, BSSGP_IE_TAG), - msgb_bvci(msg)); -} - -/* Receive a BSSGP PDU from a BSS on a PTP BVCI */ -static int gprs_bssgp_rx_ptp(struct msgb *msg, struct tlv_parsed *tp, - struct bssgp_bvc_ctx *bctx) -{ - struct bssgp_normal_hdr *bgph = - (struct bssgp_normal_hdr *) msgb_bssgph(msg); - uint8_t pdu_type = bgph->pdu_type; - int rc = 0; - - /* If traffic is received on a BVC that is marked as blocked, the - * received PDU shall not be accepted and a STATUS PDU (Cause value: - * BVC Blocked) shall be sent to the peer entity on the signalling BVC */ - if (bctx->state & BVC_S_BLOCKED && pdu_type != BSSGP_PDUT_STATUS) { - uint16_t bvci = msgb_bvci(msg); - return bssgp_tx_status(BSSGP_CAUSE_BVCI_BLOCKED, &bvci, msg); - } - - switch (pdu_type) { - case BSSGP_PDUT_UL_UNITDATA: - /* some LLC data from the MS */ - rc = bssgp_rx_ul_ud(msg, tp, bctx); - break; - case BSSGP_PDUT_RA_CAPABILITY: - /* BSS requests RA capability or IMSI */ - DEBUGP(DBSSGP, "BSSGP BVCI=%u Rx RA CAPABILITY UPDATE\n", - bctx->bvci); - /* FIXME: send GMM_RA_CAPABILITY_UPDATE.ind to GMM */ - /* FIXME: send RA_CAPA_UPDATE_ACK */ - break; - case BSSGP_PDUT_RADIO_STATUS: - DEBUGP(DBSSGP, "BSSGP BVCI=%u Rx RADIO STATUS\n", bctx->bvci); - /* BSS informs us of some exception */ - /* FIXME: send GMM_RADIO_STATUS.ind to GMM */ - break; - case BSSGP_PDUT_FLOW_CONTROL_BVC: - /* BSS informs us of available bandwidth in Gb interface */ - rc = bssgp_rx_fc_bvc(msg, tp, bctx); - break; - case BSSGP_PDUT_FLOW_CONTROL_MS: - /* BSS informs us of available bandwidth to one MS */ - DEBUGP(DBSSGP, "BSSGP BVCI=%u Rx Flow Control MS\n", - bctx->bvci); - /* FIXME: actually implement flow control */ - /* FIXME: Send FLOW_CONTROL_MS_ACK */ - break; - case BSSGP_PDUT_STATUS: - /* Some exception has occurred */ - /* FIXME: send NM_STATUS.ind to NM */ - case BSSGP_PDUT_DOWNLOAD_BSS_PFC: - case BSSGP_PDUT_CREATE_BSS_PFC_ACK: - case BSSGP_PDUT_CREATE_BSS_PFC_NACK: - case BSSGP_PDUT_MODIFY_BSS_PFC: - case BSSGP_PDUT_DELETE_BSS_PFC_ACK: - DEBUGP(DBSSGP, "BSSGP BVCI=%u Rx PDU type 0x%02x not [yet] " - "implemented\n", bctx->bvci, pdu_type); - rc = bssgp_tx_status(BSSGP_CAUSE_PDU_INCOMP_FEAT, NULL, msg); - break; - /* those only exist in the SGSN -> BSS direction */ - case BSSGP_PDUT_DL_UNITDATA: - case BSSGP_PDUT_PAGING_PS: - case BSSGP_PDUT_PAGING_CS: - case BSSGP_PDUT_RA_CAPA_UPDATE_ACK: - case BSSGP_PDUT_FLOW_CONTROL_BVC_ACK: - case BSSGP_PDUT_FLOW_CONTROL_MS_ACK: - DEBUGP(DBSSGP, "BSSGP BVCI=%u PDU type 0x%02x only exists " - "in DL\n", bctx->bvci, pdu_type); - bssgp_tx_status(BSSGP_CAUSE_PROTO_ERR_UNSPEC, NULL, msg); - rc = -EINVAL; - break; - default: - DEBUGP(DBSSGP, "BSSGP BVCI=%u PDU type 0x%02x unknown\n", - bctx->bvci, pdu_type); - rc = bssgp_tx_status(BSSGP_CAUSE_PROTO_ERR_UNSPEC, NULL, msg); - break; - } - - return rc; -} - -/* Receive a BSSGP PDU from a BSS on a SIGNALLING BVCI */ -static int gprs_bssgp_rx_sign(struct msgb *msg, struct tlv_parsed *tp, - struct bssgp_bvc_ctx *bctx) -{ - struct bssgp_normal_hdr *bgph = - (struct bssgp_normal_hdr *) msgb_bssgph(msg); - uint8_t pdu_type = bgph->pdu_type; - int rc = 0; - uint16_t ns_bvci = msgb_bvci(msg); - uint16_t bvci; - - switch (bgph->pdu_type) { - case BSSGP_PDUT_SUSPEND: - /* MS wants to suspend */ - rc = bssgp_rx_suspend(msg, tp, bctx); - break; - case BSSGP_PDUT_RESUME: - /* MS wants to resume */ - rc = bssgp_rx_resume(msg, tp, bctx); - break; - case BSSGP_PDUT_FLUSH_LL_ACK: - /* BSS informs us it has performed LL FLUSH */ - DEBUGP(DBSSGP, "BSSGP BVCI=%u Rx FLUSH LL ACK\n", bctx->bvci); - /* FIXME: send NM_FLUSH_LL.res to NM */ - break; - case BSSGP_PDUT_LLC_DISCARD: - /* BSS informs that some LLC PDU's have been discarded */ - rc = bssgp_rx_llc_disc(msg, tp, bctx); - break; - case BSSGP_PDUT_BVC_BLOCK: - /* BSS tells us that BVC shall be blocked */ - if (!TLVP_PRESENT(tp, BSSGP_IE_BVCI) || - !TLVP_PRESENT(tp, BSSGP_IE_CAUSE)) { - LOGP(DBSSGP, LOGL_ERROR, "BSSGP Rx BVC-BLOCK " - "missing mandatory IE\n"); - goto err_mand_ie; - } - rc = bssgp_rx_bvc_block(msg, tp); - break; - case BSSGP_PDUT_BVC_UNBLOCK: - /* BSS tells us that BVC shall be unblocked */ - if (!TLVP_PRESENT(tp, BSSGP_IE_BVCI)) { - LOGP(DBSSGP, LOGL_ERROR, "BSSGP Rx BVC-UNBLOCK " - "missing mandatory IE\n"); - goto err_mand_ie; - } - rc = bssgp_rx_bvc_unblock(msg, tp); - break; - case BSSGP_PDUT_BVC_RESET: - /* BSS tells us that BVC init is required */ - if (!TLVP_PRESENT(tp, BSSGP_IE_BVCI) || - !TLVP_PRESENT(tp, BSSGP_IE_CAUSE)) { - LOGP(DBSSGP, LOGL_ERROR, "BSSGP Rx BVC-RESET " - "missing mandatory IE\n"); - goto err_mand_ie; - } - rc = bssgp_rx_bvc_reset(msg, tp, ns_bvci); - break; - case BSSGP_PDUT_STATUS: - /* Some exception has occurred */ - DEBUGP(DBSSGP, "BSSGP BVCI=%u Rx BVC STATUS\n", bctx->bvci); - /* FIXME: send NM_STATUS.ind to NM */ - break; - /* those only exist in the SGSN -> BSS direction */ - case BSSGP_PDUT_PAGING_PS: - case BSSGP_PDUT_PAGING_CS: - case BSSGP_PDUT_SUSPEND_ACK: - case BSSGP_PDUT_SUSPEND_NACK: - case BSSGP_PDUT_RESUME_ACK: - case BSSGP_PDUT_RESUME_NACK: - case BSSGP_PDUT_FLUSH_LL: - case BSSGP_PDUT_BVC_BLOCK_ACK: - case BSSGP_PDUT_BVC_UNBLOCK_ACK: - case BSSGP_PDUT_SGSN_INVOKE_TRACE: - DEBUGP(DBSSGP, "BSSGP BVCI=%u Rx PDU type 0x%02x only exists " - "in DL\n", bctx->bvci, pdu_type); - bssgp_tx_status(BSSGP_CAUSE_PROTO_ERR_UNSPEC, NULL, msg); - rc = -EINVAL; - break; - default: - DEBUGP(DBSSGP, "BSSGP BVCI=%u Rx PDU type 0x%02x unknown\n", - bctx->bvci, pdu_type); - rc = bssgp_tx_status(BSSGP_CAUSE_PROTO_ERR_UNSPEC, NULL, msg); - break; - } - - return rc; -err_mand_ie: - return bssgp_tx_status(BSSGP_CAUSE_MISSING_MAND_IE, NULL, msg); -} - -/* We expect msgb_bssgph() to point to the BSSGP header */ -int gprs_bssgp_rcvmsg(struct msgb *msg) -{ - struct bssgp_normal_hdr *bgph = - (struct bssgp_normal_hdr *) msgb_bssgph(msg); - struct bssgp_ud_hdr *budh = (struct bssgp_ud_hdr *) msgb_bssgph(msg); - struct tlv_parsed tp; - struct bssgp_bvc_ctx *bctx; - uint8_t pdu_type = bgph->pdu_type; - uint16_t ns_bvci = msgb_bvci(msg); - int data_len; - int rc = 0; - - /* Identifiers from DOWN: NSEI, BVCI (both in msg->cb) */ - - /* UNITDATA BSSGP headers have TLLI in front */ - if (pdu_type != BSSGP_PDUT_UL_UNITDATA && - pdu_type != BSSGP_PDUT_DL_UNITDATA) { - data_len = msgb_bssgp_len(msg) - sizeof(*bgph); - rc = bssgp_tlv_parse(&tp, bgph->data, data_len); - } else { - data_len = msgb_bssgp_len(msg) - sizeof(*budh); - rc = bssgp_tlv_parse(&tp, budh->data, data_len); - } - - /* look-up or create the BTS context for this BVC */ - bctx = btsctx_by_bvci_nsei(ns_bvci, msgb_nsei(msg)); - /* Only a RESET PDU can create a new BVC context */ - if (!bctx && pdu_type != BSSGP_PDUT_BVC_RESET) { - LOGP(DBSSGP, LOGL_NOTICE, "NSEI=%u/BVCI=%u Rejecting PDU " - "type %u for unknown BVCI\n", msgb_nsei(msg), ns_bvci, - pdu_type); - return bssgp_tx_status(BSSGP_CAUSE_UNKNOWN_BVCI, NULL, msg); - } - - if (bctx) { - log_set_context(BSC_CTX_BVC, bctx); - rate_ctr_inc(&bctx->ctrg->ctr[BSSGP_CTR_PKTS_IN]); - rate_ctr_add(&bctx->ctrg->ctr[BSSGP_CTR_BYTES_IN], - msgb_bssgp_len(msg)); - } - - if (ns_bvci == BVCI_SIGNALLING) - rc = gprs_bssgp_rx_sign(msg, &tp, bctx); - else if (ns_bvci == BVCI_PTM) - rc = bssgp_tx_status(BSSGP_CAUSE_PDU_INCOMP_FEAT, NULL, msg); - else - rc = gprs_bssgp_rx_ptp(msg, &tp, bctx); - - return rc; -} - -/* 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 */ -int gprs_bssgp_tx_dl_ud(struct msgb *msg, struct sgsn_mm_ctx *mmctx) -{ - struct bssgp_bvc_ctx *bctx; - struct bssgp_ud_hdr *budh; - uint8_t llc_pdu_tlv_hdr_len = 2; - uint8_t *llc_pdu_tlv, *qos_profile; - uint16_t pdu_lifetime = 1000; /* centi-seconds */ - uint8_t qos_profile_default[3] = { 0x00, 0x00, 0x20 }; - uint16_t msg_len = msg->len; - uint16_t bvci = msgb_bvci(msg); - uint16_t nsei = msgb_nsei(msg); - uint16_t drx_params; - - /* Identifiers from UP: TLLI, BVCI, NSEI (all in msgb->cb) */ - if (bvci <= BVCI_PTM ) { - LOGP(DBSSGP, LOGL_ERROR, "Cannot send DL-UD to BVCI %u\n", - bvci); - return -EINVAL; - } - - bctx = btsctx_by_bvci_nsei(bvci, nsei); - if (!bctx) { - /* FIXME: don't simply create missing context, but reject message */ - bctx = btsctx_alloc(bvci, nsei); - } - - if (msg->len > TVLV_MAX_ONEBYTE) - llc_pdu_tlv_hdr_len += 1; - - /* prepend the tag and length of the LLC-PDU TLV */ - llc_pdu_tlv = msgb_push(msg, llc_pdu_tlv_hdr_len); - llc_pdu_tlv[0] = BSSGP_IE_LLC_PDU; - if (llc_pdu_tlv_hdr_len > 2) { - llc_pdu_tlv[1] = msg_len >> 8; - llc_pdu_tlv[2] = msg_len & 0xff; - } else { - llc_pdu_tlv[1] = msg_len & 0x7f; - llc_pdu_tlv[1] |= 0x80; - } - - /* FIXME: optional elements: Alignment, UTRAN CCO, LSA, PFI */ - - if (mmctx) { - /* Old TLLI to help BSS map from old->new */ -#if 0 - if (mmctx->tlli_old) - msgb_tvlv_push(msg, BSSGP_IE_TLLI, 4, htonl(*tlli_old)); -#endif - - /* IMSI */ - if (strlen(mmctx->imsi)) { - uint8_t mi[10]; - int imsi_len = gsm48_generate_mid_from_imsi(mi, mmctx->imsi); - if (imsi_len > 2) - msgb_tvlv_push(msg, BSSGP_IE_IMSI, - imsi_len-2, mi+2); - } - - /* DRX parameters */ - drx_params = htons(mmctx->drx_parms); - msgb_tvlv_push(msg, BSSGP_IE_DRX_PARAMS, 2, - (uint8_t *) &drx_params); - - /* FIXME: Priority */ - - /* MS Radio Access Capability */ - if (mmctx->ms_radio_access_capa.len) - msgb_tvlv_push(msg, BSSGP_IE_MS_RADIO_ACCESS_CAP, - mmctx->ms_radio_access_capa.len, - mmctx->ms_radio_access_capa.buf); - } - - /* prepend the pdu lifetime */ - pdu_lifetime = htons(pdu_lifetime); - msgb_tvlv_push(msg, BSSGP_IE_PDU_LIFETIME, 2, (uint8_t *)&pdu_lifetime); - - /* prepend the QoS profile, TLLI and pdu type */ - budh = (struct bssgp_ud_hdr *) msgb_push(msg, sizeof(*budh)); - memcpy(budh->qos_profile, qos_profile_default, sizeof(qos_profile_default)); - budh->tlli = htonl(msgb_tlli(msg)); - budh->pdu_type = BSSGP_PDUT_DL_UNITDATA; - - rate_ctr_inc(&bctx->ctrg->ctr[BSSGP_CTR_PKTS_OUT]); - rate_ctr_add(&bctx->ctrg->ctr[BSSGP_CTR_BYTES_OUT], msg->len); - - /* Identifiers down: BVCI, NSEI (in msgb->cb) */ - - return gprs_ns_sendmsg(bssgp_nsi, msg); -} - -/* Send a single GMM-PAGING.req to a given NSEI/NS-BVCI */ -int gprs_bssgp_tx_paging(uint16_t nsei, uint16_t ns_bvci, - struct bssgp_paging_info *pinfo) -{ - struct msgb *msg = bssgp_msgb_alloc(); - struct bssgp_normal_hdr *bgph = - (struct bssgp_normal_hdr *) msgb_put(msg, sizeof(*bgph)); - uint16_t drx_params = htons(pinfo->drx_params); - uint8_t mi[10]; - int imsi_len = gsm48_generate_mid_from_imsi(mi, pinfo->imsi); - uint8_t ra[6]; - - if (imsi_len < 2) - return -EINVAL; - - msgb_nsei(msg) = nsei; - msgb_bvci(msg) = ns_bvci; - - if (pinfo->mode == BSSGP_PAGING_PS) - bgph->pdu_type = BSSGP_PDUT_PAGING_PS; - else - bgph->pdu_type = BSSGP_PDUT_PAGING_CS; - /* IMSI */ - msgb_tvlv_put(msg, BSSGP_IE_IMSI, imsi_len-2, mi+2); - /* DRX Parameters */ - msgb_tvlv_put(msg, BSSGP_IE_DRX_PARAMS, 2, - (uint8_t *) &drx_params); - /* Scope */ - switch (pinfo->scope) { - case BSSGP_PAGING_BSS_AREA: - { - uint8_t null = 0; - msgb_tvlv_put(msg, BSSGP_IE_BSS_AREA_ID, 1, &null); - } - break; - case BSSGP_PAGING_LOCATION_AREA: - gsm48_construct_ra(ra, &pinfo->raid); - msgb_tvlv_put(msg, BSSGP_IE_LOCATION_AREA, 4, ra); - break; - case BSSGP_PAGING_ROUTEING_AREA: - gsm48_construct_ra(ra, &pinfo->raid); - msgb_tvlv_put(msg, BSSGP_IE_ROUTEING_AREA, 6, ra); - break; - case BSSGP_PAGING_BVCI: - { - uint16_t bvci = htons(pinfo->bvci); - msgb_tvlv_put(msg, BSSGP_IE_BVCI, 2, (uint8_t *)&bvci); - } - break; - } - /* QoS profile mandatory for PS */ - if (pinfo->mode == BSSGP_PAGING_PS) - msgb_tvlv_put(msg, BSSGP_IE_QOS_PROFILE, 3, pinfo->qos); - - /* Optional (P-)TMSI */ - if (pinfo->ptmsi) { - uint32_t ptmsi = htonl(*pinfo->ptmsi); - msgb_tvlv_put(msg, BSSGP_IE_TMSI, 4, (uint8_t *) &ptmsi); - } - - return gprs_ns_sendmsg(bssgp_nsi, msg); -} diff --git a/openbsc/src/gb/gprs_bssgp_util.c b/openbsc/src/gb/gprs_bssgp_util.c deleted file mode 100644 index f8e3b5699..000000000 --- a/openbsc/src/gb/gprs_bssgp_util.c +++ /dev/null @@ -1,119 +0,0 @@ -/* GPRS BSSGP protocol implementation as per 3GPP TS 08.18 */ - -/* (C) 2009-2010 by Harald Welte - * - * All Rights Reserved - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation; either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - * - */ - -#include -#include - -#include - -#include -#include -#include - -#include -#include -#include -#include - -struct gprs_ns_inst *bssgp_nsi; - -/* BSSGP Protocol specific, not implementation specific */ -/* FIXME: This needs to go into libosmocore after finished */ - -/* Chapter 11.3.9 / Table 11.10: Cause coding */ -static const struct value_string bssgp_cause_strings[] = { - { BSSGP_CAUSE_PROC_OVERLOAD, "Processor overload" }, - { BSSGP_CAUSE_EQUIP_FAIL, "Equipment Failure" }, - { BSSGP_CAUSE_TRASIT_NET_FAIL, "Transit netowkr service failure" }, - { BSSGP_CAUSE_CAPA_GREATER_0KPBS,"Transmission capacity modified" }, - { BSSGP_CAUSE_UNKNOWN_MS, "Unknown MS" }, - { BSSGP_CAUSE_UNKNOWN_BVCI, "Unknown BVCI" }, - { BSSGP_CAUSE_CELL_TRAF_CONG, "Cell traffic congestion" }, - { BSSGP_CAUSE_SGSN_CONG, "SGSN congestion" }, - { BSSGP_CAUSE_OML_INTERV, "O&M intervention" }, - { BSSGP_CAUSE_BVCI_BLOCKED, "BVCI blocked" }, - { BSSGP_CAUSE_PFC_CREATE_FAIL, "PFC create failure" }, - { BSSGP_CAUSE_SEM_INCORR_PDU, "Semantically incorrect PDU" }, - { BSSGP_CAUSE_INV_MAND_INF, "Invalid mandatory information" }, - { BSSGP_CAUSE_MISSING_MAND_IE, "Missing mandatory IE" }, - { BSSGP_CAUSE_MISSING_COND_IE, "Missing conditional IE" }, - { BSSGP_CAUSE_UNEXP_COND_IE, "Unexpected conditional IE" }, - { BSSGP_CAUSE_COND_IE_ERR, "Conditional IE error" }, - { BSSGP_CAUSE_PDU_INCOMP_STATE, "PDU incompatible with protocol state" }, - { BSSGP_CAUSE_PROTO_ERR_UNSPEC, "Protocol error - unspecified" }, - { BSSGP_CAUSE_PDU_INCOMP_FEAT, "PDU not compatible with feature set" }, - { 0, NULL }, -}; - -const char *bssgp_cause_str(enum gprs_bssgp_cause cause) -{ - return get_value_string(bssgp_cause_strings, cause); -} - - -struct msgb *bssgp_msgb_alloc(void) -{ - return msgb_alloc_headroom(4096, 128, "BSSGP"); -} - -/* Transmit a simple response such as BLOCK/UNBLOCK/RESET ACK/NACK */ -int bssgp_tx_simple_bvci(uint8_t pdu_type, uint16_t nsei, - uint16_t bvci, uint16_t ns_bvci) -{ - struct msgb *msg = bssgp_msgb_alloc(); - struct bssgp_normal_hdr *bgph = - (struct bssgp_normal_hdr *) msgb_put(msg, sizeof(*bgph)); - uint16_t _bvci; - - msgb_nsei(msg) = nsei; - msgb_bvci(msg) = ns_bvci; - - bgph->pdu_type = pdu_type; - _bvci = htons(bvci); - msgb_tvlv_put(msg, BSSGP_IE_BVCI, 2, (uint8_t *) &_bvci); - - return gprs_ns_sendmsg(bssgp_nsi, msg); -} - -/* Chapter 10.4.14: Status */ -int bssgp_tx_status(uint8_t cause, uint16_t *bvci, struct msgb *orig_msg) -{ - struct msgb *msg = bssgp_msgb_alloc(); - struct bssgp_normal_hdr *bgph = - (struct bssgp_normal_hdr *) msgb_put(msg, sizeof(*bgph)); - - LOGP(DBSSGP, LOGL_NOTICE, "BSSGP BVCI=%u Tx STATUS, cause=%s\n", - bvci ? *bvci : 0, bssgp_cause_str(cause)); - msgb_nsei(msg) = msgb_nsei(orig_msg); - msgb_bvci(msg) = 0; - - bgph->pdu_type = BSSGP_PDUT_STATUS; - msgb_tvlv_put(msg, BSSGP_IE_CAUSE, 1, &cause); - if (bvci) { - uint16_t _bvci = htons(*bvci); - msgb_tvlv_put(msg, BSSGP_IE_BVCI, 2, (uint8_t *) &_bvci); - } - if (orig_msg) - msgb_tvlv_put(msg, BSSGP_IE_PDU_IN_ERROR, - msgb_bssgp_len(orig_msg), msgb_bssgph(orig_msg)); - - return gprs_ns_sendmsg(bssgp_nsi, msg); -} diff --git a/openbsc/src/gb/gprs_bssgp_vty.c b/openbsc/src/gb/gprs_bssgp_vty.c deleted file mode 100644 index 9ebd09004..000000000 --- a/openbsc/src/gb/gprs_bssgp_vty.c +++ /dev/null @@ -1,176 +0,0 @@ -/* VTY interface for our GPRS BSS Gateway Protocol (BSSGP) implementation */ - -/* (C) 2010 by Harald Welte - * - * All Rights Reserved - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation; either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - * - */ - -#include -#include -#include -#include - -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include - -/* FIXME: this should go to some common file as it is copied - * in vty_interface.c of the BSC */ -static const struct value_string gprs_bssgp_timer_strs[] = { - { 0, NULL } -}; - -static struct cmd_node bssgp_node = { - BSSGP_NODE, - "%s(bssgp)#", - 1, -}; - -static int config_write_bssgp(struct vty *vty) -{ - vty_out(vty, "bssgp%s", VTY_NEWLINE); - - return CMD_SUCCESS; -} - -DEFUN(cfg_bssgp, cfg_bssgp_cmd, - "bssgp", - "Configure the GPRS BSS Gateway Protocol") -{ - vty->node = BSSGP_NODE; - return CMD_SUCCESS; -} - -static void dump_bvc(struct vty *vty, struct bssgp_bvc_ctx *bvc, int stats) -{ - vty_out(vty, "NSEI %5u, BVCI %5u, RA-ID: %u-%u-%u-%u, CID: %u, " - "STATE: %s%s", bvc->nsei, bvc->bvci, bvc->ra_id.mcc, - bvc->ra_id.mnc, bvc->ra_id.lac, bvc->ra_id.rac, bvc->cell_id, - bvc->state & BVC_S_BLOCKED ? "BLOCKED" : "UNBLOCKED", - VTY_NEWLINE); - if (stats) - vty_out_rate_ctr_group(vty, " ", bvc->ctrg); -} - -static void dump_bssgp(struct vty *vty, int stats) -{ - struct bssgp_bvc_ctx *bvc; - - llist_for_each_entry(bvc, &bssgp_bvc_ctxts, list) { - dump_bvc(vty, bvc, stats); - } -} - -#define BSSGP_STR "Show information about the BSSGP protocol\n" - -DEFUN(show_bssgp, show_bssgp_cmd, "show bssgp", - SHOW_STR BSSGP_STR) -{ - dump_bssgp(vty, 0); - return CMD_SUCCESS; -} - -DEFUN(show_bssgp_stats, show_bssgp_stats_cmd, "show bssgp stats", - SHOW_STR BSSGP_STR - "Include statistics\n") -{ - dump_bssgp(vty, 1); - return CMD_SUCCESS; -} - -DEFUN(show_bvc, show_bvc_cmd, "show bssgp nsei <0-65535> [stats]", - SHOW_STR BSSGP_STR - "Show all BVCs on one NSE\n" - "The NSEI\n" "Include Statistics\n") -{ - struct bssgp_bvc_ctx *bvc; - uint16_t nsei = atoi(argv[1]); - int show_stats = 0; - - if (argc >= 2) - show_stats = 1; - - llist_for_each_entry(bvc, &bssgp_bvc_ctxts, list) { - if (bvc->nsei != nsei) - continue; - dump_bvc(vty, bvc, show_stats); - } - - return CMD_SUCCESS; -} - -DEFUN(logging_fltr_bvc, - logging_fltr_bvc_cmd, - "logging filter bvc nsei <0-65535> bvci <0-65535>", - LOGGING_STR FILTER_STR - "Filter based on BSSGP Virtual Connection\n" - "NSEI of the BVC to be filtered\n" - "Network Service Entity Identifier (NSEI)\n" - "BVCI of the BVC to be filtered\n" - "BSSGP Virtual Connection Identifier (BVCI)\n") -{ - struct log_target *tgt = osmo_log_vty2tgt(vty); - struct bssgp_bvc_ctx *bvc; - uint16_t nsei = atoi(argv[0]); - uint16_t bvci = atoi(argv[1]); - - if (!tgt) - return CMD_WARNING; - - bvc = btsctx_by_bvci_nsei(bvci, nsei); - if (!bvc) { - vty_out(vty, "No BVC by that identifier%s", VTY_NEWLINE); - return CMD_WARNING; - } - - log_set_bvc_filter(tgt, bvc); - return CMD_SUCCESS; -} - -int gprs_bssgp_vty_init(void) -{ - install_element_ve(&show_bssgp_cmd); - install_element_ve(&show_bssgp_stats_cmd); - install_element_ve(&show_bvc_cmd); - install_element_ve(&logging_fltr_bvc_cmd); - - install_element(CFG_LOG_NODE, &logging_fltr_bvc_cmd); - - install_element(CONFIG_NODE, &cfg_bssgp_cmd); - install_node(&bssgp_node, config_write_bssgp); - install_default(BSSGP_NODE); - install_element(BSSGP_NODE, &ournode_exit_cmd); - install_element(BSSGP_NODE, &ournode_end_cmd); - //install_element(BSSGP_NODE, &cfg_bssgp_timer_cmd); - - return 0; -} diff --git a/openbsc/src/gb/gprs_ns.c b/openbsc/src/gb/gprs_ns.c deleted file mode 100644 index 5a8e35860..000000000 --- a/openbsc/src/gb/gprs_ns.c +++ /dev/null @@ -1,992 +0,0 @@ -/* GPRS Networks Service (NS) messages on the Gb interfacebvci = msgb_bvci(msg); - * 3GPP TS 08.16 version 8.0.1 Release 1999 / ETSI TS 101 299 V8.0.1 (2002-05) */ - -/* (C) 2009-2010 by Harald Welte - * - * All Rights Reserved - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation; either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - * - */ - -/* Some introduction into NS: NS is used typically on top of frame relay, - * but in the ip.access world it is encapsulated in UDP packets. It serves - * as an intermediate shim betwen BSSGP and the underlying medium. It doesn't - * do much, apart from providing congestion notification and status indication. - * - * Terms: - * NS Network Service - * NSVC NS Virtual Connection - * NSEI NS Entity Identifier - * NSVL NS Virtual Link - * NSVLI NS Virtual Link Identifier - * BVC BSSGP Virtual Connection - * BVCI BSSGP Virtual Connection Identifier - * NSVCG NS Virtual Connection Goup - * Blocked NS-VC cannot be used for user traffic - * Alive Ability of a NS-VC to provide communication - * - * There can be multiple BSSGP virtual connections over one (group of) NSVC's. BSSGP will - * therefore identify the BSSGP virtual connection by a BVCI passed down to NS. - * NS then has to firgure out which NSVC's are responsible for this BVCI. - * Those mappings are administratively configured. - */ - -/* This implementation has the following limitations: - * o Only one NS-VC for each NSE: No load-sharing function - * o NSVCI 65535 and 65534 are reserved for internal use - * o Only UDP is supported as of now, no frame relay support - * o The IP Sub-Network-Service (SNS) as specified in 48.016 is not implemented - * o There are no BLOCK and UNBLOCK timers (yet?) - */ - -#include -#include -#include -#include - -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -static const struct tlv_definition ns_att_tlvdef = { - .def = { - [NS_IE_CAUSE] = { TLV_TYPE_TvLV, 0 }, - [NS_IE_VCI] = { TLV_TYPE_TvLV, 0 }, - [NS_IE_PDU] = { TLV_TYPE_TvLV, 0 }, - [NS_IE_BVCI] = { TLV_TYPE_TvLV, 0 }, - [NS_IE_NSEI] = { TLV_TYPE_TvLV, 0 }, - }, -}; - -enum ns_ctr { - NS_CTR_PKTS_IN, - NS_CTR_PKTS_OUT, - NS_CTR_BYTES_IN, - NS_CTR_BYTES_OUT, - NS_CTR_BLOCKED, - NS_CTR_DEAD, -}; - -static const struct rate_ctr_desc nsvc_ctr_description[] = { - { "packets.in", "Packets at NS Level ( In)" }, - { "packets.out","Packets at NS Level (Out)" }, - { "bytes.in", "Bytes at NS Level ( In)" }, - { "bytes.out", "Bytes at NS Level (Out)" }, - { "blocked", "NS-VC Block count " }, - { "dead", "NS-VC gone dead count " }, -}; - -static const struct rate_ctr_group_desc nsvc_ctrg_desc = { - .group_name_prefix = "ns.nsvc", - .group_description = "NSVC Peer Statistics", - .num_ctr = ARRAY_SIZE(nsvc_ctr_description), - .ctr_desc = nsvc_ctr_description, -}; - -/* Lookup struct gprs_nsvc based on NSVCI */ -struct gprs_nsvc *nsvc_by_nsvci(struct gprs_ns_inst *nsi, uint16_t nsvci) -{ - struct gprs_nsvc *nsvc; - llist_for_each_entry(nsvc, &nsi->gprs_nsvcs, list) { - if (nsvc->nsvci == nsvci) - return nsvc; - } - return NULL; -} - -/* Lookup struct gprs_nsvc based on NSVCI */ -struct gprs_nsvc *nsvc_by_nsei(struct gprs_ns_inst *nsi, uint16_t nsei) -{ - struct gprs_nsvc *nsvc; - llist_for_each_entry(nsvc, &nsi->gprs_nsvcs, list) { - if (nsvc->nsei == nsei) - return nsvc; - } - return NULL; -} - -/* Lookup struct gprs_nsvc based on remote peer socket addr */ -static struct gprs_nsvc *nsvc_by_rem_addr(struct gprs_ns_inst *nsi, - struct sockaddr_in *sin) -{ - struct gprs_nsvc *nsvc; - llist_for_each_entry(nsvc, &nsi->gprs_nsvcs, list) { - if (nsvc->ip.bts_addr.sin_addr.s_addr == - sin->sin_addr.s_addr && - nsvc->ip.bts_addr.sin_port == sin->sin_port) - return nsvc; - } - return NULL; -} - -static void gprs_ns_timer_cb(void *data); - -struct gprs_nsvc *nsvc_create(struct gprs_ns_inst *nsi, uint16_t nsvci) -{ - struct gprs_nsvc *nsvc; - - LOGP(DNS, LOGL_INFO, "NSVCI=%u Creating NS-VC\n", nsvci); - - nsvc = talloc_zero(nsi, struct gprs_nsvc); - nsvc->nsvci = nsvci; - /* before RESET procedure: BLOCKED and DEAD */ - nsvc->state = NSE_S_BLOCKED; - nsvc->nsi = nsi; - nsvc->timer.cb = gprs_ns_timer_cb; - nsvc->timer.data = nsvc; - nsvc->ctrg = rate_ctr_group_alloc(nsvc, &nsvc_ctrg_desc, nsvci); - - llist_add(&nsvc->list, &nsi->gprs_nsvcs); - - return nsvc; -} - -void nsvc_delete(struct gprs_nsvc *nsvc) -{ - if (bsc_timer_pending(&nsvc->timer)) - bsc_del_timer(&nsvc->timer); - llist_del(&nsvc->list); - talloc_free(nsvc); -} - -static void ns_dispatch_signal(struct gprs_nsvc *nsvc, unsigned int signal, - uint8_t cause) -{ - struct ns_signal_data nssd; - - nssd.nsvc = nsvc; - nssd.cause = cause; - - dispatch_signal(SS_NS, signal, &nssd); -} - -/* Section 10.3.2, Table 13 */ -static const struct value_string ns_cause_str[] = { - { NS_CAUSE_TRANSIT_FAIL, "Transit network failure" }, - { NS_CAUSE_OM_INTERVENTION, "O&M intervention" }, - { NS_CAUSE_EQUIP_FAIL, "Equipment failure" }, - { NS_CAUSE_NSVC_BLOCKED, "NS-VC blocked" }, - { NS_CAUSE_NSVC_UNKNOWN, "NS-VC unknown" }, - { NS_CAUSE_BVCI_UNKNOWN, "BVCI unknown" }, - { NS_CAUSE_SEM_INCORR_PDU, "Semantically incorrect PDU" }, - { NS_CAUSE_PDU_INCOMP_PSTATE, "PDU not compatible with protocol state" }, - { NS_CAUSE_PROTO_ERR_UNSPEC, "Protocol error, unspecified" }, - { NS_CAUSE_INVAL_ESSENT_IE, "Invalid essential IE" }, - { NS_CAUSE_MISSING_ESSENT_IE, "Missing essential IE" }, - { 0, NULL } -}; - -const char *gprs_ns_cause_str(enum ns_cause cause) -{ - return get_value_string(ns_cause_str, cause); -} - -static int nsip_sendmsg(struct gprs_nsvc *nsvc, struct msgb *msg); -extern int grps_ns_frgre_sendmsg(struct gprs_nsvc *nsvc, struct msgb *msg); - -static int gprs_ns_tx(struct gprs_nsvc *nsvc, struct msgb *msg) -{ - int ret; - - log_set_context(BSC_CTX_NSVC, nsvc); - - /* Increment number of Uplink bytes */ - rate_ctr_inc(&nsvc->ctrg->ctr[NS_CTR_PKTS_OUT]); - rate_ctr_add(&nsvc->ctrg->ctr[NS_CTR_BYTES_OUT], msgb_l2len(msg)); - - switch (nsvc->ll) { - case GPRS_NS_LL_UDP: - ret = nsip_sendmsg(nsvc, msg); - break; - case GPRS_NS_LL_FR_GRE: - ret = gprs_ns_frgre_sendmsg(nsvc, msg); - break; - default: - LOGP(DNS, LOGL_ERROR, "unsupported NS linklayer %u\n", nsvc->ll); - msgb_free(msg); - ret = -EIO; - break; - } - return ret; -} - -static int gprs_ns_tx_simple(struct gprs_nsvc *nsvc, uint8_t pdu_type) -{ - struct msgb *msg = gprs_ns_msgb_alloc(); - struct gprs_ns_hdr *nsh; - - log_set_context(BSC_CTX_NSVC, nsvc); - - if (!msg) - return -ENOMEM; - - msg->l2h = msgb_put(msg, sizeof(*nsh)); - nsh = (struct gprs_ns_hdr *) msg->l2h; - - nsh->pdu_type = pdu_type; - - return gprs_ns_tx(nsvc, msg); -} - -int gprs_ns_tx_reset(struct gprs_nsvc *nsvc, uint8_t cause) -{ - struct msgb *msg = gprs_ns_msgb_alloc(); - struct gprs_ns_hdr *nsh; - uint16_t nsvci = htons(nsvc->nsvci); - uint16_t nsei = htons(nsvc->nsei); - - log_set_context(BSC_CTX_NSVC, nsvc); - - if (!msg) - return -ENOMEM; - - LOGP(DNS, LOGL_INFO, "NSEI=%u Tx NS RESET (NSVCI=%u, cause=%s)\n", - nsvc->nsei, nsvc->nsvci, gprs_ns_cause_str(cause)); - - msg->l2h = msgb_put(msg, sizeof(*nsh)); - nsh = (struct gprs_ns_hdr *) msg->l2h; - nsh->pdu_type = NS_PDUT_RESET; - - msgb_tvlv_put(msg, NS_IE_CAUSE, 1, &cause); - msgb_tvlv_put(msg, NS_IE_VCI, 2, (uint8_t *) &nsvci); - msgb_tvlv_put(msg, NS_IE_NSEI, 2, (uint8_t *) &nsei); - - return gprs_ns_tx(nsvc, msg); - -} - -int gprs_ns_tx_status(struct gprs_nsvc *nsvc, uint8_t cause, - uint16_t bvci, struct msgb *orig_msg) -{ - struct msgb *msg = gprs_ns_msgb_alloc(); - struct gprs_ns_hdr *nsh; - uint16_t nsvci = htons(nsvc->nsvci); - - log_set_context(BSC_CTX_NSVC, nsvc); - - bvci = htons(bvci); - - if (!msg) - return -ENOMEM; - - LOGP(DNS, LOGL_NOTICE, "NSEI=%u Tx NS STATUS (NSVCI=%u, cause=%s)\n", - nsvc->nsei, nsvc->nsvci, gprs_ns_cause_str(cause)); - - msg->l2h = msgb_put(msg, sizeof(*nsh)); - nsh = (struct gprs_ns_hdr *) msg->l2h; - nsh->pdu_type = NS_PDUT_STATUS; - - msgb_tvlv_put(msg, NS_IE_CAUSE, 1, &cause); - - /* Section 9.2.7.1: Static conditions for NS-VCI */ - if (cause == NS_CAUSE_NSVC_BLOCKED || - cause == NS_CAUSE_NSVC_UNKNOWN) - msgb_tvlv_put(msg, NS_IE_VCI, 2, (uint8_t *)&nsvci); - - /* Section 9.2.7.2: Static conditions for NS PDU */ - switch (cause) { - case NS_CAUSE_SEM_INCORR_PDU: - case NS_CAUSE_PDU_INCOMP_PSTATE: - case NS_CAUSE_PROTO_ERR_UNSPEC: - case NS_CAUSE_INVAL_ESSENT_IE: - case NS_CAUSE_MISSING_ESSENT_IE: - msgb_tvlv_put(msg, NS_IE_PDU, msgb_l2len(orig_msg), - orig_msg->l2h); - break; - default: - break; - } - - /* Section 9.2.7.3: Static conditions for BVCI */ - if (cause == NS_CAUSE_BVCI_UNKNOWN) - msgb_tvlv_put(msg, NS_IE_VCI, 2, (uint8_t *)&bvci); - - return gprs_ns_tx(nsvc, msg); -} - -int gprs_ns_tx_block(struct gprs_nsvc *nsvc, uint8_t cause) -{ - struct msgb *msg = gprs_ns_msgb_alloc(); - struct gprs_ns_hdr *nsh; - uint16_t nsvci = htons(nsvc->nsvci); - - log_set_context(BSC_CTX_NSVC, nsvc); - - if (!msg) - return -ENOMEM; - - LOGP(DNS, LOGL_INFO, "NSEI=%u Tx NS BLOCK (NSVCI=%u, cause=%s)\n", - nsvc->nsei, nsvc->nsvci, gprs_ns_cause_str(cause)); - - /* be conservative and mark it as blocked even now! */ - nsvc->state |= NSE_S_BLOCKED; - rate_ctr_inc(&nsvc->ctrg->ctr[NS_CTR_BLOCKED]); - - msg->l2h = msgb_put(msg, sizeof(*nsh)); - nsh = (struct gprs_ns_hdr *) msg->l2h; - nsh->pdu_type = NS_PDUT_BLOCK; - - msgb_tvlv_put(msg, NS_IE_CAUSE, 1, &cause); - msgb_tvlv_put(msg, NS_IE_VCI, 2, (uint8_t *) &nsvci); - - return gprs_ns_tx(nsvc, msg); -} - -int gprs_ns_tx_unblock(struct gprs_nsvc *nsvc) -{ - log_set_context(BSC_CTX_NSVC, nsvc); - LOGP(DNS, LOGL_INFO, "NSEI=%u Tx NS UNBLOCK (NSVCI=%u)\n", - nsvc->nsei, nsvc->nsvci); - - return gprs_ns_tx_simple(nsvc, NS_PDUT_UNBLOCK); -} - -int gprs_ns_tx_alive(struct gprs_nsvc *nsvc) -{ - log_set_context(BSC_CTX_NSVC, nsvc); - LOGP(DNS, LOGL_DEBUG, "NSEI=%u Tx NS ALIVE (NSVCI=%u)\n", - nsvc->nsei, nsvc->nsvci); - - return gprs_ns_tx_simple(nsvc, NS_PDUT_ALIVE); -} - -int gprs_ns_tx_alive_ack(struct gprs_nsvc *nsvc) -{ - log_set_context(BSC_CTX_NSVC, nsvc); - LOGP(DNS, LOGL_DEBUG, "NSEI=%u Tx NS ALIVE_ACK (NSVCI=%u)\n", - nsvc->nsei, nsvc->nsvci); - - return gprs_ns_tx_simple(nsvc, NS_PDUT_ALIVE_ACK); -} - -static const enum ns_timeout timer_mode_tout[_NSVC_TIMER_NR] = { - [NSVC_TIMER_TNS_RESET] = NS_TOUT_TNS_RESET, - [NSVC_TIMER_TNS_ALIVE] = NS_TOUT_TNS_ALIVE, - [NSVC_TIMER_TNS_TEST] = NS_TOUT_TNS_TEST, -}; - -static const struct value_string timer_mode_strs[] = { - { NSVC_TIMER_TNS_RESET, "tns-reset" }, - { NSVC_TIMER_TNS_ALIVE, "tns-alive" }, - { NSVC_TIMER_TNS_TEST, "tns-test" }, - { 0, NULL } -}; - -static void nsvc_start_timer(struct gprs_nsvc *nsvc, enum nsvc_timer_mode mode) -{ - enum ns_timeout tout = timer_mode_tout[mode]; - unsigned int seconds = nsvc->nsi->timeout[tout]; - - log_set_context(BSC_CTX_NSVC, nsvc); - DEBUGP(DNS, "NSEI=%u Starting timer in mode %s (%u seconds)\n", - nsvc->nsei, get_value_string(timer_mode_strs, mode), - seconds); - - if (bsc_timer_pending(&nsvc->timer)) - bsc_del_timer(&nsvc->timer); - - nsvc->timer_mode = mode; - bsc_schedule_timer(&nsvc->timer, seconds, 0); -} - -static void gprs_ns_timer_cb(void *data) -{ - struct gprs_nsvc *nsvc = data; - enum ns_timeout tout = timer_mode_tout[nsvc->timer_mode]; - unsigned int seconds = nsvc->nsi->timeout[tout]; - - log_set_context(BSC_CTX_NSVC, nsvc); - DEBUGP(DNS, "NSEI=%u Timer expired in mode %s (%u seconds)\n", - nsvc->nsei, get_value_string(timer_mode_strs, nsvc->timer_mode), - seconds); - - switch (nsvc->timer_mode) { - case NSVC_TIMER_TNS_ALIVE: - /* Tns-alive case: we expired without response ! */ - nsvc->alive_retries++; - if (nsvc->alive_retries > - nsvc->nsi->timeout[NS_TOUT_TNS_ALIVE_RETRIES]) { - /* mark as dead and blocked */ - nsvc->state = NSE_S_BLOCKED; - rate_ctr_inc(&nsvc->ctrg->ctr[NS_CTR_BLOCKED]); - rate_ctr_inc(&nsvc->ctrg->ctr[NS_CTR_DEAD]); - LOGP(DNS, LOGL_NOTICE, - "NSEI=%u Tns-alive expired more then " - "%u times, blocking NS-VC\n", nsvc->nsei, - nsvc->nsi->timeout[NS_TOUT_TNS_ALIVE_RETRIES]); - ns_dispatch_signal(nsvc, S_NS_ALIVE_EXP, 0); - ns_dispatch_signal(nsvc, S_NS_BLOCK, NS_CAUSE_NSVC_BLOCKED); - return; - } - /* Tns-test case: send NS-ALIVE PDU */ - gprs_ns_tx_alive(nsvc); - /* start Tns-alive timer */ - nsvc_start_timer(nsvc, NSVC_TIMER_TNS_ALIVE); - break; - case NSVC_TIMER_TNS_TEST: - /* Tns-test case: send NS-ALIVE PDU */ - gprs_ns_tx_alive(nsvc); - /* start Tns-alive timer (transition into faster - * alive retransmissions) */ - nsvc->alive_retries = 0; - nsvc_start_timer(nsvc, NSVC_TIMER_TNS_ALIVE); - break; - case NSVC_TIMER_TNS_RESET: - /* Chapter 7.3: Re-send the RESET */ - gprs_ns_tx_reset(nsvc, NS_CAUSE_OM_INTERVENTION); - /* Re-start Tns-reset timer */ - nsvc_start_timer(nsvc, NSVC_TIMER_TNS_RESET); - break; - case _NSVC_TIMER_NR: - break; - } -} - -/* Section 9.2.6 */ -static int gprs_ns_tx_reset_ack(struct gprs_nsvc *nsvc) -{ - struct msgb *msg = gprs_ns_msgb_alloc(); - struct gprs_ns_hdr *nsh; - uint16_t nsvci, nsei; - - log_set_context(BSC_CTX_NSVC, nsvc); - if (!msg) - return -ENOMEM; - - nsvci = htons(nsvc->nsvci); - nsei = htons(nsvc->nsei); - - msg->l2h = msgb_put(msg, sizeof(*nsh)); - nsh = (struct gprs_ns_hdr *) msg->l2h; - - nsh->pdu_type = NS_PDUT_RESET_ACK; - - LOGP(DNS, LOGL_INFO, "NSEI=%u Tx NS RESET ACK (NSVCI=%u)\n", - nsvc->nsei, nsvc->nsvci); - - msgb_tvlv_put(msg, NS_IE_VCI, 2, (uint8_t *)&nsvci); - msgb_tvlv_put(msg, NS_IE_NSEI, 2, (uint8_t *)&nsei); - - return gprs_ns_tx(nsvc, msg); -} - -/* Section 9.2.10: transmit side / NS-UNITDATA-REQUEST primitive */ -int gprs_ns_sendmsg(struct gprs_ns_inst *nsi, struct msgb *msg) -{ - struct gprs_nsvc *nsvc; - struct gprs_ns_hdr *nsh; - uint16_t bvci = msgb_bvci(msg); - - nsvc = nsvc_by_nsei(nsi, msgb_nsei(msg)); - if (!nsvc) { - LOGP(DNS, LOGL_ERROR, "Unable to resolve NSEI %u " - "to NS-VC!\n", msgb_nsei(msg)); - msgb_free(msg); - return -EINVAL; - } - log_set_context(BSC_CTX_NSVC, nsvc); - - if (!(nsvc->state & NSE_S_ALIVE)) { - LOGP(DNS, LOGL_ERROR, "NSEI=%u is not alive, cannot send\n", - nsvc->nsei); - msgb_free(msg); - return -EBUSY; - } - if (nsvc->state & NSE_S_BLOCKED) { - LOGP(DNS, LOGL_ERROR, "NSEI=%u is blocked, cannot send\n", - nsvc->nsei); - msgb_free(msg); - return -EBUSY; - } - - msg->l2h = msgb_push(msg, sizeof(*nsh) + 3); - nsh = (struct gprs_ns_hdr *) msg->l2h; - if (!nsh) { - LOGP(DNS, LOGL_ERROR, "Not enough headroom for NS header\n"); - msgb_free(msg); - return -EIO; - } - - nsh->pdu_type = NS_PDUT_UNITDATA; - /* spare octet in data[0] */ - nsh->data[1] = bvci >> 8; - nsh->data[2] = bvci & 0xff; - - return gprs_ns_tx(nsvc, msg); -} - -/* Section 9.2.10: receive side */ -static int gprs_ns_rx_unitdata(struct gprs_nsvc *nsvc, struct msgb *msg) -{ - struct gprs_ns_hdr *nsh = (struct gprs_ns_hdr *)msg->l2h; - uint16_t bvci; - - if (nsvc->state & NSE_S_BLOCKED) - return gprs_ns_tx_status(nsvc, NS_CAUSE_NSVC_BLOCKED, - 0, msg); - - /* spare octet in data[0] */ - bvci = nsh->data[1] << 8 | nsh->data[2]; - msgb_bssgph(msg) = &nsh->data[3]; - msgb_bvci(msg) = bvci; - - /* call upper layer (BSSGP) */ - return nsvc->nsi->cb(GPRS_NS_EVT_UNIT_DATA, nsvc, msg, bvci); -} - -/* Section 9.2.7 */ -static int gprs_ns_rx_status(struct gprs_nsvc *nsvc, struct msgb *msg) -{ - struct gprs_ns_hdr *nsh = (struct gprs_ns_hdr *) msg->l2h; - struct tlv_parsed tp; - uint8_t cause; - int rc; - - LOGP(DNS, LOGL_NOTICE, "NSEI=%u Rx NS STATUS ", nsvc->nsei); - - rc = tlv_parse(&tp, &ns_att_tlvdef, nsh->data, - msgb_l2len(msg) - sizeof(*nsh), 0, 0); - if (rc < 0) { - LOGPC(DNS, LOGL_NOTICE, "Error during TLV Parse\n"); - LOGP(DNS, LOGL_ERROR, "NSEI=%u Rx NS STATUS: " - "Error during TLV Parse\n", nsvc->nsei); - return rc; - } - - if (!TLVP_PRESENT(&tp, NS_IE_CAUSE)) { - LOGPC(DNS, LOGL_INFO, "missing cause IE\n"); - return -EINVAL; - } - - cause = *TLVP_VAL(&tp, NS_IE_CAUSE); - LOGPC(DNS, LOGL_NOTICE, "cause=%s\n", gprs_ns_cause_str(cause)); - - return 0; -} - -/* Section 7.3 */ -static int gprs_ns_rx_reset(struct gprs_nsvc *nsvc, struct msgb *msg) -{ - struct gprs_ns_hdr *nsh = (struct gprs_ns_hdr *) msg->l2h; - struct tlv_parsed tp; - uint8_t *cause; - uint16_t *nsvci, *nsei; - int rc; - - rc = tlv_parse(&tp, &ns_att_tlvdef, nsh->data, - msgb_l2len(msg) - sizeof(*nsh), 0, 0); - if (rc < 0) { - LOGP(DNS, LOGL_ERROR, "NSEI=%u Rx NS RESET " - "Error during TLV Parse\n", nsvc->nsei); - return rc; - } - - if (!TLVP_PRESENT(&tp, NS_IE_CAUSE) || - !TLVP_PRESENT(&tp, NS_IE_VCI) || - !TLVP_PRESENT(&tp, NS_IE_NSEI)) { - LOGP(DNS, LOGL_ERROR, "NS RESET Missing mandatory IE\n"); - gprs_ns_tx_status(nsvc, NS_CAUSE_MISSING_ESSENT_IE, 0, msg); - return -EINVAL; - } - - cause = (uint8_t *) TLVP_VAL(&tp, NS_IE_CAUSE); - nsvci = (uint16_t *) TLVP_VAL(&tp, NS_IE_VCI); - nsei = (uint16_t *) TLVP_VAL(&tp, NS_IE_NSEI); - - LOGP(DNS, LOGL_INFO, "NSEI=%u Rx NS RESET (NSVCI=%u, cause=%s)\n", - nsvc->nsvci, nsvc->nsei, gprs_ns_cause_str(*cause)); - - /* Mark NS-VC as blocked and alive */ - nsvc->state = NSE_S_BLOCKED | NSE_S_ALIVE; - - nsvc->nsei = ntohs(*nsei); - nsvc->nsvci = ntohs(*nsvci); - - /* start the test procedure */ - gprs_ns_tx_simple(nsvc, NS_PDUT_ALIVE); - nsvc_start_timer(nsvc, NSVC_TIMER_TNS_TEST); - - /* inform interested parties about the fact that this NSVC - * has received RESET */ - ns_dispatch_signal(nsvc, S_NS_RESET, *cause); - - return gprs_ns_tx_reset_ack(nsvc); -} - -static int gprs_ns_rx_block(struct gprs_nsvc *nsvc, struct msgb *msg) -{ - struct gprs_ns_hdr *nsh = (struct gprs_ns_hdr *) msg->l2h; - struct tlv_parsed tp; - uint8_t *cause; - int rc; - - LOGP(DNS, LOGL_INFO, "NSEI=%u Rx NS BLOCK\n", nsvc->nsei); - - nsvc->state |= NSE_S_BLOCKED; - - rc = tlv_parse(&tp, &ns_att_tlvdef, nsh->data, - msgb_l2len(msg) - sizeof(*nsh), 0, 0); - if (rc < 0) { - LOGP(DNS, LOGL_ERROR, "NSEI=%u Rx NS BLOCK " - "Error during TLV Parse\n", nsvc->nsei); - return rc; - } - - if (!TLVP_PRESENT(&tp, NS_IE_CAUSE) || - !TLVP_PRESENT(&tp, NS_IE_VCI)) { - LOGP(DNS, LOGL_ERROR, "NS RESET Missing mandatory IE\n"); - gprs_ns_tx_status(nsvc, NS_CAUSE_MISSING_ESSENT_IE, 0, msg); - return -EINVAL; - } - - cause = (uint8_t *) TLVP_VAL(&tp, NS_IE_CAUSE); - //nsvci = (uint16_t *) TLVP_VAL(&tp, NS_IE_VCI); - - ns_dispatch_signal(nsvc, S_NS_BLOCK, *cause); - rate_ctr_inc(&nsvc->ctrg->ctr[NS_CTR_BLOCKED]); - - return gprs_ns_tx_simple(nsvc, NS_PDUT_BLOCK_ACK); -} - -/* main entry point, here incoming NS frames enter */ -int gprs_ns_rcvmsg(struct gprs_ns_inst *nsi, struct msgb *msg, - struct sockaddr_in *saddr, enum gprs_ns_ll ll) -{ - struct gprs_ns_hdr *nsh = (struct gprs_ns_hdr *) msg->l2h; - struct gprs_nsvc *nsvc; - int rc = 0; - - /* look up the NSVC based on source address */ - nsvc = nsvc_by_rem_addr(nsi, saddr); - if (!nsvc) { - struct tlv_parsed tp; - uint16_t nsei; - if (nsh->pdu_type == NS_PDUT_STATUS) { - LOGP(DNS, LOGL_INFO, "Ignoring NS STATUS from %s:%u " - "for non-existing NS-VC\n", - inet_ntoa(saddr->sin_addr), ntohs(saddr->sin_port)); - return 0; - } - /* Only the RESET procedure creates a new NSVC */ - if (nsh->pdu_type != NS_PDUT_RESET) { - /* Since we have no NSVC, we have to use a fake */ - nsvc = nsi->unknown_nsvc; - log_set_context(BSC_CTX_NSVC, nsvc); - LOGP(DNS, LOGL_INFO, "Rejecting NS PDU type 0x%0x " - "from %s:%u for non-existing NS-VC\n", - nsh->pdu_type, inet_ntoa(saddr->sin_addr), - ntohs(saddr->sin_port)); - nsvc->nsvci = nsvc->nsei = 0xfffe; - nsvc->ip.bts_addr = *saddr; - nsvc->state = NSE_S_ALIVE; - nsvc->ll = ll; -#if 0 - return gprs_ns_tx_reset(nsvc, NS_CAUSE_PDU_INCOMP_PSTATE); -#else - return gprs_ns_tx_status(nsvc, - NS_CAUSE_PDU_INCOMP_PSTATE, 0, - msg); -#endif - } - rc = tlv_parse(&tp, &ns_att_tlvdef, nsh->data, - msgb_l2len(msg) - sizeof(*nsh), 0, 0); - if (rc < 0) { - LOGP(DNS, LOGL_ERROR, "Rx NS RESET Error %d during " - "TLV Parse\n", rc); - return rc; - } - if (!TLVP_PRESENT(&tp, NS_IE_CAUSE) || - !TLVP_PRESENT(&tp, NS_IE_VCI) || - !TLVP_PRESENT(&tp, NS_IE_NSEI)) { - LOGP(DNS, LOGL_ERROR, "NS RESET Missing mandatory IE\n"); - gprs_ns_tx_status(nsvc, NS_CAUSE_MISSING_ESSENT_IE, 0, - msg); - return -EINVAL; - } - nsei = ntohs(*(uint16_t *)TLVP_VAL(&tp, NS_IE_NSEI)); - /* Check if we already know this NSEI, the remote end might - * simply have changed addresses, or it is a SGSN */ - nsvc = nsvc_by_nsei(nsi, nsei); - if (!nsvc) { - nsvc = nsvc_create(nsi, 0xffff); - nsvc->ll = ll; - log_set_context(BSC_CTX_NSVC, nsvc); - LOGP(DNS, LOGL_INFO, "Creating NS-VC for BSS at %s:%u\n", - inet_ntoa(saddr->sin_addr), ntohs(saddr->sin_port)); - } - /* Update the remote peer IP address/port */ - nsvc->ip.bts_addr = *saddr; - } else - msgb_nsei(msg) = nsvc->nsei; - - log_set_context(BSC_CTX_NSVC, nsvc); - - /* Increment number of Incoming bytes */ - rate_ctr_inc(&nsvc->ctrg->ctr[NS_CTR_PKTS_IN]); - rate_ctr_add(&nsvc->ctrg->ctr[NS_CTR_BYTES_IN], msgb_l2len(msg)); - - switch (nsh->pdu_type) { - case NS_PDUT_ALIVE: - /* If we're dead and blocked and suddenly receive a - * NS-ALIVE out of the blue, we might have been re-started - * and should send a NS-RESET to make sure everything recovers - * fine. */ - if (nsvc->state == NSE_S_BLOCKED) - rc = gprs_ns_tx_reset(nsvc, NS_CAUSE_PDU_INCOMP_PSTATE); - else - rc = gprs_ns_tx_alive_ack(nsvc); - break; - case NS_PDUT_ALIVE_ACK: - /* stop Tns-alive and start Tns-test */ - nsvc_start_timer(nsvc, NSVC_TIMER_TNS_TEST); - if (nsvc->remote_end_is_sgsn) { - /* FIXME: this should be one level higher */ - if (nsvc->state & NSE_S_BLOCKED) - rc = gprs_ns_tx_unblock(nsvc); - } - break; - case NS_PDUT_UNITDATA: - /* actual user data */ - rc = gprs_ns_rx_unitdata(nsvc, msg); - break; - case NS_PDUT_STATUS: - rc = gprs_ns_rx_status(nsvc, msg); - break; - case NS_PDUT_RESET: - rc = gprs_ns_rx_reset(nsvc, msg); - break; - case NS_PDUT_RESET_ACK: - LOGP(DNS, LOGL_INFO, "NSEI=%u Rx NS RESET ACK\n", nsvc->nsei); - /* mark NS-VC as blocked + active */ - nsvc->state = NSE_S_BLOCKED | NSE_S_ALIVE; - nsvc->remote_state = NSE_S_BLOCKED | NSE_S_ALIVE; - rate_ctr_inc(&nsvc->ctrg->ctr[NS_CTR_BLOCKED]); - if (nsvc->persistent || nsvc->remote_end_is_sgsn) { - /* stop RESET timer */ - bsc_del_timer(&nsvc->timer); - } - /* Initiate TEST proc.: Send ALIVE and start timer */ - rc = gprs_ns_tx_simple(nsvc, NS_PDUT_ALIVE); - nsvc_start_timer(nsvc, NSVC_TIMER_TNS_TEST); - break; - case NS_PDUT_UNBLOCK: - /* Section 7.2: unblocking procedure */ - LOGP(DNS, LOGL_INFO, "NSEI=%u Rx NS UNBLOCK\n", nsvc->nsei); - nsvc->state &= ~NSE_S_BLOCKED; - ns_dispatch_signal(nsvc, S_NS_UNBLOCK, 0); - rc = gprs_ns_tx_simple(nsvc, NS_PDUT_UNBLOCK_ACK); - break; - case NS_PDUT_UNBLOCK_ACK: - LOGP(DNS, LOGL_INFO, "NSEI=%u Rx NS UNBLOCK ACK\n", nsvc->nsei); - /* mark NS-VC as unblocked + active */ - nsvc->state = NSE_S_ALIVE; - nsvc->remote_state = NSE_S_ALIVE; - ns_dispatch_signal(nsvc, S_NS_UNBLOCK, 0); - break; - case NS_PDUT_BLOCK: - rc = gprs_ns_rx_block(nsvc, msg); - break; - case NS_PDUT_BLOCK_ACK: - LOGP(DNS, LOGL_INFO, "NSEI=%u Rx NS BLOCK ACK\n", nsvc->nsei); - /* mark remote NS-VC as blocked + active */ - nsvc->remote_state = NSE_S_BLOCKED | NSE_S_ALIVE; - break; - default: - LOGP(DNS, LOGL_NOTICE, "NSEI=%u Rx Unknown NS PDU type 0x%02x\n", - nsvc->nsei, nsh->pdu_type); - rc = -EINVAL; - break; - } - return rc; -} - -struct gprs_ns_inst *gprs_ns_instantiate(gprs_ns_cb_t *cb) -{ - struct gprs_ns_inst *nsi = talloc_zero(tall_bsc_ctx, struct gprs_ns_inst); - - nsi->cb = cb; - INIT_LLIST_HEAD(&nsi->gprs_nsvcs); - nsi->timeout[NS_TOUT_TNS_BLOCK] = 3; - nsi->timeout[NS_TOUT_TNS_BLOCK_RETRIES] = 3; - nsi->timeout[NS_TOUT_TNS_RESET] = 3; - nsi->timeout[NS_TOUT_TNS_RESET_RETRIES] = 3; - nsi->timeout[NS_TOUT_TNS_TEST] = 30; - nsi->timeout[NS_TOUT_TNS_ALIVE] = 3; - nsi->timeout[NS_TOUT_TNS_ALIVE_RETRIES] = 10; - - /* Create the dummy NSVC that we use for sending - * messages to non-existant/unknown NS-VC's */ - nsi->unknown_nsvc = nsvc_create(nsi, 0xfffe); - llist_del(&nsi->unknown_nsvc->list); - - return nsi; -} - -void gprs_ns_destroy(struct gprs_ns_inst *nsi) -{ - /* FIXME: clear all timers */ - - /* recursively free the NSI and all its NSVCs */ - talloc_free(nsi); -} - - -/* NS-over-IP code, according to 3GPP TS 48.016 Chapter 6.2 - * We don't support Size Procedure, Configuration Procedure, ChangeWeight Procedure */ - -/* Read a single NS-over-IP message */ -static struct msgb *read_nsip_msg(struct bsc_fd *bfd, int *error, - struct sockaddr_in *saddr) -{ - struct msgb *msg = gprs_ns_msgb_alloc(); - int ret = 0; - socklen_t saddr_len = sizeof(*saddr); - - if (!msg) { - *error = -ENOMEM; - return NULL; - } - - ret = recvfrom(bfd->fd, msg->data, NS_ALLOC_SIZE - NS_ALLOC_HEADROOM, 0, - (struct sockaddr *)saddr, &saddr_len); - if (ret < 0) { - LOGP(DNS, LOGL_ERROR, "recv error %s during NSIP recv\n", - strerror(errno)); - msgb_free(msg); - *error = ret; - return NULL; - } else if (ret == 0) { - msgb_free(msg); - *error = ret; - return NULL; - } - - msg->l2h = msg->data; - msgb_put(msg, ret); - - return msg; -} - -static int handle_nsip_read(struct bsc_fd *bfd) -{ - int error; - struct sockaddr_in saddr; - struct gprs_ns_inst *nsi = bfd->data; - struct msgb *msg = read_nsip_msg(bfd, &error, &saddr); - - if (!msg) - return error; - - error = gprs_ns_rcvmsg(nsi, msg, &saddr, GPRS_NS_LL_UDP); - - msgb_free(msg); - - return error; -} - -static int handle_nsip_write(struct bsc_fd *bfd) -{ - /* FIXME: actually send the data here instead of nsip_sendmsg() */ - return -EIO; -} - -static int nsip_sendmsg(struct gprs_nsvc *nsvc, struct msgb *msg) -{ - int rc; - struct gprs_ns_inst *nsi = nsvc->nsi; - struct sockaddr_in *daddr = &nsvc->ip.bts_addr; - - rc = sendto(nsi->nsip.fd.fd, msg->data, msg->len, 0, - (struct sockaddr *)daddr, sizeof(*daddr)); - - talloc_free(msg); - - return rc; -} - -/* UDP Port 23000 carries the LLC-in-BSSGP-in-NS protocol stack */ -static int nsip_fd_cb(struct bsc_fd *bfd, unsigned int what) -{ - int rc = 0; - - if (what & BSC_FD_READ) - rc = handle_nsip_read(bfd); - if (what & BSC_FD_WRITE) - rc = handle_nsip_write(bfd); - - return rc; -} - -/* Listen for incoming GPRS packets */ -int gprs_ns_nsip_listen(struct gprs_ns_inst *nsi) -{ - int ret; - - ret = make_sock(&nsi->nsip.fd, IPPROTO_UDP, nsi->nsip.local_ip, - nsi->nsip.local_port, nsip_fd_cb); - if (ret < 0) - return ret; - - nsi->nsip.fd.data = nsi; - - return ret; -} - -/* Initiate a RESET procedure */ -void gprs_nsvc_reset(struct gprs_nsvc *nsvc, uint8_t cause) -{ - LOGP(DNS, LOGL_INFO, "NSEI=%u RESET procedure based on API request\n", - nsvc->nsei); - - /* Mark NS-VC locally as blocked and dead */ - nsvc->state = NSE_S_BLOCKED; - /* Send NS-RESET PDU */ - if (gprs_ns_tx_reset(nsvc, cause) < 0) { - LOGP(DNS, LOGL_ERROR, "NSEI=%u, error resetting NS-VC\n", - nsvc->nsei); - } - /* Start Tns-reset */ - nsvc_start_timer(nsvc, NSVC_TIMER_TNS_RESET); -} - -/* Establish a connection (from the BSS) to the SGSN */ -struct gprs_nsvc *nsip_connect(struct gprs_ns_inst *nsi, - struct sockaddr_in *dest, uint16_t nsei, - uint16_t nsvci) -{ - struct gprs_nsvc *nsvc; - - nsvc = nsvc_by_rem_addr(nsi, dest); - if (!nsvc) - nsvc = nsvc_create(nsi, nsvci); - nsvc->ip.bts_addr = *dest; - nsvc->nsei = nsei; - nsvc->nsvci = nsvci; - nsvc->remote_end_is_sgsn = 1; - - gprs_nsvc_reset(nsvc, NS_CAUSE_OM_INTERVENTION); - return nsvc; -} diff --git a/openbsc/src/gb/gprs_ns_frgre.c b/openbsc/src/gb/gprs_ns_frgre.c deleted file mode 100644 index 106f410e6..000000000 --- a/openbsc/src/gb/gprs_ns_frgre.c +++ /dev/null @@ -1,304 +0,0 @@ -/* GPRS Networks Service (NS) messages on the Gb interface - * 3GPP TS 08.16 version 8.0.1 Release 1999 / ETSI TS 101 299 V8.0.1 (2002-05) */ - -/* NS-over-FR-over-GRE implementation */ - -/* (C) 2009-2010 by Harald Welte - * - * All Rights Reserved - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation; either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - * - */ - -#include -#include -#include - -#include -#include -#include -#include - -#include -#include -#include - -#include -#include -#include - -#define GRE_PTYPE_FR 0x6559 -#define GRE_PTYPE_IPv4 0x0800 -#define GRE_PTYPE_KAR 0x0000 /* keepalive response */ - -struct gre_hdr { - uint16_t flags; - uint16_t ptype; -} __attribute__ ((packed)); - -/* IPv4 messages inside the GRE tunnel might be GRE keepalives */ -static int handle_rx_gre_ipv4(struct bsc_fd *bfd, struct msgb *msg, - struct iphdr *iph, struct gre_hdr *greh) -{ - struct gprs_ns_inst *nsi = bfd->data; - int gre_payload_len; - struct iphdr *inner_iph; - struct gre_hdr *inner_greh; - struct sockaddr_in daddr; - struct in_addr ia; - - gre_payload_len = msg->len - (iph->ihl*4 + sizeof(*greh)); - - inner_iph = (struct iphdr *) ((uint8_t *)greh + sizeof(*greh)); - - if (gre_payload_len < inner_iph->ihl*4 + sizeof(*inner_greh)) { - LOGP(DNS, LOGL_ERROR, "GRE keepalive too short\n"); - return -EIO; - } - - if (inner_iph->saddr != iph->daddr || - inner_iph->daddr != iph->saddr) { - LOGP(DNS, LOGL_ERROR, - "GRE keepalive with wrong tunnel addresses\n"); - return -EIO; - } - - if (inner_iph->protocol != IPPROTO_GRE) { - LOGP(DNS, LOGL_ERROR, "GRE keepalive with wrong protocol\n"); - return -EIO; - } - - inner_greh = (struct gre_hdr *) ((uint8_t *)inner_iph + iph->ihl*4); - if (inner_greh->ptype != htons(GRE_PTYPE_KAR)) { - LOGP(DNS, LOGL_ERROR, "GRE keepalive inner GRE type != 0\n"); - return -EIO; - } - - /* Actually send the response back */ - - daddr.sin_family = AF_INET; - daddr.sin_addr.s_addr = inner_iph->daddr; - daddr.sin_port = IPPROTO_GRE; - - ia.s_addr = iph->saddr; - LOGP(DNS, LOGL_DEBUG, "GRE keepalive from %s, responding\n", - inet_ntoa(ia)); - - return sendto(nsi->frgre.fd.fd, inner_greh, - gre_payload_len - inner_iph->ihl*4, 0, - (struct sockaddr *)&daddr, sizeof(daddr)); -} - -static struct msgb *read_nsfrgre_msg(struct bsc_fd *bfd, int *error, - struct sockaddr_in *saddr) -{ - struct msgb *msg = msgb_alloc(NS_ALLOC_SIZE, "Gb/NS/FR/GRE Rx"); - int ret = 0; - socklen_t saddr_len = sizeof(*saddr); - struct iphdr *iph; - struct gre_hdr *greh; - uint8_t *frh; - uint16_t dlci; - - if (!msg) { - *error = -ENOMEM; - return NULL; - } - - ret = recvfrom(bfd->fd, msg->data, NS_ALLOC_SIZE, 0, - (struct sockaddr *)saddr, &saddr_len); - if (ret < 0) { - LOGP(DNS, LOGL_ERROR, "recv error %s during NS-FR-GRE recv\n", - strerror(errno)); - *error = ret; - goto out_err; - } else if (ret == 0) { - *error = ret; - goto out_err; - } - - msgb_put(msg, ret); - - if (msg->len < sizeof(*iph) + sizeof(*greh) + 2) { - LOGP(DNS, LOGL_ERROR, "Short IP packet: %u bytes\n", msg->len); - *error = -EIO; - goto out_err; - } - - iph = (struct iphdr *) msg->data; - if (msg->len < (iph->ihl*4 + sizeof(*greh) + 2)) { - LOGP(DNS, LOGL_ERROR, "Short IP packet: %u bytes\n", msg->len); - *error = -EIO; - goto out_err; - } - - greh = (struct gre_hdr *) (msg->data + iph->ihl*4); - if (greh->flags) { - LOGP(DNS, LOGL_NOTICE, "Unknown GRE flags 0x%04x\n", - ntohs(greh->flags)); - } - - switch (ntohs(greh->ptype)) { - case GRE_PTYPE_IPv4: - /* IPv4 messages might be GRE keepalives */ - *error = handle_rx_gre_ipv4(bfd, msg, iph, greh); - goto out_err; - break; - case GRE_PTYPE_FR: - /* continue as usual */ - break; - default: - LOGP(DNS, LOGL_NOTICE, "Unknown GRE protocol 0x%04x != FR\n", - ntohs(greh->ptype)); - *error = -EIO; - goto out_err; - break; - } - - if (msg->len < sizeof(*greh) + 2) { - LOGP(DNS, LOGL_ERROR, "Short FR header: %u bytes\n", msg->len); - *error = -EIO; - goto out_err; - } - - frh = (uint8_t *)greh + sizeof(*greh); - if (frh[0] & 0x01) { - LOGP(DNS, LOGL_NOTICE, "Unsupported single-byte FR address\n"); - *error = -EIO; - goto out_err; - } - dlci = ((frh[0] & 0xfc) << 2); - if ((frh[1] & 0x0f) != 0x01) { - LOGP(DNS, LOGL_NOTICE, "Unknown second FR octet 0x%02x\n", - frh[1]); - *error = -EIO; - goto out_err; - } - dlci |= (frh[1] >> 4); - - msg->l2h = frh+2; - - /* Store DLCI in NETWORK BYTEORDER in sockaddr port member */ - saddr->sin_port = htons(dlci); - - return msg; - -out_err: - msgb_free(msg); - return NULL; -} - -int gprs_ns_rcvmsg(struct gprs_ns_inst *nsi, struct msgb *msg, - struct sockaddr_in *saddr, enum gprs_ns_ll ll); - -static int handle_nsfrgre_read(struct bsc_fd *bfd) -{ - int rc; - struct sockaddr_in saddr; - struct gprs_ns_inst *nsi = bfd->data; - struct msgb *msg; - uint16_t dlci; - - msg = read_nsfrgre_msg(bfd, &rc, &saddr); - if (!msg) - return rc; - - dlci = ntohs(saddr.sin_port); - if (dlci == 0 || dlci == 1023) { - LOGP(DNS, LOGL_INFO, "Received FR on LMI DLCI %u - ignoring\n", - dlci); - rc = 0; - goto out; - } - - rc = gprs_ns_rcvmsg(nsi, msg, &saddr, GPRS_NS_LL_FR_GRE); -out: - msgb_free(msg); - - return rc; -} - -static int handle_nsfrgre_write(struct bsc_fd *bfd) -{ - /* FIXME: actually send the data here instead of nsip_sendmsg() */ - return -EIO; -} - -int gprs_ns_frgre_sendmsg(struct gprs_nsvc *nsvc, struct msgb *msg) -{ - int rc; - struct gprs_ns_inst *nsi = nsvc->nsi; - struct sockaddr_in daddr; - uint16_t dlci = ntohs(nsvc->frgre.bts_addr.sin_port); - uint8_t *frh; - struct gre_hdr *greh; - - /* Build socket address for the packet destionation */ - daddr.sin_family = AF_INET; - daddr.sin_addr = nsvc->frgre.bts_addr.sin_addr; - daddr.sin_port = IPPROTO_GRE; - - /* Prepend the FR header */ - frh = msgb_push(msg, 2); - frh[0] = (dlci >> 2) & 0xfc; - frh[1] = ((dlci & 0xf)<<4) | 0x01; - - /* Prepend the GRE header */ - greh = (struct gre_hdr *) msgb_push(msg, sizeof(*greh)); - greh->flags = 0; - greh->ptype = htons(GRE_PTYPE_FR); - - rc = sendto(nsi->frgre.fd.fd, msg->data, msg->len, 0, - (struct sockaddr *)&daddr, sizeof(daddr)); - - talloc_free(msg); - - return rc; -} - -static int nsfrgre_fd_cb(struct bsc_fd *bfd, unsigned int what) -{ - int rc = 0; - - if (what & BSC_FD_READ) - rc = handle_nsfrgre_read(bfd); - if (what & BSC_FD_WRITE) - rc = handle_nsfrgre_write(bfd); - - return rc; -} - -int gprs_ns_frgre_listen(struct gprs_ns_inst *nsi) -{ - int rc; - - /* Make sure we close any existing socket before changing it */ - if (nsi->frgre.fd.fd) - close(nsi->frgre.fd.fd); - - if (!nsi->frgre.enabled) - return 0; - - rc = make_sock(&nsi->frgre.fd, IPPROTO_GRE, nsi->frgre.local_ip, - 0, nsfrgre_fd_cb); - if (rc < 0) { - LOGP(DNS, LOGL_ERROR, "Error creating GRE socket (%s)\n", - strerror(errno)); - return rc; - } - nsi->frgre.fd.data = nsi; - - return rc; -} diff --git a/openbsc/src/gb/gprs_ns_vty.c b/openbsc/src/gb/gprs_ns_vty.c deleted file mode 100644 index 39277fc71..000000000 --- a/openbsc/src/gb/gprs_ns_vty.c +++ /dev/null @@ -1,569 +0,0 @@ -/* VTY interface for our GPRS Networks Service (NS) implementation */ - -/* (C) 2009-2010 by Harald Welte - * - * All Rights Reserved - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation; either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - * - */ - -#include -#include -#include -#include - -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include - -static struct gprs_ns_inst *vty_nsi = NULL; - -/* FIXME: this should go to some common file as it is copied - * in vty_interface.c of the BSC */ -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 struct cmd_node ns_node = { - NS_NODE, - "%s(ns)#", - 1, -}; - -static int config_write_ns(struct vty *vty) -{ - struct gprs_nsvc *nsvc; - unsigned int i; - struct in_addr ia; - - vty_out(vty, "ns%s", VTY_NEWLINE); - - llist_for_each_entry(nsvc, &vty_nsi->gprs_nsvcs, list) { - if (!nsvc->persistent) - continue; - vty_out(vty, " nse %u nsvci %u%s", - nsvc->nsei, nsvc->nsvci, VTY_NEWLINE); - vty_out(vty, " nse %u remote-role %s%s", - nsvc->nsei, nsvc->remote_end_is_sgsn ? "sgsn" : "bss", - VTY_NEWLINE); - switch (nsvc->ll) { - case GPRS_NS_LL_UDP: - vty_out(vty, " nse %u encapsulation udp%s", nsvc->nsei, - VTY_NEWLINE); - vty_out(vty, " nse %u remote-ip %s%s", - nsvc->nsei, - inet_ntoa(nsvc->ip.bts_addr.sin_addr), - VTY_NEWLINE); - vty_out(vty, " nse %u remote-port %u%s", - nsvc->nsei, ntohs(nsvc->ip.bts_addr.sin_port), - VTY_NEWLINE); - break; - case GPRS_NS_LL_FR_GRE: - vty_out(vty, " nse %u encapsulation framerelay-gre%s", - nsvc->nsei, VTY_NEWLINE); - vty_out(vty, " nse %u remote-ip %s%s", - nsvc->nsei, - inet_ntoa(nsvc->frgre.bts_addr.sin_addr), - VTY_NEWLINE); - vty_out(vty, " nse %u fr-dlci %u%s", - nsvc->nsei, ntohs(nsvc->frgre.bts_addr.sin_port), - VTY_NEWLINE); - default: - break; - } - } - - for (i = 0; i < ARRAY_SIZE(vty_nsi->timeout); i++) - vty_out(vty, " timer %s %u%s", - get_value_string(gprs_ns_timer_strs, i), - vty_nsi->timeout[i], VTY_NEWLINE); - - if (vty_nsi->nsip.local_ip) { - ia.s_addr = htonl(vty_nsi->nsip.local_ip); - vty_out(vty, " encapsulation udp local-ip %s%s", - inet_ntoa(ia), VTY_NEWLINE); - } - if (vty_nsi->nsip.local_port) - vty_out(vty, " encapsulation udp local-port %u%s", - vty_nsi->nsip.local_port, VTY_NEWLINE); - - vty_out(vty, " encapsulation framerelay-gre enabled %u%s", - vty_nsi->frgre.enabled ? 1 : 0, VTY_NEWLINE); - if (vty_nsi->frgre.local_ip) { - ia.s_addr = htonl(vty_nsi->frgre.local_ip); - vty_out(vty, " encapsulation framerelay-gre local-ip %s%s", - inet_ntoa(ia), VTY_NEWLINE); - } - - return CMD_SUCCESS; -} - -DEFUN(cfg_ns, cfg_ns_cmd, - "ns", - "Configure the GPRS Network Service") -{ - vty->node = NS_NODE; - return CMD_SUCCESS; -} - -static void dump_nse(struct vty *vty, struct gprs_nsvc *nsvc, int stats) -{ - vty_out(vty, "NSEI %5u, NS-VC %5u, Remote: %-4s, %5s %9s", - nsvc->nsei, nsvc->nsvci, - nsvc->remote_end_is_sgsn ? "SGSN" : "BSS", - nsvc->state & NSE_S_ALIVE ? "ALIVE" : "DEAD", - nsvc->state & NSE_S_BLOCKED ? "BLOCKED" : "UNBLOCKED"); - if (nsvc->ll == GPRS_NS_LL_UDP || nsvc->ll == GPRS_NS_LL_FR_GRE) - vty_out(vty, ", %s %15s:%u", - nsvc->ll == GPRS_NS_LL_UDP ? "UDP " : "FR-GRE", - inet_ntoa(nsvc->ip.bts_addr.sin_addr), - ntohs(nsvc->ip.bts_addr.sin_port)); - vty_out(vty, "%s", VTY_NEWLINE); - if (stats) - vty_out_rate_ctr_group(vty, " ", nsvc->ctrg); -} - -static void dump_ns(struct vty *vty, struct gprs_ns_inst *nsi, int stats) -{ - struct gprs_nsvc *nsvc; - struct in_addr ia; - - ia.s_addr = htonl(vty_nsi->nsip.local_ip); - vty_out(vty, "Encapsulation NS-UDP-IP Local IP: %s, UDP Port: %u%s", - inet_ntoa(ia), vty_nsi->nsip.local_port, VTY_NEWLINE); - - ia.s_addr = htonl(vty_nsi->frgre.local_ip); - vty_out(vty, "Encapsulation NS-FR-GRE-IP Local IP: %s%s", - inet_ntoa(ia), VTY_NEWLINE); - - llist_for_each_entry(nsvc, &nsi->gprs_nsvcs, list) { - if (nsvc == nsi->unknown_nsvc) - continue; - dump_nse(vty, nsvc, stats); - } -} - -DEFUN(show_ns, show_ns_cmd, "show ns", - SHOW_STR "Display information about the NS protocol") -{ - struct gprs_ns_inst *nsi = vty_nsi; - dump_ns(vty, nsi, 0); - return CMD_SUCCESS; -} - -DEFUN(show_ns_stats, show_ns_stats_cmd, "show ns stats", - SHOW_STR - "Display information about the NS protocol\n" - "Include statistics\n") -{ - struct gprs_ns_inst *nsi = vty_nsi; - dump_ns(vty, nsi, 1); - return CMD_SUCCESS; -} - -DEFUN(show_nse, show_nse_cmd, "show ns (nsei|nsvc) <0-65535> [stats]", - SHOW_STR "Display information about the NS protocol\n" - "Select one NSE by its NSE Identifier\n" - "Select one NSE by its NS-VC Identifier\n" - "The Identifier of selected type\n" - "Include Statistics\n") -{ - struct gprs_ns_inst *nsi = vty_nsi; - struct gprs_nsvc *nsvc; - uint16_t id = atoi(argv[1]); - int show_stats = 0; - - if (!strcmp(argv[0], "nsei")) - nsvc = nsvc_by_nsei(nsi, id); - else - nsvc = nsvc_by_nsvci(nsi, id); - - if (!nsvc) { - vty_out(vty, "No such NS Entity%s", VTY_NEWLINE); - return CMD_WARNING; - } - - if (argc >= 3) - show_stats = 1; - - dump_nse(vty, nsvc, show_stats); - return CMD_SUCCESS; -} - -#define NSE_CMD_STR "Persistent NS Entity\n" "NS Entity ID (NSEI)\n" - -DEFUN(cfg_nse_nsvc, cfg_nse_nsvci_cmd, - "nse <0-65535> nsvci <0-65534>", - NSE_CMD_STR - "NS Virtual Connection\n" - "NS Virtual Connection ID (NSVCI)\n" - ) -{ - uint16_t nsei = atoi(argv[0]); - uint16_t nsvci = atoi(argv[1]); - struct gprs_nsvc *nsvc; - - nsvc = nsvc_by_nsei(vty_nsi, nsei); - if (!nsvc) { - nsvc = nsvc_create(vty_nsi, nsvci); - nsvc->nsei = nsei; - } - nsvc->nsvci = nsvci; - /* All NSVCs that are explicitly configured by VTY are - * marked as persistent so we can write them to the config - * file at some later point */ - nsvc->persistent = 1; - - return CMD_SUCCESS; -} - -DEFUN(cfg_nse_remoteip, cfg_nse_remoteip_cmd, - "nse <0-65535> remote-ip A.B.C.D", - NSE_CMD_STR - "Remote IP Address\n" - "Remote IP Address\n") -{ - uint16_t nsei = atoi(argv[0]); - struct gprs_nsvc *nsvc; - - nsvc = nsvc_by_nsei(vty_nsi, nsei); - if (!nsvc) { - vty_out(vty, "No such NSE (%u)%s", nsei, VTY_NEWLINE); - return CMD_WARNING; - } - inet_aton(argv[1], &nsvc->ip.bts_addr.sin_addr); - - return CMD_SUCCESS; - -} - -DEFUN(cfg_nse_remoteport, cfg_nse_remoteport_cmd, - "nse <0-65535> remote-port <0-65535>", - NSE_CMD_STR - "Remote UDP Port\n" - "Remote UDP Port Number\n") -{ - uint16_t nsei = atoi(argv[0]); - uint16_t port = atoi(argv[1]); - struct gprs_nsvc *nsvc; - - nsvc = nsvc_by_nsei(vty_nsi, nsei); - if (!nsvc) { - vty_out(vty, "No such NSE (%u)%s", nsei, VTY_NEWLINE); - return CMD_WARNING; - } - - if (nsvc->ll != GPRS_NS_LL_UDP) { - vty_out(vty, "Cannot set UDP Port on non-UDP NSE%s", - VTY_NEWLINE); - return CMD_WARNING; - } - - nsvc->ip.bts_addr.sin_port = htons(port); - - return CMD_SUCCESS; -} - -DEFUN(cfg_nse_fr_dlci, cfg_nse_fr_dlci_cmd, - "nse <0-65535> fr-dlci <16-1007>", - NSE_CMD_STR - "Frame Relay DLCI\n" - "Frame Relay DLCI Number\n") -{ - uint16_t nsei = atoi(argv[0]); - uint16_t dlci = atoi(argv[1]); - struct gprs_nsvc *nsvc; - - nsvc = nsvc_by_nsei(vty_nsi, nsei); - if (!nsvc) { - vty_out(vty, "No such NSE (%u)%s", nsei, VTY_NEWLINE); - return CMD_WARNING; - } - - if (nsvc->ll != GPRS_NS_LL_FR_GRE) { - vty_out(vty, "Cannot set FR DLCI on non-FR NSE%s", - VTY_NEWLINE); - return CMD_WARNING; - } - - nsvc->frgre.bts_addr.sin_port = htons(dlci); - - return CMD_SUCCESS; -} - -DEFUN(cfg_nse_encaps, cfg_nse_encaps_cmd, - "nse <0-65535> encapsulation (udp|framerelay-gre)", - NSE_CMD_STR - "Encapsulation for NS\n" - "UDP/IP Encapsulation\n" "Frame-Relay/GRE/IP Encapsulation\n") -{ - uint16_t nsei = atoi(argv[0]); - struct gprs_nsvc *nsvc; - - nsvc = nsvc_by_nsei(vty_nsi, nsei); - if (!nsvc) { - vty_out(vty, "No such NSE (%u)%s", nsei, VTY_NEWLINE); - return CMD_WARNING; - } - - if (!strcmp(argv[1], "udp")) - nsvc->ll = GPRS_NS_LL_UDP; - else - nsvc->ll = GPRS_NS_LL_FR_GRE; - - return CMD_SUCCESS; -} - - -DEFUN(cfg_nse_remoterole, cfg_nse_remoterole_cmd, - "nse <0-65535> remote-role (sgsn|bss)", - NSE_CMD_STR - "Remote NSE Role\n" - "Remote Peer is SGSN\n" - "Remote Peer is BSS\n") -{ - uint16_t nsei = atoi(argv[0]); - struct gprs_nsvc *nsvc; - - nsvc = nsvc_by_nsei(vty_nsi, nsei); - if (!nsvc) { - vty_out(vty, "No such NSE (%u)%s", nsei, VTY_NEWLINE); - return CMD_WARNING; - } - - if (!strcmp(argv[1], "sgsn")) - nsvc->remote_end_is_sgsn = 1; - else - nsvc->remote_end_is_sgsn = 0; - - return CMD_SUCCESS; -} - -DEFUN(cfg_no_nse, cfg_no_nse_cmd, - "no nse <0-65535>", - "Delete Persistent NS Entity\n" - "Delete " NSE_CMD_STR) -{ - uint16_t nsei = atoi(argv[0]); - struct gprs_nsvc *nsvc; - - nsvc = nsvc_by_nsei(vty_nsi, nsei); - if (!nsvc) { - vty_out(vty, "No such NSE (%u)%s", nsei, VTY_NEWLINE); - return CMD_WARNING; - } - - if (!nsvc->persistent) { - vty_out(vty, "NSEI %u is not a persistent NSE%s", - nsei, VTY_NEWLINE); - return CMD_WARNING; - } - - nsvc->persistent = 0; - - return CMD_SUCCESS; -} - -DEFUN(cfg_ns_timer, cfg_ns_timer_cmd, - "timer " NS_TIMERS " <0-65535>", - "Network Service Timer\n" - NS_TIMERS_HELP "Timer Value\n") -{ - int idx = get_string_value(gprs_ns_timer_strs, argv[0]); - int val = atoi(argv[1]); - - if (idx < 0 || idx >= ARRAY_SIZE(vty_nsi->timeout)) - return CMD_WARNING; - - vty_nsi->timeout[idx] = val; - - return CMD_SUCCESS; -} - -#define ENCAPS_STR "NS encapsulation options\n" - -DEFUN(cfg_nsip_local_ip, cfg_nsip_local_ip_cmd, - "encapsulation udp local-ip A.B.C.D", - ENCAPS_STR "NS over UDP Encapsulation\n" - "Set the IP address on which we listen for NS/UDP\n" - "IP Address\n") -{ - struct in_addr ia; - - inet_aton(argv[0], &ia); - vty_nsi->nsip.local_ip = ntohl(ia.s_addr); - - return CMD_SUCCESS; -} - -DEFUN(cfg_nsip_local_port, cfg_nsip_local_port_cmd, - "encapsulation udp local-port <0-65535>", - ENCAPS_STR "NS over UDP Encapsulation\n" - "Set the UDP port on which we listen for NS/UDP\n" - "UDP port number\n") -{ - unsigned int port = atoi(argv[0]); - - vty_nsi->nsip.local_port = port; - - return CMD_SUCCESS; -} - -DEFUN(cfg_frgre_local_ip, cfg_frgre_local_ip_cmd, - "encapsulation framerelay-gre local-ip A.B.C.D", - ENCAPS_STR "NS over Frame Relay over GRE Encapsulation\n" - "Set the IP address on which we listen for NS/FR/GRE\n" - "IP Address\n") -{ - struct in_addr ia; - - if (!vty_nsi->frgre.enabled) { - vty_out(vty, "FR/GRE is not enabled%s", VTY_NEWLINE); - return CMD_WARNING; - } - inet_aton(argv[0], &ia); - vty_nsi->frgre.local_ip = ntohl(ia.s_addr); - - return CMD_SUCCESS; -} - -DEFUN(cfg_frgre_enable, cfg_frgre_enable_cmd, - "encapsulation framerelay-gre enabled (1|0)", - ENCAPS_STR "NS over Frame Relay over GRE Encapsulation\n" - "Enable or disable Frame Relay over GRE\n" - "Enable\n" "Disable\n") -{ - int enabled = atoi(argv[0]); - - vty_nsi->frgre.enabled = enabled; - - return CMD_SUCCESS; -} - -DEFUN(nsvc_nsei, nsvc_nsei_cmd, - "nsvc nsei <0-65535> (block|unblock|reset)", - "Perform an operation on a NSVC\n" - "NS-VC Identifier (NS-VCI)\n" - "Initiate BLOCK procedure\n" - "Initiate UNBLOCK procedure\n" - "Initiate RESET procedure\n") -{ - uint16_t nsvci = atoi(argv[0]); - const char *operation = argv[1]; - struct gprs_nsvc *nsvc; - - nsvc = nsvc_by_nsei(vty_nsi, nsvci); - if (!nsvc) { - vty_out(vty, "No such NSVCI (%u)%s", nsvci, VTY_NEWLINE); - return CMD_WARNING; - } - - if (!strcmp(operation, "block")) - gprs_ns_tx_block(nsvc, NS_CAUSE_OM_INTERVENTION); - else if (!strcmp(operation, "unblock")) - gprs_ns_tx_unblock(nsvc); - else if (!strcmp(operation, "reset")) - gprs_nsvc_reset(nsvc, NS_CAUSE_OM_INTERVENTION); - else - return CMD_WARNING; - - return CMD_SUCCESS; -} - -DEFUN(logging_fltr_nsvc, - logging_fltr_nsvc_cmd, - "logging filter nsvc (nsei|nsvci) <0-65535>", - LOGGING_STR FILTER_STR - "Filter based on NS Virtual Connection\n" - "Identify NS-VC by NSEI\n" - "Identify NS-VC by NSVCI\n" - "Numeric identifier\n") -{ - struct log_target *tgt = osmo_log_vty2tgt(vty); - struct gprs_nsvc *nsvc; - uint16_t id = atoi(argv[1]); - - if (!tgt) - return CMD_WARNING; - - if (!strcmp(argv[0], "nsei")) - nsvc = nsvc_by_nsei(vty_nsi, id); - else - nsvc = nsvc_by_nsvci(vty_nsi, id); - - if (!nsvc) { - vty_out(vty, "No NS-VC by that identifier%s", VTY_NEWLINE); - return CMD_WARNING; - } - - log_set_nsvc_filter(tgt, nsvc); - return CMD_SUCCESS; -} - -int gprs_ns_vty_init(struct gprs_ns_inst *nsi) -{ - vty_nsi = nsi; - - install_element_ve(&show_ns_cmd); - install_element_ve(&show_ns_stats_cmd); - install_element_ve(&show_nse_cmd); - install_element_ve(&logging_fltr_nsvc_cmd); - - install_element(CFG_LOG_NODE, &logging_fltr_nsvc_cmd); - - install_element(CONFIG_NODE, &cfg_ns_cmd); - install_node(&ns_node, config_write_ns); - install_default(NS_NODE); - install_element(NS_NODE, &ournode_exit_cmd); - install_element(NS_NODE, &ournode_end_cmd); - install_element(NS_NODE, &cfg_nse_nsvci_cmd); - install_element(NS_NODE, &cfg_nse_remoteip_cmd); - install_element(NS_NODE, &cfg_nse_remoteport_cmd); - install_element(NS_NODE, &cfg_nse_fr_dlci_cmd); - install_element(NS_NODE, &cfg_nse_encaps_cmd); - install_element(NS_NODE, &cfg_nse_remoterole_cmd); - install_element(NS_NODE, &cfg_no_nse_cmd); - install_element(NS_NODE, &cfg_ns_timer_cmd); - install_element(NS_NODE, &cfg_nsip_local_ip_cmd); - install_element(NS_NODE, &cfg_nsip_local_port_cmd); - install_element(NS_NODE, &cfg_frgre_enable_cmd); - install_element(NS_NODE, &cfg_frgre_local_ip_cmd); - - install_element(ENABLE_NODE, &nsvc_nsei_cmd); - - return 0; -} diff --git a/openbsc/src/gprs/Makefile.am b/openbsc/src/gprs/Makefile.am index e98111561..16c2200e5 100644 --- a/openbsc/src/gprs/Makefile.am +++ b/openbsc/src/gprs/Makefile.am @@ -11,9 +11,12 @@ bin_PROGRAMS = osmo-gbproxy endif osmo_gbproxy_SOURCES = gb_proxy.c gb_proxy_main.c gb_proxy_vty.c -osmo_gbproxy_LDADD = $(top_builddir)/src/gb/libgb.a $(top_builddir)/src/common/libcommon.a +osmo_gbproxy_LDADD = $(top_builddir)/src/libgb/libgb.a \ + $(top_builddir)/src/libcommon/libcommon.a osmo_sgsn_SOURCES = gprs_gmm.c gprs_sgsn.c gprs_sndcp.c gprs_sndcp_vty.c \ sgsn_main.c sgsn_vty.c sgsn_libgtp.c \ gprs_llc.c gprs_llc_vty.c crc24.c -osmo_sgsn_LDADD = $(top_builddir)/src/gb/libgb.a $(top_builddir)/src/common/libcommon.a -lgtp +osmo_sgsn_LDADD = $(top_builddir)/src/libgb/libgb.a \ + $(top_builddir)/src/libcommon/libcommon.a \ + -lgtp diff --git a/openbsc/src/ipaccess/Makefile.am b/openbsc/src/ipaccess/Makefile.am index e789a0b08..144cca1ee 100644 --- a/openbsc/src/ipaccess/Makefile.am +++ b/openbsc/src/ipaccess/Makefile.am @@ -9,11 +9,13 @@ ipaccess_find_SOURCES = ipaccess-find.c ipaccess_config_SOURCES = ipaccess-config.c ipaccess-firmware.c network_listen.c # FIXME: resolve the bogus dependencies patched around here: -ipaccess_config_LDADD = $(top_builddir)/src/bsc/libbsc.a $(top_builddir)/src/msc/libmsc.a \ - $(top_builddir)/src/abis/libabis.a $(top_builddir)/src/bsc/libbsc.a \ - $(top_builddir)/src/trau/libtrau.a \ - $(top_builddir)/src/common/libcommon.a \ +ipaccess_config_LDADD = $(top_builddir)/src/libbsc/libbsc.a \ + $(top_builddir)/src/libmsc/libmsc.a \ + $(top_builddir)/src/libabis/libabis.a \ + $(top_builddir)/src/libbsc/libbsc.a \ + $(top_builddir)/src/libtrau/libtrau.a \ + $(top_builddir)/src/libcommon/libcommon.a \ -ldl -ldbi $(LIBCRYPT) ipaccess_proxy_SOURCES = ipaccess-proxy.c -ipaccess_proxy_LDADD = $(top_builddir)/src/common/libcommon.a +ipaccess_proxy_LDADD = $(top_builddir)/src/libcommon/libcommon.a diff --git a/openbsc/src/libabis/Makefile.am b/openbsc/src/libabis/Makefile.am new file mode 100644 index 000000000..7f5ac47cc --- /dev/null +++ b/openbsc/src/libabis/Makefile.am @@ -0,0 +1,13 @@ +INCLUDES = $(all_includes) -I$(top_srcdir)/include -I$(top_builddir) +AM_CFLAGS=-Wall $(LIBOSMOCORE_CFLAGS) $(LIBOSMOVTY_CFLAGS) $(COVERAGE_CFLAGS) +AM_LDFLAGS = $(LIBOSMOCORE_LIBS) $(COVERAGE_LDFLAGS) + +noinst_LIBRARIES = libabis.a + +libabis_a_SOURCES = e1_input.c e1_input_vty.c \ + input/misdn.c \ + input/ipaccess.c \ + input/dahdi.c \ + input/lapd.c + +EXTRA_DIST = input/lapd.h diff --git a/openbsc/src/libabis/e1_input.c b/openbsc/src/libabis/e1_input.c new file mode 100644 index 000000000..e5dd11b94 --- /dev/null +++ b/openbsc/src/libabis/e1_input.c @@ -0,0 +1,649 @@ +/* OpenBSC Abis interface to E1 */ + +/* (C) 2008-2009 by Harald Welte + * + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +//#define AF_COMPATIBILITY_FUNC +//#include +#ifndef AF_ISDN +#define AF_ISDN 34 +#define PF_ISDN AF_ISDN +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "../bscconfig.h" + +#define NUM_E1_TS 32 + +/* list of all E1 drivers */ +LLIST_HEAD(e1inp_driver_list); + +/* list of all E1 lines */ +LLIST_HEAD(e1inp_line_list); + +static void *tall_sigl_ctx; + +/* + * pcap writing of the misdn load + * pcap format is from http://wiki.wireshark.org/Development/LibpcapFileFormat + */ +#define DLT_LINUX_LAPD 177 +#define PCAP_INPUT 0 +#define PCAP_OUTPUT 1 + +struct pcap_hdr { + u_int32_t magic_number; + u_int16_t version_major; + u_int16_t version_minor; + int32_t thiszone; + u_int32_t sigfigs; + u_int32_t snaplen; + u_int32_t network; +} __attribute__((packed)); + +struct pcaprec_hdr { + u_int32_t ts_sec; + u_int32_t ts_usec; + u_int32_t incl_len; + u_int32_t orig_len; +} __attribute__((packed)); + +struct fake_linux_lapd_header { + u_int16_t pkttype; + u_int16_t hatype; + u_int16_t halen; + u_int64_t addr; + int16_t protocol; +} __attribute__((packed)); + +struct lapd_header { + u_int8_t ea1 : 1; + u_int8_t cr : 1; + u_int8_t sapi : 6; + u_int8_t ea2 : 1; + u_int8_t tei : 7; + u_int8_t control_foo; /* fake UM's ... */ +} __attribute__((packed)); + +static_assert(offsetof(struct fake_linux_lapd_header, hatype) == 2, hatype_offset); +static_assert(offsetof(struct fake_linux_lapd_header, halen) == 4, halen_offset); +static_assert(offsetof(struct fake_linux_lapd_header, addr) == 6, addr_offset); +static_assert(offsetof(struct fake_linux_lapd_header, protocol) == 14, proto_offset); +static_assert(sizeof(struct fake_linux_lapd_header) == 16, lapd_header_size); + + +static int pcap_fd = -1; + +void e1_set_pcap_fd(int fd) +{ + int ret; + struct pcap_hdr header = { + .magic_number = 0xa1b2c3d4, + .version_major = 2, + .version_minor = 4, + .thiszone = 0, + .sigfigs = 0, + .snaplen = 65535, + .network = DLT_LINUX_LAPD, + }; + + pcap_fd = fd; + ret = write(pcap_fd, &header, sizeof(header)); +} + +/* This currently only works for the D-Channel */ +static void write_pcap_packet(int direction, int sapi, int tei, + struct msgb *msg) { + if (pcap_fd < 0) + return; + + int ret; + time_t cur_time; + struct tm *tm; + + struct fake_linux_lapd_header header = { + .pkttype = 4, + .hatype = 0, + .halen = 0, + .addr = direction == PCAP_OUTPUT ? 0x0 : 0x1, + .protocol = ntohs(48), + }; + + struct lapd_header lapd_header = { + .ea1 = 0, + .cr = direction == PCAP_OUTPUT ? 1 : 0, + .sapi = sapi & 0x3F, + .ea2 = 1, + .tei = tei & 0x7F, + .control_foo = 0x03 /* UI */, + }; + + struct pcaprec_hdr payload_header = { + .ts_sec = 0, + .ts_usec = 0, + .incl_len = msgb_l2len(msg) + sizeof(struct fake_linux_lapd_header) + + sizeof(struct lapd_header), + .orig_len = msgb_l2len(msg) + sizeof(struct fake_linux_lapd_header) + + sizeof(struct lapd_header), + }; + + + cur_time = time(NULL); + tm = localtime(&cur_time); + payload_header.ts_sec = mktime(tm); + + ret = write(pcap_fd, &payload_header, sizeof(payload_header)); + ret = write(pcap_fd, &header, sizeof(header)); + ret = write(pcap_fd, &lapd_header, sizeof(lapd_header)); + ret = write(pcap_fd, msg->l2h, msgb_l2len(msg)); +} + +static const char *sign_types[] = { + [E1INP_SIGN_NONE] = "None", + [E1INP_SIGN_OML] = "OML", + [E1INP_SIGN_RSL] = "RSL", +}; +const char *e1inp_signtype_name(enum e1inp_sign_type tp) +{ + if (tp >= ARRAY_SIZE(sign_types)) + return "undefined"; + return sign_types[tp]; +} + +static const char *ts_types[] = { + [E1INP_TS_TYPE_NONE] = "None", + [E1INP_TS_TYPE_SIGN] = "Signalling", + [E1INP_TS_TYPE_TRAU] = "TRAU", +}; + +const char *e1inp_tstype_name(enum e1inp_ts_type tp) +{ + if (tp >= ARRAY_SIZE(ts_types)) + return "undefined"; + return ts_types[tp]; +} + +/* callback when a TRAU frame was received */ +static int subch_cb(struct subch_demux *dmx, int ch, u_int8_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); +} + +int abis_rsl_sendmsg(struct msgb *msg) +{ + struct e1inp_sign_link *sign_link; + struct e1inp_driver *e1inp_driver; + struct e1inp_ts *e1i_ts; + + msg->l2h = msg->data; + + if (!msg->trx) { + LOGP(DRSL, LOGL_ERROR, "rsl_sendmsg: msg->trx == NULL: %s\n", + hexdump(msg->data, msg->len)); + talloc_free(msg); + return -EINVAL; + } else if (!msg->trx->rsl_link) { + LOGP(DRSL, LOGL_ERROR, "rsl_sendmsg: msg->trx->rsl_link == NULL: %s\n", + hexdump(msg->data, msg->len)); + talloc_free(msg); + return -EIO; + } + + sign_link = msg->trx->rsl_link; + e1i_ts = sign_link->ts; + if (!bsc_timer_pending(&e1i_ts->sign.tx_timer)) { + /* notify the driver we have something to write */ + e1inp_driver = sign_link->ts->line->driver; + e1inp_driver->want_write(e1i_ts); + } + msgb_enqueue(&sign_link->tx_list, msg); + + /* dump it */ + write_pcap_packet(PCAP_OUTPUT, sign_link->sapi, sign_link->tei, msg); + + return 0; +} + +int _abis_nm_sendmsg(struct msgb *msg, int to_trx_oml) +{ + struct e1inp_sign_link *sign_link; + struct e1inp_driver *e1inp_driver; + struct e1inp_ts *e1i_ts; + + msg->l2h = msg->data; + + if (!msg->trx || !msg->trx->bts || !msg->trx->bts->oml_link) { + LOGP(DNM, LOGL_ERROR, "nm_sendmsg: msg->trx == NULL\n"); + return -EINVAL; + } + + /* Check for TRX-specific OML link first */ + if (to_trx_oml) { + if (!msg->trx->oml_link) + return -ENODEV; + sign_link = msg->trx->oml_link; + } else + sign_link = msg->trx->bts->oml_link; + + e1i_ts = sign_link->ts; + if (!bsc_timer_pending(&e1i_ts->sign.tx_timer)) { + /* notify the driver we have something to write */ + e1inp_driver = sign_link->ts->line->driver; + e1inp_driver->want_write(e1i_ts); + } + msgb_enqueue(&sign_link->tx_list, msg); + + /* dump it */ + write_pcap_packet(PCAP_OUTPUT, sign_link->sapi, sign_link->tei, msg); + + return 0; +} + +/* Timeslot */ + +/* configure and initialize one e1inp_ts */ +int e1inp_ts_config(struct e1inp_ts *ts, struct e1inp_line *line, + enum e1inp_ts_type type) +{ + if (ts->type == type && ts->line && line) + return 0; + + ts->type = type; + ts->line = line; + + switch (type) { + case E1INP_TS_TYPE_SIGN: + if (line && line->driver) + ts->sign.delay = line->driver->default_delay; + else + ts->sign.delay = 100000; + INIT_LLIST_HEAD(&ts->sign.sign_links); + break; + case E1INP_TS_TYPE_TRAU: + subchan_mux_init(&ts->trau.mux); + ts->trau.demux.out_cb = subch_cb; + ts->trau.demux.data = ts; + subch_demux_init(&ts->trau.demux); + break; + default: + LOGP(DMI, LOGL_ERROR, "unsupported E1 timeslot type %u\n", + ts->type); + return -EINVAL; + } + return 0; +} + +struct e1inp_line *e1inp_line_get(u_int8_t e1_nr) +{ + struct e1inp_line *e1i_line; + + /* iterate over global list of e1 lines */ + llist_for_each_entry(e1i_line, &e1inp_line_list, list) { + if (e1i_line->num == e1_nr) + return e1i_line; + } + return NULL; +} + +struct e1inp_line *e1inp_line_create(u_int8_t e1_nr, const char *driver_name) +{ + struct e1inp_driver *driver; + struct e1inp_line *line; + int i; + + line = e1inp_line_get(e1_nr); + if (line) { + LOGP(DINP, LOGL_ERROR, "E1 Line %u already exists\n", + e1_nr); + return NULL; + } + + driver = e1inp_driver_find(driver_name); + if (!driver) { + LOGP(DINP, LOGL_ERROR, "No such E1 driver '%s'\n", + driver_name); + return NULL; + } + + line = talloc_zero(tall_bsc_ctx, struct e1inp_line); + if (!line) + return NULL; + + line->driver = driver; + + line->num = e1_nr; + for (i = 0; i < NUM_E1_TS; i++) { + line->ts[i].num = i+1; + line->ts[i].line = line; + } + llist_add_tail(&line->list, &e1inp_line_list); + + return line; +} + +#if 0 +struct e1inp_line *e1inp_line_get_create(u_int8_t e1_nr) +{ + struct e1inp_line *line; + int i; + + line = e1inp_line_get(e1_nr); + if (line) + return line; + + line = talloc_zero(tall_bsc_ctx, struct e1inp_line); + if (!line) + return NULL; + + line->num = e1_nr; + for (i = 0; i < NUM_E1_TS; i++) { + line->ts[i].num = i+1; + line->ts[i].line = line; + } + llist_add_tail(&line->list, &e1inp_line_list); + + return line; +} +#endif + +static struct e1inp_ts *e1inp_ts_get(u_int8_t e1_nr, u_int8_t ts_nr) +{ + struct e1inp_line *e1i_line; + + e1i_line = e1inp_line_get(e1_nr); + if (!e1i_line) + return NULL; + + return &e1i_line->ts[ts_nr-1]; +} + +struct subch_mux *e1inp_get_mux(u_int8_t e1_nr, u_int8_t ts_nr) +{ + struct e1inp_ts *e1i_ts = e1inp_ts_get(e1_nr, ts_nr); + + if (!e1i_ts) + return NULL; + + return &e1i_ts->trau.mux; +} + +/* Signalling Link */ + +struct e1inp_sign_link *e1inp_lookup_sign_link(struct e1inp_ts *e1i, + u_int8_t tei, u_int8_t sapi) +{ + struct e1inp_sign_link *link; + + llist_for_each_entry(link, &e1i->sign.sign_links, list) { + if (link->sapi == sapi && link->tei == tei) + return link; + } + + return NULL; +} + +/* create a new signalling link in a E1 timeslot */ + +struct e1inp_sign_link * +e1inp_sign_link_create(struct e1inp_ts *ts, enum e1inp_sign_type type, + struct gsm_bts_trx *trx, u_int8_t tei, + u_int8_t sapi) +{ + struct e1inp_sign_link *link; + + if (ts->type != E1INP_TS_TYPE_SIGN) + return NULL; + + link = talloc_zero(tall_sigl_ctx, struct e1inp_sign_link); + if (!link) + return NULL; + + link->ts = ts; + link->type = type; + INIT_LLIST_HEAD(&link->tx_list); + link->trx = trx; + link->tei = tei; + link->sapi = sapi; + + llist_add_tail(&link->list, &ts->sign.sign_links); + + return link; +} + +void e1inp_sign_link_destroy(struct e1inp_sign_link *link) +{ + struct msgb *msg; + + llist_del(&link->list); + while (!llist_empty(&link->tx_list)) { + msg = msgb_dequeue(&link->tx_list); + msgb_free(msg); + } + + if (link->ts->type == E1INP_TS_TYPE_SIGN) + bsc_del_timer(&link->ts->sign.tx_timer); + + talloc_free(link); +} + +/* the E1 driver tells us he has received something on a TS */ +int e1inp_rx_ts(struct e1inp_ts *ts, struct msgb *msg, + u_int8_t tei, u_int8_t sapi) +{ + struct e1inp_sign_link *link; + struct gsm_bts *bts; + int ret; + + switch (ts->type) { + case E1INP_TS_TYPE_SIGN: + /* consult the list of signalling links */ + write_pcap_packet(PCAP_INPUT, sapi, tei, msg); + link = e1inp_lookup_sign_link(ts, tei, sapi); + if (!link) { + LOGP(DMI, LOGL_ERROR, "didn't find signalling link for " + "tei %d, sapi %d\n", tei, sapi); + return -EINVAL; + } + + log_set_context(BSC_CTX_BTS, link->trx->bts); + switch (link->type) { + case E1INP_SIGN_OML: + msg->trx = link->trx; + bts = msg->trx->bts; + ret = bts->model->oml_rcvmsg(msg); + break; + case E1INP_SIGN_RSL: + msg->trx = link->trx; + ret = abis_rsl_rcvmsg(msg); + break; + default: + ret = -EINVAL; + LOGP(DMI, LOGL_ERROR, "unknown link type %u\n", link->type); + break; + } + break; + case E1INP_TS_TYPE_TRAU: + ret = subch_demux_in(&ts->trau.demux, msg->l2h, msgb_l2len(msg)); + break; + default: + ret = -EINVAL; + LOGP(DMI, LOGL_ERROR, "unknown TS type %u\n", ts->type); + break; + } + + return ret; +} + +#define TSX_ALLOC_SIZE 4096 + +/* called by driver if it wants to transmit on a given TS */ +struct msgb *e1inp_tx_ts(struct e1inp_ts *e1i_ts, + struct e1inp_sign_link **sign_link) +{ + struct e1inp_sign_link *link; + struct msgb *msg = NULL; + int len; + + switch (e1i_ts->type) { + case E1INP_TS_TYPE_SIGN: + /* FIXME: implement this round robin */ + llist_for_each_entry(link, &e1i_ts->sign.sign_links, list) { + msg = msgb_dequeue(&link->tx_list); + if (msg) { + if (sign_link) + *sign_link = link; + break; + } + } + break; + case E1INP_TS_TYPE_TRAU: + msg = msgb_alloc(TSX_ALLOC_SIZE, "TRAU_TX"); + if (!msg) + return NULL; + len = subchan_mux_out(&e1i_ts->trau.mux, msg->data, 40); + msgb_put(msg, 40); + break; + default: + LOGP(DMI, LOGL_ERROR, "unsupported E1 TS type %u\n", e1i_ts->type); + return NULL; + } + return msg; +} + +/* called by driver in case some kind of link state event */ +int e1inp_event(struct e1inp_ts *ts, int evt, u_int8_t tei, u_int8_t sapi) +{ + struct e1inp_sign_link *link; + struct input_signal_data isd; + + link = e1inp_lookup_sign_link(ts, tei, sapi); + if (!link) + return -EINVAL; + + isd.link_type = link->type; + isd.trx = link->trx; + isd.tei = tei; + isd.sapi = sapi; + + /* report further upwards */ + dispatch_signal(SS_INPUT, evt, &isd); + return 0; +} + +/* register a driver with the E1 core */ +int e1inp_driver_register(struct e1inp_driver *drv) +{ + llist_add_tail(&drv->list, &e1inp_driver_list); + return 0; +} + +struct e1inp_driver *e1inp_driver_find(const char *name) +{ + struct e1inp_driver *drv; + + llist_for_each_entry(drv, &e1inp_driver_list, list) { + if (!strcasecmp(name, drv->name)) + return drv; + } + return NULL; +} + +int e1inp_line_update(struct e1inp_line *line) +{ + struct input_signal_data isd; + int rc; + + if (line->driver && line->driver->line_update) + rc = line->driver->line_update(line); + else + rc = 0; + + /* Send a signal to anyone who is interested in new lines being + * configured */ + memset(&isd, 0, sizeof(isd)); + isd.line = line; + dispatch_signal(SS_INPUT, S_INP_LINE_INIT, &isd); + + return rc; +} + +static int e1i_sig_cb(unsigned int subsys, unsigned int signal, + void *handler_data, void *signal_data) +{ + if (subsys != SS_GLOBAL || + signal != S_GLOBAL_SHUTDOWN) + return 0; + + if (pcap_fd) { + close(pcap_fd); + pcap_fd = -1; + } + + return 0; +} + +void e1inp_misdn_init(void); +void e1inp_dahdi_init(void); + +void e1inp_init(void) +{ + tall_sigl_ctx = talloc_named_const(tall_bsc_ctx, 1, + "e1inp_sign_link"); + register_signal_handler(SS_GLOBAL, e1i_sig_cb, NULL); + + e1inp_misdn_init(); +#ifdef HAVE_DAHDI_USER_H + e1inp_dahdi_init(); +#endif +} diff --git a/openbsc/src/libabis/e1_input_vty.c b/openbsc/src/libabis/e1_input_vty.c new file mode 100644 index 000000000..3909f652f --- /dev/null +++ b/openbsc/src/libabis/e1_input_vty.c @@ -0,0 +1,102 @@ +/* OpenBSC E1 vty interface */ +/* (C) 2011 by Harald Welte + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + * + */ + +#include +#include +#include + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "../bscconfig.h" + +#define E1_DRIVER_NAMES "(misdn|dahdi)" +#define E1_DRIVER_HELP "mISDN supported E1 Card\n" \ + "DAHDI supported E1/T1/J1 Card\n" + +DEFUN(cfg_e1line_driver, cfg_e1_line_driver_cmd, + "e1_line <0-255> driver " E1_DRIVER_NAMES, + "Configure E1/T1/J1 Line\n" "Line Number\n" "Set driver for this line\n" + E1_DRIVER_HELP) +{ + struct e1inp_line *line; + int e1_nr = atoi(argv[0]); + + line = e1inp_line_get(e1_nr); + if (line) { + vty_out(vty, "%% Line %d already exists%s", e1_nr, VTY_NEWLINE); + return CMD_WARNING; + } + line = e1inp_line_create(e1_nr, argv[1]); + if (!line) { + vty_out(vty, "%% Error creating line %d%s", e1_nr, VTY_NEWLINE); + return CMD_WARNING; + } + + return CMD_SUCCESS; +} + +DEFUN(cfg_e1inp, cfg_e1inp_cmd, + "e1_input", + "Configure E1/T1/J1 TDM input\n") +{ + vty->node = E1INP_NODE; + + return CMD_SUCCESS; +} + +static int e1inp_config_write(struct vty *vty) +{ + struct e1inp_line *line; + + vty_out(vty, "e1_input%s", VTY_NEWLINE); + + llist_for_each_entry(line, &e1inp_line_list, list) { + vty_out(vty, " e1_line %u driver %s%s", line->num, + line->driver->name, VTY_NEWLINE); + } + return CMD_SUCCESS; +} + +struct cmd_node e1inp_node = { + E1INP_NODE, + "%s(e1_input)#", + 1, +}; + +int e1inp_vty_init(void) +{ + install_element(CONFIG_NODE, &cfg_e1inp_cmd); + install_node(&e1inp_node, e1inp_config_write); + install_element(E1INP_NODE, &cfg_e1_line_driver_cmd); + + return 0; +} diff --git a/openbsc/src/libabis/input/dahdi.c b/openbsc/src/libabis/input/dahdi.c new file mode 100644 index 000000000..2fc99f58e --- /dev/null +++ b/openbsc/src/libabis/input/dahdi.c @@ -0,0 +1,494 @@ +/* OpenBSC Abis input driver for DAHDI */ + +/* (C) 2008-2011 by Harald Welte + * (C) 2009 by Holger Hans Peter Freyther + * (C) 2010 by Digium and Matthew Fredrickson + * + * 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 "../../bscconfig.h" + +#ifdef HAVE_DAHDI_USER_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "lapd.h" + +#define TS1_ALLOC_SIZE 300 + +/* Corresponds to dahdi/user.h, only PRI related events */ +static const struct value_string dahdi_evt_names[] = { + { DAHDI_EVENT_NONE, "NONE" }, + { DAHDI_EVENT_ALARM, "ALARM" }, + { DAHDI_EVENT_NOALARM, "NOALARM" }, + { DAHDI_EVENT_ABORT, "HDLC ABORT" }, + { DAHDI_EVENT_OVERRUN, "HDLC OVERRUN" }, + { DAHDI_EVENT_BADFCS, "HDLC BAD FCS" }, + { DAHDI_EVENT_REMOVED, "REMOVED" }, + { 0, NULL } +}; + +static void handle_dahdi_exception(struct e1inp_ts *ts) +{ + int rc, evt; + struct input_signal_data isd; + + rc = ioctl(ts->driver.dahdi.fd.fd, DAHDI_GETEVENT, &evt); + if (rc < 0) + return; + + LOGP(DMI, LOGL_NOTICE, "Line %u(%s) / TS %u DAHDI EVENT %s\n", + ts->line->num, ts->line->name, ts->num, + get_value_string(dahdi_evt_names, evt)); + + isd.line = ts->line; + + switch (evt) { + case DAHDI_EVENT_ALARM: + /* we should notify the code that the line is gone */ + dispatch_signal(SS_INPUT, S_INP_LINE_ALARM, &isd); + break; + case DAHDI_EVENT_NOALARM: + /* alarm has gone, we should re-start the SABM requests */ + dispatch_signal(SS_INPUT, S_INP_LINE_NOALARM, &isd); + break; + } +} + +static int handle_ts1_read(struct bsc_fd *bfd) +{ + struct e1inp_line *line = bfd->data; + unsigned int ts_nr = bfd->priv_nr; + struct e1inp_ts *e1i_ts = &line->ts[ts_nr-1]; + struct msgb *msg = msgb_alloc(TS1_ALLOC_SIZE, "DAHDI TS1"); + lapd_mph_type prim; + unsigned int sapi, tei; + int ilen, ret; + uint8_t *idata; + + if (!msg) + return -ENOMEM; + + ret = read(bfd->fd, msg->data, TS1_ALLOC_SIZE - 16); + if (ret == -1) + handle_dahdi_exception(e1i_ts); + else if (ret < 0) { + perror("read "); + } + msgb_put(msg, ret - 2); + if (ret <= 3) { + perror("read "); + } + + sapi = msg->data[0] >> 2; + tei = msg->data[1] >> 1; + + DEBUGP(DMI, "<= len = %d, sapi(%d) tei(%d)", ret, sapi, tei); + + idata = lapd_receive(e1i_ts->driver.dahdi.lapd, msg->data, msg->len, &ilen, &prim); + if (!idata && prim == 0) + return -EIO; + + msgb_pull(msg, 2); + + DEBUGP(DMI, "prim %08x\n", prim); + + switch (prim) { + case 0: + break; + case LAPD_MPH_ACTIVATE_IND: + DEBUGP(DMI, "MPH_ACTIVATE_IND: sapi(%d) tei(%d)\n", sapi, tei); + ret = e1inp_event(e1i_ts, S_INP_TEI_UP, tei, sapi); + break; + case LAPD_MPH_DEACTIVATE_IND: + DEBUGP(DMI, "MPH_DEACTIVATE_IND: sapi(%d) tei(%d)\n", sapi, tei); + ret = e1inp_event(e1i_ts, S_INP_TEI_DN, tei, sapi); + break; + case LAPD_DL_DATA_IND: + case LAPD_DL_UNITDATA_IND: + if (prim == LAPD_DL_DATA_IND) + msg->l2h = msg->data + 2; + else + msg->l2h = msg->data + 1; + DEBUGP(DMI, "RX: %s\n", hexdump(msgb_l2(msg), ret)); + ret = e1inp_rx_ts(e1i_ts, msg, tei, sapi); + break; + default: + printf("ERROR: unknown prim\n"); + break; + } + + DEBUGP(DMI, "Returned ok\n"); + return ret; +} + +static int ts_want_write(struct e1inp_ts *e1i_ts) +{ + /* We never include the DAHDI B-Channel FD into the + * writeset, since it doesn't support poll() based + * write flow control */ + if (e1i_ts->type == E1INP_TS_TYPE_TRAU) { + fprintf(stderr, "Trying to write TRAU ts\n"); + return 0; + } + + e1i_ts->driver.dahdi.fd.when |= BSC_FD_WRITE; + + return 0; +} + +static void timeout_ts1_write(void *data) +{ + struct e1inp_ts *e1i_ts = (struct e1inp_ts *)data; + + /* trigger write of ts1, due to tx delay timer */ + ts_want_write(e1i_ts); +} + +static void dahdi_write_msg(uint8_t *data, int len, void *cbdata) +{ + struct bsc_fd *bfd = cbdata; + struct e1inp_line *line = bfd->data; + unsigned int ts_nr = bfd->priv_nr; + struct e1inp_ts *e1i_ts = &line->ts[ts_nr-1]; + int ret; + + ret = write(bfd->fd, data, len + 2); + if (ret == -1) + handle_dahdi_exception(e1i_ts); + else if (ret < 0) + LOGP(DMI, LOGL_NOTICE, "%s write failed %d\n", __func__, ret); +} + +static int handle_ts1_write(struct bsc_fd *bfd) +{ + struct e1inp_line *line = bfd->data; + unsigned int ts_nr = bfd->priv_nr; + struct e1inp_ts *e1i_ts = &line->ts[ts_nr-1]; + struct e1inp_sign_link *sign_link; + struct msgb *msg; + + bfd->when &= ~BSC_FD_WRITE; + + /* get the next msg for this timeslot */ + msg = e1inp_tx_ts(e1i_ts, &sign_link); + if (!msg) { + /* no message after tx delay timer */ + return 0; + } + + DEBUGP(DMI, "TX: %s\n", hexdump(msg->data, msg->len)); + lapd_transmit(e1i_ts->driver.dahdi.lapd, sign_link->tei, + sign_link->sapi, msg->data, msg->len); + msgb_free(msg); + + /* set tx delay timer for next event */ + e1i_ts->sign.tx_timer.cb = timeout_ts1_write; + e1i_ts->sign.tx_timer.data = e1i_ts; + bsc_schedule_timer(&e1i_ts->sign.tx_timer, 0, 50000); + + return 0; +} + + +static int invertbits = 1; + +static u_int8_t flip_table[256]; + +static void init_flip_bits(void) +{ + int i,k; + + for (i = 0 ; i < 256 ; i++) { + u_int8_t sample = 0 ; + for (k = 0; k<8; k++) { + if ( i & 1 << k ) sample |= 0x80 >> k; + } + flip_table[i] = sample; + } +} + +static u_int8_t * flip_buf_bits ( u_int8_t * buf , int len) +{ + int i; + u_int8_t * start = buf; + + for (i = 0 ; i < len; i++) { + buf[i] = flip_table[(u_int8_t)buf[i]]; + } + + return start; +} + +#define D_BCHAN_TX_GRAN 160 +/* write to a B channel TS */ +static int handle_tsX_write(struct bsc_fd *bfd) +{ + struct e1inp_line *line = bfd->data; + unsigned int ts_nr = bfd->priv_nr; + struct e1inp_ts *e1i_ts = &line->ts[ts_nr-1]; + u_int8_t tx_buf[D_BCHAN_TX_GRAN]; + struct subch_mux *mx = &e1i_ts->trau.mux; + int ret; + + ret = subchan_mux_out(mx, tx_buf, D_BCHAN_TX_GRAN); + + if (ret != D_BCHAN_TX_GRAN) { + fprintf(stderr, "Huh, got ret of %d\n", ret); + if (ret < 0) + return ret; + } + + DEBUGP(DMIB, "BCHAN TX: %s\n", + hexdump(tx_buf, D_BCHAN_TX_GRAN)); + + if (invertbits) { + flip_buf_bits(tx_buf, ret); + } + + ret = write(bfd->fd, tx_buf, ret); + if (ret < D_BCHAN_TX_GRAN) + fprintf(stderr, "send returns %d instead of %d\n", ret, + D_BCHAN_TX_GRAN); + + return ret; +} + +#define D_TSX_ALLOC_SIZE (D_BCHAN_TX_GRAN) +/* FIXME: read from a B channel TS */ +static int handle_tsX_read(struct bsc_fd *bfd) +{ + struct e1inp_line *line = bfd->data; + unsigned int ts_nr = bfd->priv_nr; + struct e1inp_ts *e1i_ts = &line->ts[ts_nr-1]; + struct msgb *msg = msgb_alloc(D_TSX_ALLOC_SIZE, "DAHDI TSx"); + int ret; + + if (!msg) + return -ENOMEM; + + ret = read(bfd->fd, msg->data, D_TSX_ALLOC_SIZE); + if (ret < 0 || ret != D_TSX_ALLOC_SIZE) { + fprintf(stderr, "read error %d %s\n", ret, strerror(errno)); + return ret; + } + + if (invertbits) { + flip_buf_bits(msg->data, ret); + } + + msgb_put(msg, ret); + + msg->l2h = msg->data; + DEBUGP(DMIB, "BCHAN RX: %s\n", + hexdump(msgb_l2(msg), ret)); + ret = e1inp_rx_ts(e1i_ts, msg, 0, 0); + /* physical layer indicates that data has been sent, + * we thus can send some more data */ + ret = handle_tsX_write(bfd); + msgb_free(msg); + + return ret; +} + +/* callback from select.c in case one of the fd's can be read/written */ +static int dahdi_fd_cb(struct bsc_fd *bfd, unsigned int what) +{ + struct e1inp_line *line = bfd->data; + unsigned int ts_nr = bfd->priv_nr; + unsigned int idx = ts_nr-1; + struct e1inp_ts *e1i_ts = &line->ts[idx]; + int rc = 0; + + switch (e1i_ts->type) { + case E1INP_TS_TYPE_SIGN: + if (what & BSC_FD_EXCEPT) + handle_dahdi_exception(e1i_ts); + if (what & BSC_FD_READ) + rc = handle_ts1_read(bfd); + if (what & BSC_FD_WRITE) + rc = handle_ts1_write(bfd); + break; + case E1INP_TS_TYPE_TRAU: + if (what & BSC_FD_EXCEPT) + handle_dahdi_exception(e1i_ts); + if (what & BSC_FD_READ) + rc = handle_tsX_read(bfd); + if (what & BSC_FD_WRITE) + rc = handle_tsX_write(bfd); + /* We never include the DAHDI B-Channel FD into the + * writeset, since it doesn't support poll() based + * write flow control */ + break; + default: + fprintf(stderr, "unknown E1 TS type %u\n", e1i_ts->type); + break; + } + + return rc; +} + +static int dahdi_e1_line_update(struct e1inp_line *line); + +struct e1inp_driver dahdi_driver = { + .name = "dahdi", + .want_write = ts_want_write, + .line_update = &dahdi_e1_line_update, +}; + +void dahdi_set_bufinfo(int fd, int as_sigchan) +{ + struct dahdi_bufferinfo bi; + int x = 0; + + if (ioctl(fd, DAHDI_GET_BUFINFO, &bi)) { + fprintf(stderr, "Error getting bufinfo\n"); + exit(-1); + } + + if (as_sigchan) { + bi.numbufs = 4; + bi.bufsize = 512; + } else { + bi.numbufs = 8; + bi.bufsize = D_BCHAN_TX_GRAN; + bi.txbufpolicy = DAHDI_POLICY_WHEN_FULL; + } + + if (ioctl(fd, DAHDI_SET_BUFINFO, &bi)) { + fprintf(stderr, "Error setting bufinfo\n"); + exit(-1); + } + + if (!as_sigchan) { + if (ioctl(fd, DAHDI_AUDIOMODE, &x)) { + fprintf(stderr, "Error setting bufinfo\n"); + exit(-1); + } + } else { + int one = 1; + ioctl(fd, DAHDI_HDLCFCSMODE, &one); + /* we cannot reliably check for the ioctl return value here + * as this command will fail if the slot _already_ was a + * signalling slot before :( */ + } +} + +static int dahdi_e1_setup(struct e1inp_line *line) +{ + int ts, ret; + + /* TS0 is CRC4, don't need any fd for it */ + for (ts = 1; ts < NUM_E1_TS; ts++) { + unsigned int idx = ts-1; + char openstr[128]; + struct e1inp_ts *e1i_ts = &line->ts[idx]; + struct bsc_fd *bfd = &e1i_ts->driver.dahdi.fd; + + bfd->data = line; + bfd->priv_nr = ts; + bfd->cb = dahdi_fd_cb; + snprintf(openstr, sizeof(openstr), "/dev/dahdi/%d", ts); + + switch (e1i_ts->type) { + case E1INP_TS_TYPE_NONE: + continue; + break; + case E1INP_TS_TYPE_SIGN: + bfd->fd = open(openstr, O_RDWR | O_NONBLOCK); + if (bfd->fd == -1) { + fprintf(stderr, "%s could not open %s %s\n", + __func__, openstr, strerror(errno)); + exit(-1); + } + bfd->when = BSC_FD_READ | BSC_FD_EXCEPT; + dahdi_set_bufinfo(bfd->fd, 1); + e1i_ts->driver.dahdi.lapd = lapd_instance_alloc(1, dahdi_write_msg, bfd); + break; + case E1INP_TS_TYPE_TRAU: + bfd->fd = open(openstr, O_RDWR | O_NONBLOCK); + if (bfd->fd == -1) { + fprintf(stderr, "%s could not open %s %s\n", + __func__, openstr, strerror(errno)); + exit(-1); + } + dahdi_set_bufinfo(bfd->fd, 0); + /* We never include the DAHDI B-Channel FD into the + * writeset, since it doesn't support poll() based + * write flow control */ + bfd->when = BSC_FD_READ | BSC_FD_EXCEPT;// | BSC_FD_WRITE; + break; + } + + if (bfd->fd < 0) { + fprintf(stderr, "%s could not open %s %s\n", + __func__, openstr, strerror(errno)); + return bfd->fd; + } + + ret = bsc_register_fd(bfd); + if (ret < 0) { + fprintf(stderr, "could not register FD: %s\n", + strerror(ret)); + return ret; + } + } + + return 0; +} + +static int dahdi_e1_line_update(struct e1inp_line *line) +{ + if (line->driver != &dahdi_driver) + return -EINVAL; + + return dahdi_e1_setup(line); +} + +int e1inp_dahdi_init(void) +{ + init_flip_bits(); + + /* register the driver with the core */ + return e1inp_driver_register(&dahdi_driver); +} + +#endif /* HAVE_DAHDI_USER_H */ diff --git a/openbsc/src/libabis/input/ipaccess.c b/openbsc/src/libabis/input/ipaccess.c new file mode 100644 index 000000000..dcf8d1a53 --- /dev/null +++ b/openbsc/src/libabis/input/ipaccess.c @@ -0,0 +1,797 @@ +/* OpenBSC Abis input driver for ip.access */ + +/* (C) 2009 by Harald Welte + * (C) 2010 by Holger Hans Peter Freyther + * (C) 2010 by On-Waves + * + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define PRIV_OML 1 +#define PRIV_RSL 2 + +/* data structure for one E1 interface with A-bis */ +struct ia_e1_handle { + struct bsc_fd listen_fd; + struct bsc_fd rsl_listen_fd; + struct gsm_network *gsmnet; +}; + +static struct ia_e1_handle *e1h; + + +#define TS1_ALLOC_SIZE 900 + +static const u_int8_t pong[] = { 0, 1, IPAC_PROTO_IPACCESS, IPAC_MSGT_PONG }; +static const u_int8_t id_ack[] = { 0, 1, IPAC_PROTO_IPACCESS, IPAC_MSGT_ID_ACK }; +static const u_int8_t id_req[] = { 0, 17, IPAC_PROTO_IPACCESS, 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, + }; + +static const char *idtag_names[] = { + [IPAC_IDTAG_SERNR] = "Serial_Number", + [IPAC_IDTAG_UNITNAME] = "Unit_Name", + [IPAC_IDTAG_LOCATION1] = "Location_1", + [IPAC_IDTAG_LOCATION2] = "Location_2", + [IPAC_IDTAG_EQUIPVERS] = "Equipment_Version", + [IPAC_IDTAG_SWVERSION] = "Software_Version", + [IPAC_IDTAG_IPADDR] = "IP_Address", + [IPAC_IDTAG_MACADDR] = "MAC_Address", + [IPAC_IDTAG_UNIT] = "Unit_ID", +}; + +static const char *ipac_idtag_name(int tag) +{ + if (tag >= ARRAY_SIZE(idtag_names)) + return "unknown"; + + return idtag_names[tag]; +} + +int ipaccess_idtag_parse(struct tlv_parsed *dec, unsigned char *buf, int len) +{ + u_int8_t t_len; + u_int8_t t_tag; + u_int8_t *cur = buf; + + memset(dec, 0, sizeof(*dec)); + + while (len >= 2) { + len -= 2; + t_len = *cur++; + t_tag = *cur++; + + if (t_len > len + 1) { + LOGP(DMI, LOGL_ERROR, "The tag does not fit: %d\n", t_len); + return -1; + } + + DEBUGPC(DMI, "%s='%s' ", ipac_idtag_name(t_tag), cur); + + dec->lv[t_tag].len = t_len; + dec->lv[t_tag].val = cur; + + cur += t_len; + len -= t_len; + } + return 0; +} + +struct gsm_bts *find_bts_by_unitid(struct gsm_network *net, + u_int16_t site_id, u_int16_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; +} + +static int parse_unitid(const char *str, u_int16_t *site_id, u_int16_t *bts_id, + u_int16_t *trx_id) +{ + unsigned long ul; + char *endptr; + const char *nptr; + + nptr = str; + ul = strtoul(nptr, &endptr, 10); + if (endptr <= nptr) + return -EINVAL; + if (site_id) + *site_id = ul & 0xffff; + + if (*endptr++ != '/') + return -EINVAL; + + nptr = endptr; + ul = strtoul(nptr, &endptr, 10); + if (endptr <= nptr) + return -EINVAL; + if (bts_id) + *bts_id = ul & 0xffff; + + if (*endptr++ != '/') + return -EINVAL; + + nptr = endptr; + ul = strtoul(nptr, &endptr, 10); + if (endptr <= nptr) + return -EINVAL; + if (trx_id) + *trx_id = ul & 0xffff; + + return 0; +} + +/* send the id ack */ +int ipaccess_send_id_ack(int fd) +{ + return write(fd, id_ack, sizeof(id_ack)); +} + +int ipaccess_send_id_req(int fd) +{ + return write(fd, id_req, sizeof(id_req)); +} + +/* base handling of the ip.access protocol */ +int ipaccess_rcvmsg_base(struct msgb *msg, + struct bsc_fd *bfd) +{ + u_int8_t msg_type = *(msg->l2h); + int ret = 0; + + switch (msg_type) { + case IPAC_MSGT_PING: + ret = write(bfd->fd, pong, sizeof(pong)); + break; + case IPAC_MSGT_PONG: + DEBUGP(DMI, "PONG!\n"); + break; + case IPAC_MSGT_ID_ACK: + DEBUGP(DMI, "ID_ACK? -> ACK!\n"); + ret = ipaccess_send_id_ack(bfd->fd); + break; + } + return 0; +} + +static int ipaccess_rcvmsg(struct e1inp_line *line, struct msgb *msg, + struct bsc_fd *bfd) +{ + struct tlv_parsed tlvp; + u_int8_t msg_type = *(msg->l2h); + u_int16_t site_id = 0, bts_id = 0, trx_id = 0; + struct gsm_bts *bts; + char *unitid; + int len; + + /* handle base messages */ + ipaccess_rcvmsg_base(msg, bfd); + + switch (msg_type) { + case IPAC_MSGT_ID_RESP: + DEBUGP(DMI, "ID_RESP "); + /* parse tags, search for Unit ID */ + ipaccess_idtag_parse(&tlvp, (u_int8_t *)msg->l2h + 2, + msgb_l2len(msg)-2); + DEBUGP(DMI, "\n"); + + if (!TLVP_PRESENT(&tlvp, IPAC_IDTAG_UNIT)) + break; + + len = TLVP_LEN(&tlvp, IPAC_IDTAG_UNIT); + if (len < 1) + break; + + /* lookup BTS, create sign_link, ... */ + unitid = (char *) TLVP_VAL(&tlvp, IPAC_IDTAG_UNIT); + unitid[len - 1] = '\0'; + parse_unitid(unitid, &site_id, &bts_id, &trx_id); + bts = find_bts_by_unitid(e1h->gsmnet, site_id, bts_id); + if (!bts) { + LOGP(DINP, LOGL_ERROR, "Unable to find BTS configuration for " + " %u/%u/%u, disconnecting\n", site_id, bts_id, + trx_id); + return -EIO; + } + DEBUGP(DINP, "Identified BTS %u/%u/%u\n", site_id, bts_id, trx_id); + if (bfd->priv_nr == PRIV_OML) { + /* drop any old oml connection */ + ipaccess_drop_oml(bts); + bts->oml_link = e1inp_sign_link_create(&line->ts[PRIV_OML - 1], + E1INP_SIGN_OML, bts->c0, + bts->oml_tei, 0); + } else if (bfd->priv_nr == PRIV_RSL) { + struct e1inp_ts *e1i_ts; + struct bsc_fd *newbfd; + struct gsm_bts_trx *trx = gsm_bts_trx_num(bts, trx_id); + + /* drop any old rsl connection */ + ipaccess_drop_rsl(trx); + + if (!bts->oml_link) { + bsc_unregister_fd(bfd); + close(bfd->fd); + bfd->fd = -1; + talloc_free(bfd); + return 0; + } + + bfd->data = line = bts->oml_link->ts->line; + e1i_ts = &line->ts[PRIV_RSL + trx_id - 1]; + newbfd = &e1i_ts->driver.ipaccess.fd; + e1inp_ts_config(e1i_ts, line, E1INP_TS_TYPE_SIGN); + + trx->rsl_link = e1inp_sign_link_create(e1i_ts, + E1INP_SIGN_RSL, trx, + trx->rsl_tei, 0); + trx->rsl_link->ts->sign.delay = 0; + + /* get rid of our old temporary bfd */ + memcpy(newbfd, bfd, sizeof(*newbfd)); + newbfd->priv_nr = PRIV_RSL + trx_id; + bsc_unregister_fd(bfd); + bfd->fd = -1; + talloc_free(bfd); + bsc_register_fd(newbfd); + } + break; + } + return 0; +} + +#define OML_UP 0x0001 +#define RSL_UP 0x0002 + +/* + * read one ipa message from the socket + * return NULL in case of error + */ +struct msgb *ipaccess_read_msg(struct bsc_fd *bfd, int *error) +{ + struct msgb *msg = msgb_alloc(TS1_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, sizeof(*hh), 0); + if (ret == 0) { + msgb_free(msg); + *error = ret; + return NULL; + } else if (ret != sizeof(*hh)) { + if (errno != EAGAIN) + LOGP(DINP, LOGL_ERROR, "recv error %d %s\n", ret, strerror(errno)); + 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); + + if (len < 0 || TS1_ALLOC_SIZE < len + sizeof(*hh)) { + LOGP(DINP, LOGL_ERROR, "Can not read this packet. %d avail\n", len); + msgb_free(msg); + *error = -EIO; + return NULL; + } + + ret = recv(bfd->fd, msg->l2h, len, 0); + if (ret < len) { + LOGP(DINP, LOGL_ERROR, "short read! Got %d from %d\n", ret, len); + msgb_free(msg); + *error = -EIO; + return NULL; + } + msgb_put(msg, ret); + + return msg; +} + +int ipaccess_drop_oml(struct gsm_bts *bts) +{ + struct gsm_bts_trx *trx; + struct e1inp_ts *ts; + struct e1inp_line *line; + struct bsc_fd *bfd; + + if (!bts || !bts->oml_link) + return -1; + + /* send OML down */ + ts = bts->oml_link->ts; + line = ts->line; + e1inp_event(ts, S_INP_TEI_DN, bts->oml_link->tei, bts->oml_link->sapi); + + bfd = &ts->driver.ipaccess.fd; + bsc_unregister_fd(bfd); + close(bfd->fd); + bfd->fd = -1; + + /* clean up OML and RSL */ + e1inp_sign_link_destroy(bts->oml_link); + bts->oml_link = NULL; + bts->ip_access.flags = 0; + + /* drop all RSL connections too */ + llist_for_each_entry(trx, &bts->trx_list, list) + ipaccess_drop_rsl(trx); + + /* kill the E1 line now... as we have no one left to use it */ + talloc_free(line); + + return -1; +} + +static int ipaccess_drop(struct e1inp_ts *ts, struct bsc_fd *bfd) +{ + struct e1inp_sign_link *link; + int bts_nr; + + if (!ts) { + /* + * If we don't have a TS this means that this is a RSL + * connection but we are not past the authentication + * handling yet. So we can safely delete this bfd and + * wait for a reconnect. + */ + bsc_unregister_fd(bfd); + close(bfd->fd); + bfd->fd = -1; + talloc_free(bfd); + return -1; + } + + /* attempt to find a signalling link */ + if (ts->type == E1INP_TS_TYPE_SIGN) { + llist_for_each_entry(link, &ts->sign.sign_links, list) { + bts_nr = link->trx->bts->bts_nr; + /* we have issues just reconnecting RLS so we drop OML */ + ipaccess_drop_oml(link->trx->bts); + return bts_nr; + } + } + + /* error case */ + LOGP(DINP, LOGL_ERROR, "Failed to find a signalling link for ts: %p\n", ts); + bsc_unregister_fd(bfd); + close(bfd->fd); + bfd->fd = -1; + return -1; +} + +int ipaccess_drop_rsl(struct gsm_bts_trx *trx) +{ + struct bsc_fd *bfd; + struct e1inp_ts *ts; + + if (!trx || !trx->rsl_link) + return -1; + + /* send RSL down */ + ts = trx->rsl_link->ts; + e1inp_event(ts, S_INP_TEI_DN, trx->rsl_link->tei, trx->rsl_link->sapi); + + /* close the socket */ + bfd = &ts->driver.ipaccess.fd; + bsc_unregister_fd(bfd); + close(bfd->fd); + bfd->fd = -1; + + /* destroy */ + e1inp_sign_link_destroy(trx->rsl_link); + trx->rsl_link = NULL; + + return -1; +} + +static int handle_ts1_read(struct bsc_fd *bfd) +{ + struct e1inp_line *line = bfd->data; + unsigned int ts_nr = bfd->priv_nr; + struct e1inp_ts *e1i_ts = &line->ts[ts_nr-1]; + struct e1inp_sign_link *link; + struct msgb *msg; + struct ipaccess_head *hh; + int ret = 0, error; + + msg = ipaccess_read_msg(bfd, &error); + if (!msg) { + if (error == 0) { + int ret = ipaccess_drop(e1i_ts, bfd); + if (ret >= 0) + LOGP(DINP, LOGL_NOTICE, "BTS %u disappeared, dead socket\n", + ret); + else + LOGP(DINP, LOGL_NOTICE, "unknown BTS disappeared, dead socket\n"); + } + return error; + } + + DEBUGP(DMI, "RX %u: %s\n", ts_nr, hexdump(msgb_l2(msg), msgb_l2len(msg))); + + hh = (struct ipaccess_head *) msg->data; + if (hh->proto == IPAC_PROTO_IPACCESS) { + ret = ipaccess_rcvmsg(line, msg, bfd); + if (ret < 0) + ipaccess_drop(e1i_ts, bfd); + msgb_free(msg); + return ret; + } + /* BIG FAT WARNING: bfd might no longer exist here, since ipaccess_rcvmsg() + * might have free'd it !!! */ + + link = e1inp_lookup_sign_link(e1i_ts, hh->proto, 0); + if (!link) { + LOGP(DINP, LOGL_ERROR, "no matching signalling link for " + "hh->proto=0x%02x\n", hh->proto); + msgb_free(msg); + return -EIO; + } + msg->trx = link->trx; + + switch (link->type) { + case E1INP_SIGN_RSL: + if (!(msg->trx->bts->ip_access.flags & (RSL_UP << msg->trx->nr))) { + e1inp_event(e1i_ts, S_INP_TEI_UP, link->tei, link->sapi); + msg->trx->bts->ip_access.flags |= (RSL_UP << msg->trx->nr); + } + ret = abis_rsl_rcvmsg(msg); + break; + case E1INP_SIGN_OML: + if (!(msg->trx->bts->ip_access.flags & OML_UP)) { + e1inp_event(e1i_ts, S_INP_TEI_UP, link->tei, link->sapi); + msg->trx->bts->ip_access.flags |= OML_UP; + } + ret = abis_nm_rcvmsg(msg); + break; + default: + LOGP(DINP, LOGL_NOTICE, "Unknown IP.access protocol proto=0x%02x\n", hh->proto); + msgb_free(msg); + break; + } + return ret; +} + +void ipaccess_prepend_header(struct msgb *msg, int proto) +{ + struct ipaccess_head *hh; + + /* prepend the ip.access header */ + hh = (struct ipaccess_head *) msgb_push(msg, sizeof(*hh)); + hh->len = htons(msg->len - sizeof(*hh)); + hh->proto = proto; +} + +static int ts_want_write(struct e1inp_ts *e1i_ts) +{ + e1i_ts->driver.ipaccess.fd.when |= BSC_FD_WRITE; + + return 0; +} + +static void timeout_ts1_write(void *data) +{ + struct e1inp_ts *e1i_ts = (struct e1inp_ts *)data; + + /* trigger write of ts1, due to tx delay timer */ + ts_want_write(e1i_ts); +} + +static int handle_ts1_write(struct bsc_fd *bfd) +{ + struct e1inp_line *line = bfd->data; + unsigned int ts_nr = bfd->priv_nr; + struct e1inp_ts *e1i_ts = &line->ts[ts_nr-1]; + struct e1inp_sign_link *sign_link; + struct msgb *msg; + u_int8_t proto; + int ret; + + bfd->when &= ~BSC_FD_WRITE; + + /* get the next msg for this timeslot */ + msg = e1inp_tx_ts(e1i_ts, &sign_link); + if (!msg) { + /* no message after tx delay timer */ + return 0; + } + + switch (sign_link->type) { + case E1INP_SIGN_OML: + proto = IPAC_PROTO_OML; + break; + case E1INP_SIGN_RSL: + proto = IPAC_PROTO_RSL; + break; + default: + msgb_free(msg); + bfd->when |= BSC_FD_WRITE; /* come back for more msg */ + return -EINVAL; + } + + msg->l2h = msg->data; + ipaccess_prepend_header(msg, sign_link->tei); + + DEBUGP(DMI, "TX %u: %s\n", ts_nr, hexdump(msg->l2h, msgb_l2len(msg))); + + ret = send(bfd->fd, msg->data, msg->len, 0); + msgb_free(msg); + + /* set tx delay timer for next event */ + e1i_ts->sign.tx_timer.cb = timeout_ts1_write; + e1i_ts->sign.tx_timer.data = e1i_ts; + + /* Reducing this might break the nanoBTS 900 init. */ + bsc_schedule_timer(&e1i_ts->sign.tx_timer, 0, e1i_ts->sign.delay); + + return ret; +} + +/* callback from select.c in case one of the fd's can be read/written */ +static int ipaccess_fd_cb(struct bsc_fd *bfd, unsigned int what) +{ + struct e1inp_line *line = bfd->data; + unsigned int ts_nr = bfd->priv_nr; + unsigned int idx = ts_nr-1; + struct e1inp_ts *e1i_ts; + int rc = 0; + + /* In case of early RSL we might not yet have a line */ + + if (line) + e1i_ts = &line->ts[idx]; + + if (!line || e1i_ts->type == E1INP_TS_TYPE_SIGN) { + if (what & BSC_FD_READ) + rc = handle_ts1_read(bfd); + if (what & BSC_FD_WRITE) + rc = handle_ts1_write(bfd); + } else + LOGP(DINP, LOGL_ERROR, "unknown E1 TS type %u\n", e1i_ts->type); + + return rc; +} + +struct e1inp_driver ipaccess_driver = { + .name = "ip.access", + .want_write = ts_want_write, + .default_delay = 0, +}; + +/* callback of the OML listening filedescriptor */ +static int listen_fd_cb(struct bsc_fd *listen_bfd, unsigned int what) +{ + int ret; + int idx = 0; + int i; + struct e1inp_line *line; + struct e1inp_ts *e1i_ts; + struct bsc_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; + } + LOGP(DINP, LOGL_NOTICE, "accept()ed new OML link from %s\n", + inet_ntoa(sa.sin_addr)); + + line = talloc_zero(tall_bsc_ctx, struct e1inp_line); + if (!line) { + close(ret); + return -ENOMEM; + } + line->driver = &ipaccess_driver; + //line->driver_data = e1h; + /* create virrtual E1 timeslots for signalling */ + e1inp_ts_config(&line->ts[1-1], line, E1INP_TS_TYPE_SIGN); + + /* initialize the fds */ + for (i = 0; i < ARRAY_SIZE(line->ts); ++i) + line->ts[i].driver.ipaccess.fd.fd = -1; + + e1i_ts = &line->ts[idx]; + + bfd = &e1i_ts->driver.ipaccess.fd; + bfd->fd = ret; + bfd->data = line; + bfd->priv_nr = PRIV_OML; + bfd->cb = ipaccess_fd_cb; + bfd->when = BSC_FD_READ; + ret = bsc_register_fd(bfd); + if (ret < 0) { + LOGP(DINP, LOGL_ERROR, "could not register FD\n"); + close(bfd->fd); + talloc_free(line); + return ret; + } + + /* Request ID. FIXME: request LOCATION, HW/SW VErsion, Unit Name, Serno */ + ret = ipaccess_send_id_req(bfd->fd); + + return ret; + //return e1inp_line_register(line); +} + +static int rsl_listen_fd_cb(struct bsc_fd *listen_bfd, unsigned int what) +{ + struct sockaddr_in sa; + socklen_t sa_len = sizeof(sa); + struct bsc_fd *bfd; + int ret; + + if (!(what & BSC_FD_READ)) + return 0; + + bfd = talloc_zero(tall_bsc_ctx, struct bsc_fd); + if (!bfd) + return -ENOMEM; + + /* Some BTS has connected to us, but we don't know yet which line + * (as created by the OML link) to associate it with. Thus, we + * allocate a temporary bfd until we have received ID from BTS */ + + bfd->fd = accept(listen_bfd->fd, (struct sockaddr *) &sa, &sa_len); + if (bfd->fd < 0) { + perror("accept"); + return bfd->fd; + } + LOGP(DINP, LOGL_NOTICE, "accept()ed new RSL link from %s\n", inet_ntoa(sa.sin_addr)); + bfd->priv_nr = PRIV_RSL; + bfd->cb = ipaccess_fd_cb; + bfd->when = BSC_FD_READ; + ret = bsc_register_fd(bfd); + if (ret < 0) { + LOGP(DINP, LOGL_ERROR, "could not register FD\n"); + close(bfd->fd); + talloc_free(bfd); + return ret; + } + /* Request ID. FIXME: request LOCATION, HW/SW VErsion, Unit Name, Serno */ + ret = write(bfd->fd, id_req, sizeof(id_req)); + + return 0; +} + +/* Actively connect to a BTS. Currently used by ipaccess-config.c */ +int ipaccess_connect(struct e1inp_line *line, struct sockaddr_in *sa) +{ + struct e1inp_ts *e1i_ts = &line->ts[0]; + struct bsc_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 = PRIV_OML; + + if (bfd->fd < 0) { + LOGP(DINP, LOGL_ERROR, "could not create TCP socket.\n"); + return -EIO; + } + + setsockopt(bfd->fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)); + + ret = connect(bfd->fd, (struct sockaddr *) sa, sizeof(*sa)); + if (ret < 0) { + LOGP(DINP, LOGL_ERROR, "could not connect socket\n"); + close(bfd->fd); + return ret; + } + + ret = bsc_register_fd(bfd); + if (ret < 0) { + close(bfd->fd); + return ret; + } + + line->driver = &ipaccess_driver; + + return ret; + //return e1inp_line_register(line); +} + +int ipaccess_setup(struct gsm_network *gsmnet) +{ + int ret; + + /* register the driver with the core */ + /* FIXME: do this in the plugin initializer function */ + ret = e1inp_driver_register(&ipaccess_driver); + if (ret) + return ret; + + e1h = talloc_zero(tall_bsc_ctx, struct ia_e1_handle); + if (!e1h) + return -ENOMEM; + + e1h->gsmnet = gsmnet; + + /* Listen for OML connections */ + ret = make_sock(&e1h->listen_fd, IPPROTO_TCP, 0, IPA_TCP_PORT_OML, + listen_fd_cb); + if (ret < 0) + return ret; + + /* Listen for RSL connections */ + ret = make_sock(&e1h->rsl_listen_fd, IPPROTO_TCP, 0, + IPA_TCP_PORT_RSL, rsl_listen_fd_cb); + if (ret < 0) + return ret; + + return ret; +} diff --git a/openbsc/src/libabis/input/lapd.c b/openbsc/src/libabis/input/lapd.c new file mode 100644 index 000000000..7bce6cc51 --- /dev/null +++ b/openbsc/src/libabis/input/lapd.c @@ -0,0 +1,710 @@ +/* OpenBSC minimal LAPD implementation */ + +/* (C) 2009 by oystein@homelien.no + * (C) 2009 by Holger Hans Peter Freyther + * (C) 2010 by Digium and Matthew Fredrickson + * (C) 2011 by Harald Welte + * + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU 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. + * + */ + +/* TODO: + * detect RR timeout and set SAP state back to SABM_RETRANSMIT + * use of value_string + * further code cleanup (spaghetti) + */ + +#include +#include +#include +#include + +#include "lapd.h" + +#include +#include +#include +#include +#include + +#define SABM_INTERVAL 0, 300000 + +typedef enum { + LAPD_TEI_NONE = 0, + LAPD_TEI_ASSIGNED, + LAPD_TEI_ACTIVE, +} lapd_tei_state; + +const char *lapd_tei_states[] = { + "NONE", + "ASSIGNED", + "ACTIVE", +}; + +typedef enum { + LAPD_TYPE_NONE = 0, + + LAPD_TYPE_I, + LAPD_TYPE_S, + LAPD_TYPE_U, +} lapd_msg_type; + +typedef enum { + /* commands/responses */ + LAPD_CMD_NONE = 0, + + LAPD_CMD_I, + LAPD_CMD_RR, + LAPD_CMD_RNR, + LAPD_CMD_REJ, + + LAPD_CMD_SABME, + LAPD_CMD_DM, + LAPD_CMD_UI, + LAPD_CMD_DISC, + LAPD_CMD_UA, + LAPD_CMD_FRMR, + LAPD_CMD_XID, +} lapd_cmd_type; + +const char *lapd_cmd_types[] = { + "NONE", + + "I", + "RR", + "RNR", + "REJ", + + "SABME", + "DM", + "UI", + "DISC", + "UA", + "FRMR", + "XID", + +}; + +enum lapd_sap_state { + SAP_STATE_INACTIVE, + SAP_STATE_SABM_RETRANS, + SAP_STATE_ACTIVE, +}; + +const char *lapd_sap_states[] = { + "INACTIVE", + "SABM_RETRANS", + "ACTIVE", +}; + +const char *lapd_msg_types = "?ISU"; + +/* structure representing an allocated TEI within a LAPD instance */ +struct lapd_tei { + struct llist_head list; + struct lapd_instance *li; + uint8_t tei; + lapd_tei_state state; + + struct llist_head sap_list; +}; + +/* Structure representing a SAP within a TEI. We use this for TE-mode to + * re-transmit SABM */ +struct lapd_sap { + struct llist_head list; + struct lapd_tei *tei; + uint8_t sapi; + enum lapd_sap_state state; + + /* A valid N(R) value is one that is in the range V(A) ≤ N(R) ≤ V(S). */ + int vs; /* next to be transmitted */ + int va; /* last acked by peer */ + int vr; /* next expected to be received */ + + struct timer_list sabme_timer; /* timer to re-transmit SABM message */ +}; + +/* 3.5.2.2 Send state variable V(S) + * Each point-to-point data link connection endpoint shall have an associated V(S) when using I frame + * commands. V(S) denotes the sequence number of the next I frame to be transmitted. The V(S) can + * take on the value 0 through n minus 1. The value of V(S) shall be incremented by 1 with each + * successive I frame transmission, and shall not exceed V(A) by more than the maximum number of + * outstanding I frames k. The value of k may be in the range of 1 ≤ k ≤ 127. + * + * 3.5.2.3 Acknowledge state variable V(A) + * Each point-to-point data link connection endpoint shall have an associated V(A) when using I frame + * commands and supervisory frame commands/responses. V(A) identifies the last I frame that has been + * acknowledged by its peer [V(A) − 1 equals the N(S) of the last acknowledged I frame]. V(A) can + * take on the value 0 through n minus 1. The value of V(A) shall be updated by the valid N(R) values + * received from its peer (see 3.5.2.6). A valid N(R) value is one that is in the range V(A) ≤ N(R) ≤ + * V(S). + * + * 3.5.2.5 Receive state variable V(R) + * Each point-to-point data link connection endpoint shall have an associated V(R) when using I frame + * commands and supervisory frame commands/responses. V(R) denotes the sequence number of the + * next in-sequence I frame expected to be received. V(R) can take on the value 0 through n minus 1. + * The value of V(R) shall be incremented by one with the receipt of an error-free, in-sequence I frame + * whose N(S) equals V(R). + */ +#define LAPD_NS(sap) (sap->vs) +#define LAPD_NR(sap) (sap->vr) + +/* 3.5.2.4 Send sequence number N(S) + * Only I frames contain N(S), the send sequence number of transmitted I frames. At the time that an in- + * sequence I frame is designated for transmission, the value of N(S) is set equal to V(S). + * + * 3.5.2.6 Receive sequence number N(R) + * All I frames and supervisory frames contain N(R), the expected send sequence number of the next + * received I frame. At the time that a frame of the above types is designated for transmission, the value + * of N(R) is set equal to V(R). N(R) indicates that the data link layer entity transmitting the N(R) has + * correctly received all I frames numbered up to and including N(R) − 1. + */ + +/* Resolve TEI structure from given numeric TEI */ +static struct lapd_tei *teip_from_tei(struct lapd_instance *li, uint8_t tei) +{ + struct lapd_tei *lt; + + llist_for_each_entry(lt, &li->tei_list, list) { + if (lt->tei == tei) + return lt; + } + return NULL; +}; + +static void lapd_tei_set_state(struct lapd_tei *teip, int newstate) +{ + DEBUGP(DMI, "state change on TEI %d: %s -> %s\n", teip->tei, + lapd_tei_states[teip->state], lapd_tei_states[newstate]); + teip->state = newstate; +}; + +/* Allocate a new TEI */ +struct lapd_tei *lapd_tei_alloc(struct lapd_instance *li, uint8_t tei) +{ + struct lapd_tei *teip; + + teip = talloc_zero(li, struct lapd_tei); + if (!teip) + return NULL; + + teip->li = li; + teip->tei = tei; + llist_add(&teip->list, &li->tei_list); + INIT_LLIST_HEAD(&teip->sap_list); + + lapd_tei_set_state(teip, LAPD_TEI_ASSIGNED); + + return teip; +} + +/* Find a SAP within a given TEI */ +static struct lapd_sap *lapd_sap_find(struct lapd_tei *teip, uint8_t sapi) +{ + struct lapd_sap *sap; + + llist_for_each_entry(sap, &teip->sap_list, list) { + if (sap->sapi == sapi) + return sap; + } + + return NULL; +} + +static void sabme_timer_cb(void *_sap); + +/* Allocate a new SAP within a given TEI */ +static struct lapd_sap *lapd_sap_alloc(struct lapd_tei *teip, uint8_t sapi) +{ + struct lapd_sap *sap = talloc_zero(teip, struct lapd_sap); + + LOGP(DMI, LOGL_INFO, "Allocating SAP for SAPI=%u / TEI=%u\n", + sapi, teip->tei); + + sap->sapi = sapi; + sap->tei = teip; + sap->sabme_timer.cb = &sabme_timer_cb; + sap->sabme_timer.data = sap; + + llist_add(&sap->list, &teip->sap_list); + + return sap; +} + +static void lapd_sap_set_state(struct lapd_tei *teip, uint8_t sapi, + enum lapd_sap_state newstate) +{ + struct lapd_sap *sap = lapd_sap_find(teip, sapi); + if (!sap) + return; + + DEBUGP(DMI, "state change on TEI %u / SAPI %u: %s -> %s\n", teip->tei, + sapi, lapd_sap_states[sap->state], lapd_sap_states[newstate]); + switch (sap->state) { + case SAP_STATE_SABM_RETRANS: + if (newstate != SAP_STATE_SABM_RETRANS) + bsc_del_timer(&sap->sabme_timer); + break; + default: + if (newstate == SAP_STATE_SABM_RETRANS) + bsc_schedule_timer(&sap->sabme_timer, SABM_INTERVAL); + break; + } + + sap->state = newstate; +}; + +/* Input function into TEI manager */ +static void lapd_tei_receive(struct lapd_instance *li, uint8_t *data, int len) +{ + uint8_t entity = data[0]; + uint8_t ref = data[1]; + uint8_t mt = data[3]; + uint8_t action = data[4] >> 1; + uint8_t e = data[4] & 1; + uint8_t resp[8]; + struct lapd_tei *teip; + + DEBUGP(DMI, "TEIMGR: entity %x, ref %x, mt %x, action %x, e %x\n", entity, ref, mt, action, e); + + switch (mt) { + case 0x01: /* IDENTITY REQUEST */ + DEBUGP(DMI, "TEIMGR: identity request for TEI %u\n", action); + + teip = teip_from_tei(li, action); + if (!teip) { + LOGP(DMI, LOGL_INFO, "TEI MGR: New TEI %u\n", action); + lapd_tei_alloc(li, action); + } + + /* Send ACCEPT */ + memmove(resp, "\xfe\xff\x03\x0f\x00\x00\x02\x00", 8); + resp[7] = (action << 1) | 1; + li->transmit_cb(resp, 8, li->cbdata); + + if (teip->state == LAPD_TEI_NONE) + lapd_tei_set_state(teip, LAPD_TEI_ASSIGNED); + break; + default: + LOGP(DMI, LOGL_NOTICE, "TEIMGR: unknown mt %x action %x\n", + mt, action); + break; + }; +}; + +/* General input function for any data received for this LAPD instance */ +uint8_t *lapd_receive(struct lapd_instance *li, uint8_t * data, unsigned int len, + int *ilen, lapd_mph_type *prim) +{ + uint8_t sapi, cr, tei, command; + int pf, ns, nr; + uint8_t *contents; + struct lapd_tei *teip; + struct lapd_sap *sap; + + uint8_t resp[8]; + int l = 0; + + *ilen = 0; + *prim = 0; + + if (len < 2) { + DEBUGP(DMI, "len %d < 2\n", len); + return NULL; + }; + + if ((data[0] & 1) != 0 || (data[1] & 1) != 1) { + DEBUGP(DMI, "address field %x/%x not well formed\n", data[0], + data[1]); + return NULL; + }; + + sapi = data[0] >> 2; + cr = (data[0] >> 1) & 1; + tei = data[1] >> 1; + command = li->network_side ^ cr; + //DEBUGP(DMI, " address sapi %x tei %d cmd %d cr %d\n", sapi, tei, command, cr); + + if (len < 3) { + DEBUGP(DMI, "len %d < 3\n", len); + return NULL; + }; + + lapd_msg_type typ = 0; + lapd_cmd_type cmd = 0; + pf = -1; + ns = -1; + nr = -1; + if ((data[2] & 1) == 0) { + typ = LAPD_TYPE_I; + assert(len >= 4); + ns = data[2] >> 1; + nr = data[3] >> 1; + pf = data[3] & 1; + cmd = LAPD_CMD_I; + } else if ((data[2] & 3) == 1) { + typ = LAPD_TYPE_S; + assert(len >= 4); + nr = data[3] >> 1; + pf = data[3] & 1; + switch (data[2]) { + case 0x1: + cmd = LAPD_CMD_RR; + break; + case 0x5: + cmd = LAPD_CMD_RNR; + break; + case 0x9: + cmd = LAPD_CMD_REJ; + break; + default: + LOGP(DMI, LOGL_ERROR, "unknown LAPD S cmd %x\n", data[2]); + return NULL; + }; + } else if ((data[2] & 3) == 3) { + typ = LAPD_TYPE_U; + pf = (data[2] >> 4) & 1; + int val = data[2] & ~(1 << 4); + switch (val) { + case 0x6f: + cmd = LAPD_CMD_SABME; + break; + case 0x0f: + cmd = LAPD_CMD_DM; + break; + case 0x03: + cmd = LAPD_CMD_UI; + break; + case 0x43: + cmd = LAPD_CMD_DISC; + break; + case 0x63: + cmd = LAPD_CMD_UA; + break; + case 0x87: + cmd = LAPD_CMD_FRMR; + break; + case 0xaf: + cmd = LAPD_CMD_XID; + break; + + default: + LOGP(DMI, LOGL_ERROR, "unknown U cmd %x " + "(pf %x data %x)\n", val, pf, data[2]); + return NULL; + }; + }; + + contents = &data[4]; + if (typ == LAPD_TYPE_U) + contents--; + *ilen = len - (contents - data); + + if (tei == 127) + lapd_tei_receive(li, contents, *ilen); + + teip = teip_from_tei(li, tei); + if (!teip) { + LOGP(DMI, LOGL_NOTICE, "Unknown TEI %u\n", tei); + return NULL; + } + + sap = lapd_sap_find(teip, sapi); + if (!sap) { + LOGP(DMI, LOGL_INFO, "No SAP for TEI=%u / SAPI=%u, " + "allocating\n", tei, sapi); + sap = lapd_sap_alloc(teip, sapi); + } + + DEBUGP(DMI, "<- %c %s sapi %x tei %3d cmd %x pf %x ns %3d nr %3d " + "ilen %d teip %p vs %d va %d vr %d len %d\n", + lapd_msg_types[typ], lapd_cmd_types[cmd], sapi, tei, command, pf, + ns, nr, *ilen, teip, sap->vs, sap->va, sap->vr, len); + + switch (cmd) { + case LAPD_CMD_I: + if (ns != sap->vr) { + DEBUGP(DMI, "ns %d != vr %d\n", ns, sap->vr); + if (ns == ((sap->vr - 1) & 0x7f)) { + DEBUGP(DMI, "DOUBLE FRAME, ignoring\n"); + cmd = 0; // ignore + } else { + assert(0); + }; + } else { + //printf("IN SEQUENCE\n"); + sap->vr = (ns + 1) & 0x7f; // FIXME: hack! + }; + + break; + case LAPD_CMD_UI: + break; + case LAPD_CMD_SABME: + sap->vs = 0; + sap->vr = 0; + sap->va = 0; + + // ua + resp[l++] = data[0]; + resp[l++] = (tei << 1) | 1; + resp[l++] = 0x73; + li->transmit_cb(resp, l, li->cbdata); + if (teip->state != LAPD_TEI_ACTIVE) { + if (teip->state == LAPD_TEI_ASSIGNED) { + lapd_tei_set_state(teip, + LAPD_TEI_ACTIVE); + //printf("ASSIGNED and ACTIVE\n"); + } else { +#if 0 + DEBUGP(DMI, "rr in strange state, send rej\n"); + + // rej + resp[l++] = (sap-> sapi << 2) | (li->network_side ? 0 : 2); + resp[l++] = (tei << 1) | 1; + resp[l++] = 0x09; //rej + resp[l++] = ((sap->vr + 1) << 1) | 0; + li->transmit_cb(resp, l, li->cbdata); + pf = 0; // dont reply +#endif + }; + }; + + *prim = LAPD_MPH_ACTIVATE_IND; + break; + case LAPD_CMD_UA: + sap->vs = 0; + sap->vr = 0; + sap->va = 0; + lapd_tei_set_state(teip, LAPD_TEI_ACTIVE); + lapd_sap_set_state(teip, sapi, SAP_STATE_ACTIVE); + *prim = LAPD_MPH_ACTIVATE_IND; + break; + case LAPD_CMD_RR: + sap->va = (nr & 0x7f); +#if 0 + if (teip->state != LAPD_TEI_ACTIVE) { + if (teip->state == LAPD_TEI_ASSIGNED) { + lapd_tei_set_state(teip, LAPD_TEI_ACTIVE); + *prim = LAPD_MPH_ACTIVATE_IND; + //printf("ASSIGNED and ACTIVE\n"); + } else { +#if 0 + DEBUGP(DMI, "rr in strange " "state, send rej\n"); + + // rej + resp[l++] = (sap-> sapi << 2) | (li->network_side ? 0 : 2); + resp[l++] = (tei << 1) | 1; + resp[l++] = 0x09; //rej + resp[l++] = + ((sap->vr + 1) << 1) | 0; + li->transmit_cb(resp, l, li->cbdata); + pf = 0; // dont reply +#endif + }; + }; +#endif + if (pf) { + // interrogating us, send rr + resp[l++] = data[0]; + resp[l++] = (tei << 1) | 1; + resp[l++] = 0x01; // rr + resp[l++] = (LAPD_NR(sap) << 1) | (data[3] & 1); // pf bit from req + + li->transmit_cb(resp, l, li->cbdata); + + }; + break; + case LAPD_CMD_FRMR: + // frame reject +#if 0 + if (teip->state == LAPD_TEI_ACTIVE) + *prim = LAPD_MPH_DEACTIVATE_IND; + lapd_tei_set_state(teip, LAPD_TEI_ASSIGNED); +#endif + LOGP(DMI, LOGL_NOTICE, "frame reject, ignoring\n"); + break; + case LAPD_CMD_DISC: + // disconnect + resp[l++] = data[0]; + resp[l++] = (tei << 1) | 1; + resp[l++] = 0x73; + li->transmit_cb(resp, l, li->cbdata); + lapd_tei_set_state(teip, LAPD_TEI_NONE); + break; + default: + LOGP(DMI, LOGL_NOTICE, "unknown cmd for tei %d (cmd %x)\n", + tei, cmd); + break; + } + + if (typ == LAPD_TYPE_I) { + /* send rr + * Thu Jan 22 19:17:13 2009 <4000> sangoma.c:340 read (62/25) 4: fa 33 01 0a + * lapd <- S RR sapi 3e tei 25 cmd 0 pf 0 ns -1 nr 5 ilen 0 teip 0x613800 vs 7 va 5 vr 2 len 4 + */ + + /* interrogating us, send rr */ + DEBUGP(DMI, "Sending RR response\n"); + resp[l++] = data[0]; + resp[l++] = (tei << 1) | 1; + resp[l++] = 0x01; // rr + resp[l++] = (LAPD_NR(sap) << 1) | (data[3] & 1); // pf bit from req + + li->transmit_cb(resp, l, li->cbdata); + + if (cmd != 0) { + *prim = LAPD_DL_DATA_IND; + return contents; + } + } else if (tei != 127 && typ == LAPD_TYPE_U && cmd == LAPD_CMD_UI) { + *prim = LAPD_DL_UNITDATA_IND; + return contents; + } + + return NULL; +}; + +/* low-level function to send a single SABM message */ +static int lapd_send_sabm(struct lapd_instance *li, uint8_t tei, uint8_t sapi) +{ + struct msgb *msg = msgb_alloc_headroom(1024, 128, "LAPD SABM"); + if (!msg) + return -ENOMEM; + + DEBUGP(DMI, "Sending SABM for TEI=%u, SAPI=%u\n", tei, sapi); + + msgb_put_u8(msg, (sapi << 2) | (li->network_side ? 2 : 0)); + msgb_put_u8(msg, (tei << 1) | 1); + msgb_put_u8(msg, 0x7F); + + li->transmit_cb(msg->data, msg->len, li->cbdata); + + msgb_free(msg); + + return 0; +} + +/* timer call-back function for SABM re-transmission */ +static void sabme_timer_cb(void *_sap) +{ + struct lapd_sap *sap = _sap; + + lapd_send_sabm(sap->tei->li, sap->tei->tei, sap->sapi); + + if (sap->state == SAP_STATE_SABM_RETRANS) + bsc_schedule_timer(&sap->sabme_timer, SABM_INTERVAL); +} + +/* Start a (user-side) SAP for the specified TEI/SAPI on the LAPD instance */ +int lapd_sap_start(struct lapd_instance *li, uint8_t tei, uint8_t sapi) +{ + struct lapd_sap *sap; + struct lapd_tei *teip; + + teip = teip_from_tei(li, tei); + if (!teip) + teip = lapd_tei_alloc(li, tei); + + sap = lapd_sap_find(teip, sapi); + if (sap) + return -EEXIST; + + sap = lapd_sap_alloc(teip, sapi); + + lapd_sap_set_state(teip, sapi, SAP_STATE_SABM_RETRANS); + + return 0; +} + +/* Stop a (user-side) SAP for the specified TEI/SAPI on the LAPD instance */ +int lapd_sap_stop(struct lapd_instance *li, uint8_t tei, uint8_t sapi) +{ + struct lapd_tei *teip; + struct lapd_sap *sap; + + teip = teip_from_tei(li, tei); + if (!teip) + return -ENODEV; + + sap = lapd_sap_find(teip, sapi); + if (!sap) + return -ENODEV; + + lapd_sap_set_state(teip, sapi, SAP_STATE_INACTIVE); + + llist_del(&sap->list); + talloc_free(sap); + + return 0; +} + +/* Transmit Data (I-Frame) on the given LAPD Instance / TEI / SAPI */ +void lapd_transmit(struct lapd_instance *li, uint8_t tei, uint8_t sapi, + uint8_t *data, unsigned int len) +{ + struct lapd_tei *teip = teip_from_tei(li, tei); + struct lapd_sap *sap; + + if (!teip) { + LOGP(DMI, LOGL_ERROR, "Cannot transmit on non-existing " + "TEI %u\n", tei); + return; + } + + sap = lapd_sap_find(teip, sapi); + if (!sap) { + LOGP(DMI, LOGL_INFO, "Tx on unknown SAPI=%u in TEI=%u, " + "allocating\n", sapi, tei); + sap = lapd_sap_alloc(teip, sapi); + } + + /* prepend stuff */ + uint8_t buf[10000]; + memset(buf, 0, sizeof(buf)); + memmove(buf + 4, data, len); + len += 4; + + buf[0] = (sapi << 2) | (li->network_side ? 2 : 0); + buf[1] = (tei << 1) | 1; + buf[2] = (LAPD_NS(sap) << 1); + buf[3] = (LAPD_NR(sap) << 1) | 0; + + sap->vs = (sap->vs + 1) & 0x7f; + + li->transmit_cb(buf, len, li->cbdata); +}; + +/* Allocate a new LAPD instance */ +struct lapd_instance *lapd_instance_alloc(int network_side, + void (*tx_cb)(uint8_t *data, int len, + void *cbdata), void *cbdata) +{ + struct lapd_instance *li; + + li = talloc_zero(NULL, struct lapd_instance); + if (!li) + return NULL; + + li->transmit_cb = tx_cb; + li->cbdata = cbdata; + li->network_side = network_side; + INIT_LLIST_HEAD(&li->tei_list); + + return li; +} diff --git a/openbsc/src/libabis/input/lapd.h b/openbsc/src/libabis/input/lapd.h new file mode 100644 index 000000000..fd11edaa3 --- /dev/null +++ b/openbsc/src/libabis/input/lapd.h @@ -0,0 +1,46 @@ +#ifndef OPENBSC_LAPD_H +#define OPENBSC_LAPD_H + +#include + +#include + +typedef enum { + LAPD_MPH_NONE = 0, + + LAPD_MPH_ACTIVATE_IND, + LAPD_MPH_DEACTIVATE_IND, + + LAPD_DL_DATA_IND, + LAPD_DL_UNITDATA_IND, + +} lapd_mph_type; + +struct lapd_instance { + struct llist_head list; /* list of LAPD instances */ + int network_side; + + void (*transmit_cb)(uint8_t *data, int len, void *cbdata); + void *cbdata; + + struct llist_head tei_list; /* list of TEI in this LAPD instance */ +}; + +extern uint8_t *lapd_receive(struct lapd_instance *li, uint8_t *data, unsigned int len, + int *ilen, lapd_mph_type *prim); + +extern void lapd_transmit(struct lapd_instance *li, uint8_t tei, uint8_t sapi, + uint8_t *data, unsigned int len); + +struct lapd_instance *lapd_instance_alloc(int network_side, + void (*tx_cb)(uint8_t *data, int len, + void *cbdata), void *cbdata); + + +/* Start a (user-side) SAP for the specified TEI/SAPI on the LAPD instance */ +int lapd_sap_start(struct lapd_instance *li, uint8_t tei, uint8_t sapi); + +/* Stop a (user-side) SAP for the specified TEI/SAPI on the LAPD instance */ +int lapd_sap_stop(struct lapd_instance *li, uint8_t tei, uint8_t sapi); + +#endif /* OPENBSC_LAPD_H */ diff --git a/openbsc/src/libabis/input/misdn.c b/openbsc/src/libabis/input/misdn.c new file mode 100644 index 000000000..459887917 --- /dev/null +++ b/openbsc/src/libabis/input/misdn.c @@ -0,0 +1,542 @@ +/* OpenBSC Abis input driver for mISDNuser */ + +/* (C) 2008-2009 by Harald Welte + * (C) 2009 by Holger Hans Peter Freyther + * + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +//#define AF_COMPATIBILITY_FUNC +//#include +#ifndef AF_ISDN +#define AF_ISDN 34 +#define PF_ISDN AF_ISDN +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define TS1_ALLOC_SIZE 300 + +struct prim_name { + unsigned int prim; + const char *name; +}; + +const struct prim_name prim_names[] = { + { PH_CONTROL_IND, "PH_CONTROL_IND" }, + { PH_DATA_IND, "PH_DATA_IND" }, + { PH_DATA_CNF, "PH_DATA_CNF" }, + { PH_ACTIVATE_IND, "PH_ACTIVATE_IND" }, + { DL_ESTABLISH_IND, "DL_ESTABLISH_IND" }, + { DL_ESTABLISH_CNF, "DL_ESTABLISH_CNF" }, + { DL_RELEASE_IND, "DL_RELEASE_IND" }, + { DL_RELEASE_CNF, "DL_RELEASE_CNF" }, + { DL_DATA_IND, "DL_DATA_IND" }, + { DL_UNITDATA_IND, "DL_UNITDATA_IND" }, + { DL_INFORMATION_IND, "DL_INFORMATION_IND" }, + { MPH_ACTIVATE_IND, "MPH_ACTIVATE_IND" }, + { MPH_DEACTIVATE_IND, "MPH_DEACTIVATE_IND" }, +}; + +const char *get_prim_name(unsigned int prim) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(prim_names); i++) { + if (prim_names[i].prim == prim) + return prim_names[i].name; + } + + return "UNKNOWN"; +} + +static int handle_ts1_read(struct bsc_fd *bfd) +{ + struct e1inp_line *line = bfd->data; + unsigned int ts_nr = bfd->priv_nr; + struct e1inp_ts *e1i_ts = &line->ts[ts_nr-1]; + struct e1inp_sign_link *link; + struct msgb *msg = msgb_alloc(TS1_ALLOC_SIZE, "mISDN TS1"); + struct sockaddr_mISDN l2addr; + struct mISDNhead *hh; + socklen_t alen; + int ret; + + if (!msg) + return -ENOMEM; + + hh = (struct mISDNhead *) msg->data; + + alen = sizeof(l2addr); + ret = recvfrom(bfd->fd, msg->data, 300, 0, + (struct sockaddr *) &l2addr, &alen); + if (ret < 0) { + fprintf(stderr, "recvfrom error %s\n", strerror(errno)); + return ret; + } + + if (alen != sizeof(l2addr)) { + fprintf(stderr, "%s error len\n", __func__); + return -EINVAL; + } + + msgb_put(msg, ret); + + DEBUGP(DMI, "alen =%d, dev(%d) channel(%d) sapi(%d) tei(%d)\n", + alen, l2addr.dev, l2addr.channel, l2addr.sapi, l2addr.tei); + + DEBUGP(DMI, "<= len = %d, prim(0x%x) id(0x%x): %s\n", + ret, hh->prim, hh->id, get_prim_name(hh->prim)); + + switch (hh->prim) { + case DL_INFORMATION_IND: + /* mISDN tells us which channel number is allocated for this + * tuple of (SAPI, TEI). */ + DEBUGP(DMI, "DL_INFORMATION_IND: use channel(%d) sapi(%d) tei(%d) for now\n", + l2addr.channel, l2addr.sapi, l2addr.tei); + link = e1inp_lookup_sign_link(e1i_ts, l2addr.tei, l2addr.sapi); + if (!link) { + DEBUGPC(DMI, "mISDN message for unknown sign_link\n"); + msgb_free(msg); + return -EINVAL; + } + /* save the channel number in the driver private struct */ + link->driver.misdn.channel = l2addr.channel; + break; + case DL_ESTABLISH_IND: + DEBUGP(DMI, "DL_ESTABLISH_IND: channel(%d) sapi(%d) tei(%d)\n", + l2addr.channel, l2addr.sapi, l2addr.tei); + /* For some strange reason, sometimes the DL_INFORMATION_IND tells + * us the wrong channel, and we only get the real channel number + * during the DL_ESTABLISH_IND */ + link = e1inp_lookup_sign_link(e1i_ts, l2addr.tei, l2addr.sapi); + if (!link) { + DEBUGPC(DMI, "mISDN message for unknown sign_link\n"); + msgb_free(msg); + return -EINVAL; + } + /* save the channel number in the driver private struct */ + link->driver.misdn.channel = l2addr.channel; + ret = e1inp_event(e1i_ts, S_INP_TEI_UP, l2addr.tei, l2addr.sapi); + break; + case DL_RELEASE_IND: + DEBUGP(DMI, "DL_RELEASE_IND: channel(%d) sapi(%d) tei(%d)\n", + l2addr.channel, l2addr.sapi, l2addr.tei); + ret = e1inp_event(e1i_ts, S_INP_TEI_DN, l2addr.tei, l2addr.sapi); + break; + case DL_DATA_IND: + case DL_UNITDATA_IND: + msg->l2h = msg->data + MISDN_HEADER_LEN; + DEBUGP(DMI, "RX: %s\n", hexdump(msgb_l2(msg), ret - MISDN_HEADER_LEN)); + ret = e1inp_rx_ts(e1i_ts, msg, l2addr.tei, l2addr.sapi); + break; + case PH_ACTIVATE_IND: + DEBUGP(DMI, "PH_ACTIVATE_IND: channel(%d) sapi(%d) tei(%d)\n", + l2addr.channel, l2addr.sapi, l2addr.tei); + break; + case PH_DEACTIVATE_IND: + DEBUGP(DMI, "PH_DEACTIVATE_IND: channel(%d) sapi(%d) tei(%d)\n", + l2addr.channel, l2addr.sapi, l2addr.tei); + break; + default: + break; + } + return ret; +} + +static int ts_want_write(struct e1inp_ts *e1i_ts) +{ + /* We never include the mISDN B-Channel FD into the + * writeset, since it doesn't support poll() based + * write flow control */ + if (e1i_ts->type == E1INP_TS_TYPE_TRAU) + return 0; + + e1i_ts->driver.misdn.fd.when |= BSC_FD_WRITE; + + return 0; +} + +static void timeout_ts1_write(void *data) +{ + struct e1inp_ts *e1i_ts = (struct e1inp_ts *)data; + + /* trigger write of ts1, due to tx delay timer */ + ts_want_write(e1i_ts); +} + +static int handle_ts1_write(struct bsc_fd *bfd) +{ + struct e1inp_line *line = bfd->data; + unsigned int ts_nr = bfd->priv_nr; + struct e1inp_ts *e1i_ts = &line->ts[ts_nr-1]; + struct e1inp_sign_link *sign_link; + struct sockaddr_mISDN sa; + struct msgb *msg; + struct mISDNhead *hh; + u_int8_t *l2_data; + int ret; + + bfd->when &= ~BSC_FD_WRITE; + + /* get the next msg for this timeslot */ + msg = e1inp_tx_ts(e1i_ts, &sign_link); + if (!msg) { + /* no message after tx delay timer */ + return 0; + } + + l2_data = msg->data; + + /* prepend the mISDNhead */ + hh = (struct mISDNhead *) msgb_push(msg, sizeof(*hh)); + hh->prim = DL_DATA_REQ; + + DEBUGP(DMI, "TX channel(%d) TEI(%d) SAPI(%d): %s\n", + sign_link->driver.misdn.channel, sign_link->tei, + sign_link->sapi, hexdump(l2_data, msg->len - MISDN_HEADER_LEN)); + + /* construct the sockaddr */ + sa.family = AF_ISDN; + sa.sapi = sign_link->sapi; + sa.dev = sign_link->tei; + sa.channel = sign_link->driver.misdn.channel; + + ret = sendto(bfd->fd, msg->data, msg->len, 0, + (struct sockaddr *)&sa, sizeof(sa)); + if (ret < 0) + fprintf(stderr, "%s sendto failed %d\n", __func__, ret); + msgb_free(msg); + + /* set tx delay timer for next event */ + e1i_ts->sign.tx_timer.cb = timeout_ts1_write; + e1i_ts->sign.tx_timer.data = e1i_ts; + bsc_schedule_timer(&e1i_ts->sign.tx_timer, 0, e1i_ts->sign.delay); + + return ret; +} + +#define BCHAN_TX_GRAN 160 +/* write to a B channel TS */ +static int handle_tsX_write(struct bsc_fd *bfd) +{ + struct e1inp_line *line = bfd->data; + unsigned int ts_nr = bfd->priv_nr; + struct e1inp_ts *e1i_ts = &line->ts[ts_nr-1]; + struct mISDNhead *hh; + u_int8_t tx_buf[BCHAN_TX_GRAN + sizeof(*hh)]; + struct subch_mux *mx = &e1i_ts->trau.mux; + int ret; + + hh = (struct mISDNhead *) tx_buf; + hh->prim = PH_DATA_REQ; + + subchan_mux_out(mx, tx_buf+sizeof(*hh), BCHAN_TX_GRAN); + + DEBUGP(DMIB, "BCHAN TX: %s\n", + hexdump(tx_buf+sizeof(*hh), BCHAN_TX_GRAN)); + + ret = send(bfd->fd, tx_buf, sizeof(*hh) + BCHAN_TX_GRAN, 0); + if (ret < sizeof(*hh) + BCHAN_TX_GRAN) + DEBUGP(DMIB, "send returns %d instead of %zu\n", ret, + sizeof(*hh) + BCHAN_TX_GRAN); + + return ret; +} + +#define TSX_ALLOC_SIZE 4096 +/* FIXME: read from a B channel TS */ +static int handle_tsX_read(struct bsc_fd *bfd) +{ + struct e1inp_line *line = bfd->data; + unsigned int ts_nr = bfd->priv_nr; + struct e1inp_ts *e1i_ts = &line->ts[ts_nr-1]; + struct msgb *msg = msgb_alloc(TSX_ALLOC_SIZE, "mISDN TSx"); + struct mISDNhead *hh; + int ret; + + if (!msg) + return -ENOMEM; + + hh = (struct mISDNhead *) msg->data; + + ret = recv(bfd->fd, msg->data, TSX_ALLOC_SIZE, 0); + if (ret < 0) { + fprintf(stderr, "recvfrom error %s\n", strerror(errno)); + return ret; + } + + msgb_put(msg, ret); + + if (hh->prim != PH_CONTROL_IND) + DEBUGP(DMIB, "<= BCHAN len = %d, prim(0x%x) id(0x%x): %s\n", + ret, hh->prim, hh->id, get_prim_name(hh->prim)); + + switch (hh->prim) { + case PH_DATA_IND: + msg->l2h = msg->data + MISDN_HEADER_LEN; + DEBUGP(DMIB, "BCHAN RX: %s\n", + hexdump(msgb_l2(msg), ret - MISDN_HEADER_LEN)); + ret = e1inp_rx_ts(e1i_ts, msg, 0, 0); + break; + case PH_ACTIVATE_IND: + case PH_DATA_CNF: + /* physical layer indicates that data has been sent, + * we thus can send some more data */ + ret = handle_tsX_write(bfd); + default: + break; + } + /* FIXME: why do we free signalling msgs in the caller, and trau not? */ + msgb_free(msg); + + return ret; +} + +/* callback from select.c in case one of the fd's can be read/written */ +static int misdn_fd_cb(struct bsc_fd *bfd, unsigned int what) +{ + struct e1inp_line *line = bfd->data; + unsigned int ts_nr = bfd->priv_nr; + unsigned int idx = ts_nr-1; + struct e1inp_ts *e1i_ts = &line->ts[idx]; + int rc = 0; + + switch (e1i_ts->type) { + case E1INP_TS_TYPE_SIGN: + if (what & BSC_FD_READ) + rc = handle_ts1_read(bfd); + if (what & BSC_FD_WRITE) + rc = handle_ts1_write(bfd); + break; + case E1INP_TS_TYPE_TRAU: + if (what & BSC_FD_READ) + rc = handle_tsX_read(bfd); + /* We never include the mISDN B-Channel FD into the + * writeset, since it doesn't support poll() based + * write flow control */ + break; + default: + fprintf(stderr, "unknown E1 TS type %u\n", e1i_ts->type); + break; + } + + return rc; +} + +static int activate_bchan(struct e1inp_line *line, int ts, int act) +{ + struct mISDNhead hh; + int ret; + unsigned int idx = ts-1; + struct e1inp_ts *e1i_ts = &line->ts[idx]; + struct bsc_fd *bfd = &e1i_ts->driver.misdn.fd; + + fprintf(stdout, "activate bchan\n"); + if (act) + hh.prim = PH_ACTIVATE_REQ; + else + hh.prim = PH_DEACTIVATE_REQ; + + hh.id = MISDN_ID_ANY; + ret = sendto(bfd->fd, &hh, sizeof(hh), 0, NULL, 0); + if (ret < 0) { + fprintf(stdout, "could not send ACTIVATE_RQ %s\n", + strerror(errno)); + } + + return ret; +} + +static int mi_e1_line_update(struct e1inp_line *line); + +struct e1inp_driver misdn_driver = { + .name = "misdn", + .want_write = ts_want_write, + .default_delay = 50000, + .line_update = &mi_e1_line_update, +}; + +static int mi_e1_setup(struct e1inp_line *line, int release_l2) +{ + int ts, ret; + + /* TS0 is CRC4, don't need any fd for it */ + for (ts = 1; ts < NUM_E1_TS; ts++) { + unsigned int idx = ts-1; + struct e1inp_ts *e1i_ts = &line->ts[idx]; + struct bsc_fd *bfd = &e1i_ts->driver.misdn.fd; + struct sockaddr_mISDN addr; + + bfd->data = line; + bfd->priv_nr = ts; + bfd->cb = misdn_fd_cb; + + switch (e1i_ts->type) { + case E1INP_TS_TYPE_NONE: + continue; + break; + case E1INP_TS_TYPE_SIGN: + bfd->fd = socket(PF_ISDN, SOCK_DGRAM, ISDN_P_LAPD_NT); + bfd->when = BSC_FD_READ; + break; + case E1INP_TS_TYPE_TRAU: + bfd->fd = socket(PF_ISDN, SOCK_DGRAM, ISDN_P_B_RAW); + /* We never include the mISDN B-Channel FD into the + * writeset, since it doesn't support poll() based + * write flow control */ + bfd->when = BSC_FD_READ; + break; + } + + if (bfd->fd < 0) { + fprintf(stderr, "%s could not open socket %s\n", + __func__, strerror(errno)); + return bfd->fd; + } + + memset(&addr, 0, sizeof(addr)); + addr.family = AF_ISDN; + addr.dev = line->num; + switch (e1i_ts->type) { + case E1INP_TS_TYPE_SIGN: + addr.channel = 0; + /* SAPI not supported yet in kernel */ + //addr.sapi = e1inp_ts->sign.sapi; + addr.sapi = 0; + addr.tei = GROUP_TEI; + break; + case E1INP_TS_TYPE_TRAU: + addr.channel = ts; + break; + default: + DEBUGP(DMI, "unsupported E1 TS type: %u\n", + e1i_ts->type); + break; + } + + ret = bind(bfd->fd, (struct sockaddr *) &addr, sizeof(addr)); + if (ret < 0) { + fprintf(stderr, "could not bind l2 socket %s\n", + strerror(errno)); + return -EIO; + } + + if (e1i_ts->type == E1INP_TS_TYPE_SIGN) { + ret = ioctl(bfd->fd, IMCLEAR_L2, &release_l2); + if (ret < 0) { + fprintf(stderr, "could not send IOCTL IMCLEAN_L2 %s\n", strerror(errno)); + return -EIO; + } + } + + /* FIXME: only activate B-Channels once we start to + * use them to conserve CPU power */ + if (e1i_ts->type == E1INP_TS_TYPE_TRAU) + activate_bchan(line, ts, 1); + + ret = bsc_register_fd(bfd); + if (ret < 0) { + fprintf(stderr, "could not register FD: %s\n", + strerror(ret)); + return ret; + } + } + + return 0; +} + +static int mi_e1_line_update(struct e1inp_line *line) +{ + struct mISDN_devinfo devinfo; + int sk, ret, cnt; + + if (line->driver != &misdn_driver) + return -EINVAL; + + /* open the ISDN card device */ + sk = socket(PF_ISDN, SOCK_RAW, ISDN_P_BASE); + if (sk < 0) { + fprintf(stderr, "%s could not open socket %s\n", + __func__, strerror(errno)); + return sk; + } + + ret = ioctl(sk, IMGETCOUNT, &cnt); + if (ret) { + fprintf(stderr, "%s error getting interf count: %s\n", + __func__, strerror(errno)); + close(sk); + return -ENODEV; + } + //DEBUGP(DMI,"%d device%s found\n", cnt, (cnt==1)?"":"s"); + printf("%d device%s found\n", cnt, (cnt==1)?"":"s"); +#if 1 + devinfo.id = line->num; + ret = ioctl(sk, IMGETDEVINFO, &devinfo); + if (ret < 0) { + fprintf(stdout, "error getting info for device %d: %s\n", + line->num, strerror(errno)); + return -ENODEV; + } + fprintf(stdout, " id: %d\n", devinfo.id); + fprintf(stdout, " Dprotocols: %08x\n", devinfo.Dprotocols); + fprintf(stdout, " Bprotocols: %08x\n", devinfo.Bprotocols); + fprintf(stdout, " protocol: %d\n", devinfo.protocol); + fprintf(stdout, " nrbchan: %d\n", devinfo.nrbchan); + fprintf(stdout, " name: %s\n", devinfo.name); +#endif + + if (!(devinfo.Dprotocols & (1 << ISDN_P_NT_E1))) { + fprintf(stderr, "error: card is not of type E1 (NT-mode)\n"); + return -EINVAL; + } + + ret = mi_e1_setup(line, 1); + if (ret) + return ret; + + return 0; +} + +void e1inp_misdn_init(void) +{ + /* register the driver with the core */ + e1inp_driver_register(&misdn_driver); +} diff --git a/openbsc/src/libbsc/Makefile.am b/openbsc/src/libbsc/Makefile.am new file mode 100644 index 000000000..91be7fa62 --- /dev/null +++ b/openbsc/src/libbsc/Makefile.am @@ -0,0 +1,21 @@ +INCLUDES = $(all_includes) -I$(top_srcdir)/include -I$(top_builddir) +AM_CFLAGS=-Wall $(LIBOSMOCORE_CFLAGS) $(LIBOSMOVTY_CFLAGS) $(COVERAGE_CFLAGS) +AM_LDFLAGS = $(LIBOSMOCORE_LIBS) $(COVERAGE_LDFLAGS) + +noinst_LIBRARIES = libbsc.a + +libbsc_a_SOURCES = abis_nm.c abis_nm_vty.c \ + abis_om2000.c abis_om2000_vty.c \ + abis_rsl.c bsc_rll.c \ + paging.c \ + bts_ericsson_rbs2000.c bts_ipaccess_nanobts.c bts_siemens_bs11.c bts_unknown.c \ + chan_alloc.c \ + gsm_subscriber_base.c \ + handover_decision.c handover_logic.c meas_rep.c \ + rest_octets.c system_information.c \ + e1_config.c \ + transaction.c \ + bsc_api.c bsc_msc.c bsc_vty.c \ + gsm_04_08_utils.c \ + bsc_init.c + diff --git a/openbsc/src/libbsc/abis_nm.c b/openbsc/src/libbsc/abis_nm.c new file mode 100644 index 000000000..45db8bac5 --- /dev/null +++ b/openbsc/src/libbsc/abis_nm.c @@ -0,0 +1,3126 @@ +/* GSM Network Management (OML) messages on the A-bis interface + * 3GPP TS 12.21 version 8.0.0 Release 1999 / ETSI TS 100 623 V8.0.0 */ + +/* (C) 2008-2009 by Harald Welte + * + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + * + */ + + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#define OM_ALLOC_SIZE 1024 +#define OM_HEADROOM_SIZE 128 +#define IPACC_SEGMENT_SIZE 245 + +/* unidirectional messages from BTS to BSC */ +static const enum abis_nm_msgtype reports[] = { + NM_MT_SW_ACTIVATED_REP, + NM_MT_TEST_REP, + NM_MT_STATECHG_EVENT_REP, + NM_MT_FAILURE_EVENT_REP, +}; + +/* messages without ACK/NACK */ +static const enum abis_nm_msgtype no_ack_nack[] = { + NM_MT_MEAS_RES_REQ, + NM_MT_STOP_MEAS, + NM_MT_START_MEAS, +}; + +/* Messages related to software load */ +static const enum abis_nm_msgtype sw_load_msgs[] = { + NM_MT_LOAD_INIT_ACK, + NM_MT_LOAD_INIT_NACK, + NM_MT_LOAD_SEG_ACK, + NM_MT_LOAD_ABORT, + NM_MT_LOAD_END_ACK, + NM_MT_LOAD_END_NACK, + //NM_MT_SW_ACT_REQ, + NM_MT_ACTIVATE_SW_ACK, + NM_MT_ACTIVATE_SW_NACK, + NM_MT_SW_ACTIVATED_REP, +}; + +static const enum abis_nm_msgtype nacks[] = { + NM_MT_LOAD_INIT_NACK, + NM_MT_LOAD_END_NACK, + NM_MT_SW_ACT_REQ_NACK, + NM_MT_ACTIVATE_SW_NACK, + NM_MT_ESTABLISH_TEI_NACK, + NM_MT_CONN_TERR_SIGN_NACK, + NM_MT_DISC_TERR_SIGN_NACK, + NM_MT_CONN_TERR_TRAF_NACK, + NM_MT_DISC_TERR_TRAF_NACK, + NM_MT_CONN_MDROP_LINK_NACK, + NM_MT_DISC_MDROP_LINK_NACK, + NM_MT_SET_BTS_ATTR_NACK, + NM_MT_SET_RADIO_ATTR_NACK, + NM_MT_SET_CHAN_ATTR_NACK, + NM_MT_PERF_TEST_NACK, + NM_MT_SEND_TEST_REP_NACK, + NM_MT_STOP_TEST_NACK, + NM_MT_STOP_EVENT_REP_NACK, + NM_MT_REST_EVENT_REP_NACK, + NM_MT_CHG_ADM_STATE_NACK, + NM_MT_CHG_ADM_STATE_REQ_NACK, + NM_MT_REP_OUTST_ALARMS_NACK, + NM_MT_CHANGEOVER_NACK, + NM_MT_OPSTART_NACK, + NM_MT_REINIT_NACK, + NM_MT_SET_SITE_OUT_NACK, + NM_MT_CHG_HW_CONF_NACK, + NM_MT_GET_ATTR_NACK, + NM_MT_SET_ALARM_THRES_NACK, + NM_MT_BS11_BEGIN_DB_TX_NACK, + NM_MT_BS11_END_DB_TX_NACK, + NM_MT_BS11_CREATE_OBJ_NACK, + NM_MT_BS11_DELETE_OBJ_NACK, +}; + +static const struct value_string nack_names[] = { + { NM_MT_LOAD_INIT_NACK, "SOFTWARE LOAD INIT" }, + { NM_MT_LOAD_END_NACK, "SOFTWARE LOAD END" }, + { NM_MT_SW_ACT_REQ_NACK, "SOFTWARE ACTIVATE REQUEST" }, + { NM_MT_ACTIVATE_SW_NACK, "ACTIVATE SOFTWARE" }, + { NM_MT_ESTABLISH_TEI_NACK, "ESTABLISH TEI" }, + { NM_MT_CONN_TERR_SIGN_NACK, "CONNECT TERRESTRIAL SIGNALLING" }, + { NM_MT_DISC_TERR_SIGN_NACK, "DISCONNECT TERRESTRIAL SIGNALLING" }, + { NM_MT_CONN_TERR_TRAF_NACK, "CONNECT TERRESTRIAL TRAFFIC" }, + { NM_MT_DISC_TERR_TRAF_NACK, "DISCONNECT TERRESTRIAL TRAFFIC" }, + { NM_MT_CONN_MDROP_LINK_NACK, "CONNECT MULTI-DROP LINK" }, + { NM_MT_DISC_MDROP_LINK_NACK, "DISCONNECT MULTI-DROP LINK" }, + { NM_MT_SET_BTS_ATTR_NACK, "SET BTS ATTRIBUTE" }, + { NM_MT_SET_RADIO_ATTR_NACK, "SET RADIO ATTRIBUTE" }, + { NM_MT_SET_CHAN_ATTR_NACK, "SET CHANNEL ATTRIBUTE" }, + { NM_MT_PERF_TEST_NACK, "PERFORM TEST" }, + { NM_MT_SEND_TEST_REP_NACK, "SEND TEST REPORT" }, + { NM_MT_STOP_TEST_NACK, "STOP TEST" }, + { NM_MT_STOP_EVENT_REP_NACK, "STOP EVENT REPORT" }, + { NM_MT_REST_EVENT_REP_NACK, "RESET EVENT REPORT" }, + { NM_MT_CHG_ADM_STATE_NACK, "CHANGE ADMINISTRATIVE STATE" }, + { NM_MT_CHG_ADM_STATE_REQ_NACK, + "CHANGE ADMINISTRATIVE STATE REQUEST" }, + { NM_MT_REP_OUTST_ALARMS_NACK, "REPORT OUTSTANDING ALARMS" }, + { NM_MT_CHANGEOVER_NACK, "CHANGEOVER" }, + { NM_MT_OPSTART_NACK, "OPSTART" }, + { NM_MT_REINIT_NACK, "REINIT" }, + { NM_MT_SET_SITE_OUT_NACK, "SET SITE OUTPUT" }, + { NM_MT_CHG_HW_CONF_NACK, "CHANGE HARDWARE CONFIGURATION" }, + { NM_MT_GET_ATTR_NACK, "GET ATTRIBUTE" }, + { NM_MT_SET_ALARM_THRES_NACK, "SET ALARM THRESHOLD" }, + { NM_MT_BS11_BEGIN_DB_TX_NACK, "BS11 BEGIN DATABASE TRANSMISSION" }, + { NM_MT_BS11_END_DB_TX_NACK, "BS11 END DATABASE TRANSMISSION" }, + { NM_MT_BS11_CREATE_OBJ_NACK, "BS11 CREATE OBJECT" }, + { NM_MT_BS11_DELETE_OBJ_NACK, "BS11 DELETE OBJECT" }, + { 0, NULL } +}; + +/* Chapter 9.4.36 */ +static const struct value_string nack_cause_names[] = { + /* General Nack Causes */ + { NM_NACK_INCORR_STRUCT, "Incorrect message structure" }, + { NM_NACK_MSGTYPE_INVAL, "Invalid message type value" }, + { NM_NACK_OBJCLASS_INVAL, "Invalid Object class value" }, + { NM_NACK_OBJCLASS_NOTSUPP, "Object class not supported" }, + { NM_NACK_BTSNR_UNKN, "BTS no. unknown" }, + { NM_NACK_TRXNR_UNKN, "Baseband Transceiver no. unknown" }, + { NM_NACK_OBJINST_UNKN, "Object Instance unknown" }, + { NM_NACK_ATTRID_INVAL, "Invalid attribute identifier value" }, + { NM_NACK_ATTRID_NOTSUPP, "Attribute identifier not supported" }, + { NM_NACK_PARAM_RANGE, "Parameter value outside permitted range" }, + { NM_NACK_ATTRLIST_INCONSISTENT,"Inconsistency in attribute list" }, + { NM_NACK_SPEC_IMPL_NOTSUPP, "Specified implementation not supported" }, + { NM_NACK_CANT_PERFORM, "Message cannot be performed" }, + /* Specific Nack Causes */ + { NM_NACK_RES_NOTIMPL, "Resource not implemented" }, + { NM_NACK_RES_NOTAVAIL, "Resource not available" }, + { NM_NACK_FREQ_NOTAVAIL, "Frequency not available" }, + { NM_NACK_TEST_NOTSUPP, "Test not supported" }, + { NM_NACK_CAPACITY_RESTR, "Capacity restrictions" }, + { NM_NACK_PHYSCFG_NOTPERFORM, "Physical configuration cannot be performed" }, + { NM_NACK_TEST_NOTINIT, "Test not initiated" }, + { NM_NACK_PHYSCFG_NOTRESTORE, "Physical configuration cannot be restored" }, + { NM_NACK_TEST_NOSUCH, "No such test" }, + { NM_NACK_TEST_NOSTOP, "Test cannot be stopped" }, + { NM_NACK_MSGINCONSIST_PHYSCFG, "Message inconsistent with physical configuration" }, + { NM_NACK_FILE_INCOMPLETE, "Complete file notreceived" }, + { NM_NACK_FILE_NOTAVAIL, "File not available at destination" }, + { NM_NACK_FILE_NOTACTIVATE, "File cannot be activate" }, + { NM_NACK_REQ_NOT_GRANT, "Request not granted" }, + { NM_NACK_WAIT, "Wait" }, + { NM_NACK_NOTH_REPORT_EXIST, "Nothing reportable existing" }, + { NM_NACK_MEAS_NOTSUPP, "Measurement not supported" }, + { NM_NACK_MEAS_NOTSTART, "Measurement not started" }, + { 0, NULL } +}; + +static const char *nack_cause_name(u_int8_t cause) +{ + return get_value_string(nack_cause_names, cause); +} + +/* Chapter 9.4.16: Event Type */ +static const struct value_string event_type_names[] = { + { NM_EVT_COMM_FAIL, "communication failure" }, + { NM_EVT_QOS_FAIL, "quality of service failure" }, + { NM_EVT_PROC_FAIL, "processing failure" }, + { NM_EVT_EQUIP_FAIL, "equipment failure" }, + { NM_EVT_ENV_FAIL, "environment failure" }, + { 0, NULL } +}; + +static const char *event_type_name(u_int8_t cause) +{ + return get_value_string(event_type_names, cause); +} + +/* Chapter 9.4.63: Perceived Severity */ +static const struct value_string severity_names[] = { + { NM_SEVER_CEASED, "failure ceased" }, + { NM_SEVER_CRITICAL, "critical failure" }, + { NM_SEVER_MAJOR, "major failure" }, + { NM_SEVER_MINOR, "minor failure" }, + { NM_SEVER_WARNING, "warning level failure" }, + { NM_SEVER_INDETERMINATE, "indeterminate failure" }, + { 0, NULL } +}; + +static const char *severity_name(u_int8_t cause) +{ + return get_value_string(severity_names, cause); +} + +/* Attributes that the BSC can set, not only get, according to Section 9.4 */ +static const enum abis_nm_attr nm_att_settable[] = { + NM_ATT_ADD_INFO, + NM_ATT_ADD_TEXT, + NM_ATT_DEST, + NM_ATT_EVENT_TYPE, + NM_ATT_FILE_DATA, + NM_ATT_GET_ARI, + NM_ATT_HW_CONF_CHG, + NM_ATT_LIST_REQ_ATTR, + NM_ATT_MDROP_LINK, + NM_ATT_MDROP_NEXT, + NM_ATT_NACK_CAUSES, + NM_ATT_OUTST_ALARM, + NM_ATT_PHYS_CONF, + NM_ATT_PROB_CAUSE, + NM_ATT_RAD_SUBC, + NM_ATT_SOURCE, + NM_ATT_SPEC_PROB, + NM_ATT_START_TIME, + NM_ATT_TEST_DUR, + NM_ATT_TEST_NO, + NM_ATT_TEST_REPORT, + NM_ATT_WINDOW_SIZE, + NM_ATT_SEVERITY, + NM_ATT_MEAS_RES, + NM_ATT_MEAS_TYPE, +}; + +const struct tlv_definition nm_att_tlvdef = { + .def = { + [NM_ATT_ABIS_CHANNEL] = { TLV_TYPE_FIXED, 3 }, + [NM_ATT_ADD_INFO] = { TLV_TYPE_TL16V }, + [NM_ATT_ADD_TEXT] = { TLV_TYPE_TL16V }, + [NM_ATT_ADM_STATE] = { TLV_TYPE_TV }, + [NM_ATT_ARFCN_LIST]= { TLV_TYPE_TL16V }, + [NM_ATT_AUTON_REPORT] = { TLV_TYPE_TV }, + [NM_ATT_AVAIL_STATUS] = { TLV_TYPE_TL16V }, + [NM_ATT_BCCH_ARFCN] = { TLV_TYPE_FIXED, 2 }, + [NM_ATT_BSIC] = { TLV_TYPE_TV }, + [NM_ATT_BTS_AIR_TIMER] = { TLV_TYPE_TV }, + [NM_ATT_CCCH_L_I_P] = { TLV_TYPE_TV }, + [NM_ATT_CCCH_L_T] = { TLV_TYPE_TV }, + [NM_ATT_CHAN_COMB] = { TLV_TYPE_TV }, + [NM_ATT_CONN_FAIL_CRIT] = { TLV_TYPE_TL16V }, + [NM_ATT_DEST] = { TLV_TYPE_TL16V }, + [NM_ATT_EVENT_TYPE] = { TLV_TYPE_TV }, + [NM_ATT_FILE_DATA] = { TLV_TYPE_TL16V }, + [NM_ATT_FILE_ID] = { TLV_TYPE_TL16V }, + [NM_ATT_FILE_VERSION] = { TLV_TYPE_TL16V }, + [NM_ATT_GSM_TIME] = { TLV_TYPE_FIXED, 2 }, + [NM_ATT_HSN] = { TLV_TYPE_TV }, + [NM_ATT_HW_CONFIG] = { TLV_TYPE_TL16V }, + [NM_ATT_HW_DESC] = { TLV_TYPE_TL16V }, + [NM_ATT_INTAVE_PARAM] = { TLV_TYPE_TV }, + [NM_ATT_INTERF_BOUND] = { TLV_TYPE_FIXED, 6 }, + [NM_ATT_LIST_REQ_ATTR] = { TLV_TYPE_TL16V }, + [NM_ATT_MAIO] = { TLV_TYPE_TV }, + [NM_ATT_MANUF_STATE] = { TLV_TYPE_TV }, + [NM_ATT_MANUF_THRESH] = { TLV_TYPE_TL16V }, + [NM_ATT_MANUF_ID] = { TLV_TYPE_TL16V }, + [NM_ATT_MAX_TA] = { TLV_TYPE_TV }, + [NM_ATT_MDROP_LINK] = { TLV_TYPE_FIXED, 2 }, + [NM_ATT_MDROP_NEXT] = { TLV_TYPE_FIXED, 2 }, + [NM_ATT_NACK_CAUSES] = { TLV_TYPE_TV }, + [NM_ATT_NY1] = { TLV_TYPE_TV }, + [NM_ATT_OPER_STATE] = { TLV_TYPE_TV }, + [NM_ATT_OVERL_PERIOD] = { TLV_TYPE_TL16V }, + [NM_ATT_PHYS_CONF] = { TLV_TYPE_TL16V }, + [NM_ATT_POWER_CLASS] = { TLV_TYPE_TV }, + [NM_ATT_POWER_THRESH] = { TLV_TYPE_FIXED, 3 }, + [NM_ATT_PROB_CAUSE] = { TLV_TYPE_FIXED, 3 }, + [NM_ATT_RACH_B_THRESH] = { TLV_TYPE_TV }, + [NM_ATT_LDAVG_SLOTS] = { TLV_TYPE_FIXED, 2 }, + [NM_ATT_RAD_SUBC] = { TLV_TYPE_TV }, + [NM_ATT_RF_MAXPOWR_R] = { TLV_TYPE_TV }, + [NM_ATT_SITE_INPUTS] = { TLV_TYPE_TL16V }, + [NM_ATT_SITE_OUTPUTS] = { TLV_TYPE_TL16V }, + [NM_ATT_SOURCE] = { TLV_TYPE_TL16V }, + [NM_ATT_SPEC_PROB] = { TLV_TYPE_TV }, + [NM_ATT_START_TIME] = { TLV_TYPE_FIXED, 2 }, + [NM_ATT_T200] = { TLV_TYPE_FIXED, 7 }, + [NM_ATT_TEI] = { TLV_TYPE_TV }, + [NM_ATT_TEST_DUR] = { TLV_TYPE_FIXED, 2 }, + [NM_ATT_TEST_NO] = { TLV_TYPE_TV }, + [NM_ATT_TEST_REPORT] = { TLV_TYPE_TL16V }, + [NM_ATT_VSWR_THRESH] = { TLV_TYPE_FIXED, 2 }, + [NM_ATT_WINDOW_SIZE] = { TLV_TYPE_TV }, + [NM_ATT_TSC] = { TLV_TYPE_TV }, + [NM_ATT_SW_CONFIG] = { TLV_TYPE_TL16V }, + [NM_ATT_SEVERITY] = { TLV_TYPE_TV }, + [NM_ATT_GET_ARI] = { TLV_TYPE_TL16V }, + [NM_ATT_HW_CONF_CHG] = { TLV_TYPE_TL16V }, + [NM_ATT_OUTST_ALARM] = { TLV_TYPE_TV }, + [NM_ATT_MEAS_RES] = { TLV_TYPE_TL16V }, + }, +}; + +static const enum abis_nm_chan_comb chcomb4pchan[] = { + [GSM_PCHAN_CCCH] = NM_CHANC_mainBCCH, + [GSM_PCHAN_CCCH_SDCCH4] = NM_CHANC_BCCHComb, + [GSM_PCHAN_TCH_F] = NM_CHANC_TCHFull, + [GSM_PCHAN_TCH_H] = NM_CHANC_TCHHalf, + [GSM_PCHAN_SDCCH8_SACCH8C] = NM_CHANC_SDCCH, + [GSM_PCHAN_PDCH] = NM_CHANC_IPAC_PDCH, + [GSM_PCHAN_TCH_F_PDCH] = NM_CHANC_IPAC_TCHFull_PDCH, + /* FIXME: bounds check */ +}; + +int abis_nm_chcomb4pchan(enum gsm_phys_chan_config pchan) +{ + if (pchan < ARRAY_SIZE(chcomb4pchan)) + return chcomb4pchan[pchan]; + + return -EINVAL; +} + +int abis_nm_tlv_parse(struct tlv_parsed *tp, struct gsm_bts *bts, const u_int8_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, reports, ARRAY_SIZE(reports)); +} + +#define MT_ACK(x) (x+1) +#define MT_NACK(x) (x+2) + +static void fill_om_hdr(struct abis_om_hdr *oh, u_int8_t len) +{ + oh->mdisc = ABIS_OM_MDISC_FOM; + oh->placement = ABIS_OM_PLACEMENT_ONLY; + oh->sequence = 0; + oh->length = len; +} + +static void fill_om_fom_hdr(struct abis_om_hdr *oh, u_int8_t len, + u_int8_t msg_type, u_int8_t obj_class, + u_int8_t bts_nr, u_int8_t trx_nr, u_int8_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; +} + +static struct msgb *nm_msgb_alloc(void) +{ + return msgb_alloc_headroom(OM_ALLOC_SIZE, OM_HEADROOM_SIZE, + "OML"); +} + +/* Send a OML NM Message from BSC to BTS */ +static int abis_nm_queue_msg(struct gsm_bts *bts, struct msgb *msg) +{ + msg->trx = bts->c0; + + /* 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, 0); + } 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); + +const struct value_string abis_nm_obj_class_names[] = { + { NM_OC_SITE_MANAGER, "SITE-MANAGER" }, + { NM_OC_BTS, "BTS" }, + { NM_OC_RADIO_CARRIER, "RADIO-CARRIER" }, + { NM_OC_BASEB_TRANSC, "BASEBAND-TRANSCEIVER" }, + { NM_OC_CHANNEL, "CHANNEL" }, + { NM_OC_BS11_ADJC, "ADJC" }, + { NM_OC_BS11_HANDOVER, "HANDOVER" }, + { NM_OC_BS11_PWR_CTRL, "POWER-CONTROL" }, + { NM_OC_BS11_BTSE, "BTSE" }, + { NM_OC_BS11_RACK, "RACK" }, + { NM_OC_BS11_TEST, "TEST" }, + { NM_OC_BS11_ENVABTSE, "ENVABTSE" }, + { NM_OC_BS11_BPORT, "BPORT" }, + { NM_OC_GPRS_NSE, "GPRS-NSE" }, + { NM_OC_GPRS_CELL, "GPRS-CELL" }, + { NM_OC_GPRS_NSVC, "GPRS-NSVC" }, + { NM_OC_BS11, "SIEMENSHW" }, + { 0, NULL } +}; + +static const char *obj_class_name(u_int8_t oc) +{ + return get_value_string(abis_nm_obj_class_names, oc); +} + +const char *nm_opstate_name(u_int8_t os) +{ + switch (os) { + case NM_OPSTATE_DISABLED: + return "Disabled"; + case NM_OPSTATE_ENABLED: + return "Enabled"; + case NM_OPSTATE_NULL: + return "NULL"; + default: + return "RFU"; + } +} + +/* Chapter 9.4.7 */ +static const struct value_string avail_names[] = { + { 0, "In test" }, + { 1, "Failed" }, + { 2, "Power off" }, + { 3, "Off line" }, + /* Not used */ + { 5, "Dependency" }, + { 6, "Degraded" }, + { 7, "Not installed" }, + { 0xff, "OK" }, + { 0, NULL } +}; + +const char *nm_avail_name(u_int8_t avail) +{ + return get_value_string(avail_names, avail); +} + +static struct value_string test_names[] = { + /* FIXME: standard test names */ + { NM_IPACC_TESTNO_CHAN_USAGE, "Channel Usage" }, + { NM_IPACC_TESTNO_BCCH_CHAN_USAGE, "BCCH Channel Usage" }, + { NM_IPACC_TESTNO_FREQ_SYNC, "Frequency Synchronization" }, + { NM_IPACC_TESTNO_BCCH_INFO, "BCCH Info" }, + { NM_IPACC_TESTNO_TX_BEACON, "Transmit Beacon" }, + { NM_IPACC_TESTNO_SYSINFO_MONITOR, "System Info Monitor" }, + { NM_IPACC_TESTNO_BCCCH_MONITOR, "BCCH Monitor" }, + { 0, NULL } +}; + +const struct value_string abis_nm_adm_state_names[] = { + { NM_STATE_LOCKED, "Locked" }, + { NM_STATE_UNLOCKED, "Unlocked" }, + { NM_STATE_SHUTDOWN, "Shutdown" }, + { NM_STATE_NULL, "NULL" }, + { 0, NULL } +}; + +const char *nm_adm_name(u_int8_t adm) +{ + return get_value_string(abis_nm_adm_state_names, adm); +} + +int nm_is_running(struct gsm_nm_state *s) { + return (s->operational == NM_OPSTATE_ENABLED) && ( + (s->availability == NM_AVSTATE_OK) || + (s->availability == 0xff) + ); +} + +static void debugp_foh(struct abis_om_fom_hdr *foh) +{ + DEBUGP(DNM, "OC=%s(%02x) INST=(%02x,%02x,%02x) ", + obj_class_name(foh->obj_class), foh->obj_class, + foh->obj_inst.bts_nr, foh->obj_inst.trx_nr, + foh->obj_inst.ts_nr); +} + +/* obtain the gsm_nm_state data structure for a given object instance */ +static struct gsm_nm_state * +objclass2nmstate(struct gsm_bts *bts, u_int8_t obj_class, + struct abis_om_obj_inst *obj_inst) +{ + struct gsm_bts_trx *trx; + struct gsm_nm_state *nm_state = NULL; + + switch (obj_class) { + case NM_OC_BTS: + nm_state = &bts->nm_state; + break; + case NM_OC_RADIO_CARRIER: + if (obj_inst->trx_nr >= bts->num_trx) { + DEBUGPC(DNM, "TRX %u does not exist ", obj_inst->trx_nr); + return NULL; + } + trx = gsm_bts_trx_num(bts, obj_inst->trx_nr); + nm_state = &trx->nm_state; + break; + case NM_OC_BASEB_TRANSC: + if (obj_inst->trx_nr >= bts->num_trx) { + DEBUGPC(DNM, "TRX %u does not exist ", obj_inst->trx_nr); + return NULL; + } + trx = gsm_bts_trx_num(bts, obj_inst->trx_nr); + nm_state = &trx->bb_transc.nm_state; + break; + case NM_OC_CHANNEL: + if (obj_inst->trx_nr >= bts->num_trx) { + DEBUGPC(DNM, "TRX %u does not exist ", obj_inst->trx_nr); + return NULL; + } + trx = gsm_bts_trx_num(bts, obj_inst->trx_nr); + if (obj_inst->ts_nr >= TRX_NR_TS) + return NULL; + nm_state = &trx->ts[obj_inst->ts_nr].nm_state; + break; + case NM_OC_SITE_MANAGER: + nm_state = &bts->site_mgr.nm_state; + break; + case NM_OC_BS11: + switch (obj_inst->bts_nr) { + case BS11_OBJ_CCLK: + nm_state = &bts->bs11.cclk.nm_state; + 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); + nm_state = &trx->bs11.bbsig.nm_state; + 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); + nm_state = &trx->bs11.pa.nm_state; + break; + default: + return NULL; + } + case NM_OC_BS11_RACK: + nm_state = &bts->bs11.rack.nm_state; + break; + case NM_OC_BS11_ENVABTSE: + if (obj_inst->trx_nr >= ARRAY_SIZE(bts->bs11.envabtse)) + return NULL; + nm_state = &bts->bs11.envabtse[obj_inst->trx_nr].nm_state; + break; + case NM_OC_GPRS_NSE: + nm_state = &bts->gprs.nse.nm_state; + break; + case NM_OC_GPRS_CELL: + nm_state = &bts->gprs.cell.nm_state; + break; + case NM_OC_GPRS_NSVC: + if (obj_inst->trx_nr >= ARRAY_SIZE(bts->gprs.nsvc)) + return NULL; + nm_state = &bts->gprs.nsvc[obj_inst->trx_nr].nm_state; + break; + } + return nm_state; +} + +/* obtain the in-memory data structure of a given object instance */ +static void * +objclass2obj(struct gsm_bts *bts, u_int8_t obj_class, + 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) { + DEBUGPC(DNM, "TRX %u does not exist ", obj_inst->trx_nr); + 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) { + DEBUGPC(DNM, "TRX %u does not exist ", obj_inst->trx_nr); + 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) { + DEBUGPC(DNM, "TRX %u does not exist ", obj_inst->trx_nr); + 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; +} + +/* 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, u_int8_t obj_class, + struct abis_om_obj_inst *obj_inst, u_int8_t adm_state) +{ + struct gsm_nm_state *nm_state, new_state; + struct nm_statechg_signal_data nsd; + + nsd.obj = objclass2obj(bts, obj_class, obj_inst); + if (!nsd.obj) + return -EINVAL; + nm_state = objclass2nmstate(bts, obj_class, obj_inst); + if (!nm_state) + return -1; + + new_state = *nm_state; + new_state.administrative = adm_state; + + nsd.obj_class = obj_class; + nsd.old_state = nm_state; + nsd.new_state = &new_state; + nsd.obj_inst = obj_inst; + dispatch_signal(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 gsm_bts *bts = mb->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 = 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 ", 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) ", 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 ", nm_adm_name(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 = 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; + dispatch_signal(SS_NM, S_NM_STATECHG_OPER, &nsd); + nm_state->operational = new_state.operational; + nm_state->availability = new_state.availability; + if (nm_state->administrative == 0) + nm_state->administrative = new_state.administrative; + } +#if 0 + if (op_state == 1) { + /* try to enable objects that are disabled */ + abis_nm_opstart(bts, foh->obj_class, + foh->obj_inst.bts_nr, + foh->obj_inst.trx_nr, + foh->obj_inst.ts_nr); + } +#endif + return 0; +} + +static int rx_fail_evt_rep(struct msgb *mb) +{ + struct abis_om_hdr *oh = msgb_l2(mb); + struct abis_om_fom_hdr *foh = msgb_l3(mb); + struct tlv_parsed tp; + const uint8_t *p_val; + char *p_text; + + DEBUGPC(DNM, "Failure Event Report "); + + abis_nm_tlv_parse(&tp, mb->trx->bts, foh->data, oh->length-sizeof(*foh)); + + if (TLVP_PRESENT(&tp, NM_ATT_EVENT_TYPE)) + DEBUGPC(DNM, "Type=%s ", event_type_name(*TLVP_VAL(&tp, NM_ATT_EVENT_TYPE))); + if (TLVP_PRESENT(&tp, NM_ATT_SEVERITY)) + DEBUGPC(DNM, "Severity=%s ", 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); + DEBUGPC(DNM, "Probable cause= %02X %02X %02X ", p_val[0], p_val[1], p_val[2]); + } + if (TLVP_PRESENT(&tp, NM_ATT_ADD_TEXT)) { + p_val = TLVP_VAL(&tp, NM_ATT_ADD_TEXT); + p_text = talloc_strndup(tall_bsc_ctx, (const char *) p_val, TLVP_LEN(&tp, NM_ATT_ADD_TEXT)); + if (p_text) { + DEBUGPC(DNM, "Additional Text=%s ", p_text); + talloc_free(p_text); + } + } + + DEBUGPC(DNM, "\n"); + + return 0; +} + +static int abis_nm_rcvmsg_report(struct msgb *mb) +{ + struct abis_om_fom_hdr *foh = msgb_l3(mb); + u_int8_t mt = foh->msg_type; + + debugp_foh(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"); + dispatch_signal(SS_NM, S_NM_SW_ACTIV_REP, mb); + break; + case NM_MT_FAILURE_EVENT_REP: + rx_fail_evt_rep(mb); + dispatch_signal(SS_NM, S_NM_FAIL_REP, mb); + break; + case NM_MT_TEST_REP: + DEBUGPC(DNM, "Test Report\n"); + dispatch_signal(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, u_int8_t obj_class, u_int8_t i0, u_int8_t i1, + u_int8_t i2, const u_int8_t *sw_desc, u_int8_t swdesc_len) +{ + struct abis_om_hdr *oh; + struct msgb *msg = nm_msgb_alloc(); + u_int8_t len = swdesc_len; + u_int8_t *trailer; + + oh = (struct abis_om_hdr *) msgb_put(msg, ABIS_OM_FOM_HDR_SIZE); + fill_om_fom_hdr(oh, len, NM_MT_ACTIVATE_SW, obj_class, i0, i1, i2); + + trailer = msgb_put(msg, swdesc_len); + memcpy(trailer, sw_desc, swdesc_len); + + return abis_nm_sendmsg(bts, msg); +} + +static int abis_nm_parse_sw_descr(const u_int8_t *sw_descr, int sw_descr_len) +{ + static const struct tlv_definition sw_descr_def = { + .def = { + [NM_ATT_FILE_ID] = { TLV_TYPE_TL16V, }, + [NM_ATT_FILE_VERSION] = { TLV_TYPE_TL16V, }, + }, + }; + + u_int8_t tag; + u_int16_t tag_len; + const u_int8_t *val; + int ofs = 0, len; + + /* Classic TLV parsing doesn't work well with SW_DESCR because of it's + * nested nature and the fact you have to assume it contains only two sub + * tags NM_ATT_FILE_VERSION & NM_ATT_FILE_ID to parse it */ + + if (sw_descr[0] != NM_ATT_SW_DESCR) { + DEBUGP(DNM, "SW_DESCR attribute identifier not found!\n"); + return -1; + } + ofs += 1; + + len = tlv_parse_one(&tag, &tag_len, &val, + &sw_descr_def, &sw_descr[ofs], sw_descr_len-ofs); + if (len < 0 || (tag != NM_ATT_FILE_ID)) { + DEBUGP(DNM, "FILE_ID attribute identifier not found!\n"); + return -2; + } + ofs += len; + + len = tlv_parse_one(&tag, &tag_len, &val, + &sw_descr_def, &sw_descr[ofs], sw_descr_len-ofs); + if (len < 0 || (tag != NM_ATT_FILE_VERSION)) { + DEBUGP(DNM, "FILE_VERSION attribute identifier not found!\n"); + return -3; + } + ofs += len; + + return ofs; +} + +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 tlv_parsed tp; + const u_int8_t *sw_config; + int ret, sw_config_len, sw_descr_len; + + debugp_foh(foh); + + DEBUGPC(DNM, "SW Activate Request: "); + + DEBUGP(DNM, "Software Activate Request, ACKing and Activating\n"); + + ret = abis_nm_sw_act_req_ack(mb->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)); + + abis_nm_tlv_parse(&tp, mb->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)) { + DEBUGP(DNM, "SW config not found! Can't continue.\n"); + return -EINVAL; + } else { + DEBUGP(DNM, "Found SW config: %s\n", hexdump(sw_config, sw_config_len)); + } + + /* Use the first SW_DESCR present in SW config */ + sw_descr_len = abis_nm_parse_sw_descr(sw_config, sw_config_len); + if (sw_descr_len < 0) + return -EINVAL; + + return ipacc_sw_activate(mb->trx->bts, foh->obj_class, + foh->obj_inst.bts_nr, + foh->obj_inst.trx_nr, + foh->obj_inst.ts_nr, + sw_config, sw_descr_len); +} + +/* Receive a CHANGE_ADM_STATE_ACK, parse the TLV and update local state */ +static int abis_nm_rx_chg_adm_state_ack(struct msgb *mb) +{ + struct abis_om_hdr *oh = msgb_l2(mb); + struct abis_om_fom_hdr *foh = msgb_l3(mb); + struct tlv_parsed tp; + u_int8_t adm_state; + + abis_nm_tlv_parse(&tp, mb->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(mb->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 tlv_parsed tp; + + DEBUGP(DNM, "LMT Event "); + abis_nm_tlv_parse(&tp, mb->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) { + u_int8_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) { + u_int8_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; +} + +static 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, 0); + + 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); + u_int8_t mt = foh->msg_type; + int ret = 0; + + /* check for unsolicited message */ + if (is_report(mt)) + return abis_nm_rcvmsg_report(mb); + + if (is_in_arr(mt, sw_load_msgs, ARRAY_SIZE(sw_load_msgs))) + return abis_nm_rcvmsg_sw(mb); + + if (is_in_arr(mt, nacks, ARRAY_SIZE(nacks))) { + struct nm_nack_signal_data nack_data; + struct tlv_parsed tp; + + debugp_foh(foh); + + DEBUGPC(DNM, "%s NACK ", get_value_string(nack_names, mt)); + + abis_nm_tlv_parse(&tp, mb->trx->bts, foh->data, oh->length-sizeof(*foh)); + if (TLVP_PRESENT(&tp, NM_ATT_NACK_CAUSES)) + DEBUGPC(DNM, "CAUSE=%s\n", + nack_cause_name(*TLVP_VAL(&tp, NM_ATT_NACK_CAUSES))); + else + DEBUGPC(DNM, "\n"); + + nack_data.msg = mb; + nack_data.mt = mt; + dispatch_signal(SS_NM, S_NM_NACK, &nack_data); + abis_nm_queue_send_next(mb->trx->bts); + return 0; + } +#if 0 + /* check if last message is to be acked */ + if (is_ack_nack(nmh->last_msgtype)) { + if (mt == MT_ACK(nmh->last_msgtype)) { + DEBUGP(DNM, "received ACK (0x%x)\n", foh->msg_type); + /* we got our ACK, continue sending the next msg */ + } else if (mt == MT_NACK(nmh->last_msgtype)) { + /* we got a NACK, signal this to the caller */ + DEBUGP(DNM, "received NACK (0x%x)\n", foh->msg_type); + /* FIXME: somehow signal this to the caller */ + } else { + /* really strange things happen */ + return -EINVAL; + } + } +#endif + + switch (mt) { + case NM_MT_CHG_ADM_STATE_ACK: + ret = abis_nm_rx_chg_adm_state_ack(mb); + break; + case NM_MT_SW_ACT_REQ: + ret = abis_nm_rx_sw_act_req(mb); + break; + case NM_MT_BS11_LMT_SESSION: + ret = abis_nm_rx_lmt_event(mb); + break; + case NM_MT_CONN_MDROP_LINK_ACK: + DEBUGP(DNM, "CONN MDROP LINK ACK\n"); + break; + case NM_MT_IPACC_RESTART_ACK: + dispatch_signal(SS_NM, S_NM_IPACC_RESTART_ACK, NULL); + break; + case NM_MT_IPACC_RESTART_NACK: + dispatch_signal(SS_NM, S_NM_IPACC_RESTART_NACK, NULL); + break; + } + + abis_nm_queue_send_next(mb->trx->bts); + return ret; +} + +static int abis_nm_rx_ipacc(struct msgb *mb); + +static int abis_nm_rcvmsg_manuf(struct msgb *mb) +{ + int rc; + int bts_type = mb->trx->bts->type; + + switch (bts_type) { + case GSM_BTS_TYPE_NANOBTS: + rc = abis_nm_rx_ipacc(mb); + abis_nm_queue_send_next(mb->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) + return -EINVAL; + } + if (oh->sequence != 0) { + LOGP(DNM, LOGL_ERROR, "ABIS OML sequence 0x%x != 0x00\n", + oh->sequence); + return -EINVAL; + } +#if 0 + unsigned int l2_len = msg->tail - (u_int8_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); + return -EINVAL; + } + + 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 */ + u_int8_t obj_class; + u_int8_t obj_instance[3]; + + u_int8_t file_id[255]; + u_int8_t file_id_len; + + u_int8_t file_version[255]; + u_int8_t file_version_len; + + u_int8_t window_size; + u_int8_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(); + u_int8_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); + if (!fgets(next_seg_buf, sizeof(next_seg_buf)-2, stream)) { + fseek(stream, pos, SEEK_SET); + return 1; + } + + fseek(stream, pos, SEEK_SET); + return 0; +} + +/* 6.2.2 / 8.3.2 Load Data Segment */ +static int sw_load_segment(struct abis_nm_sw *sw) +{ + struct abis_om_hdr *oh; + struct msgb *msg = nm_msgb_alloc(); + char seg_buf[256]; + char *line_buf = seg_buf+2; + unsigned char *tlv; + u_int8_t 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, (u_int8_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: { + 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 u_int8_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(); + u_int8_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(); + u_int8_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); + 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(mb->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(mb->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(mb->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(mb->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(mb->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(mb->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(mb->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, + u_int8_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, u_int8_t bts_port, + u_int8_t ts_nr, u_int8_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, u_int8_t trx_nr, + u_int8_t e1_port, u_int8_t e1_timeslot, u_int8_t e1_subslot, + u_int8_t tei) +{ + struct abis_om_hdr *oh; + struct abis_nm_channel *ch; + u_int8_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, + u_int8_t e1_port, u_int8_t e1_timeslot, u_int8_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, + u_int8_t e1_port, u_int8_t e1_timeslot, + u_int8_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, + u_int8_t subchan) +{ +} +#endif + +/* Chapter 8.6.1 */ +int abis_nm_set_bts_attr(struct gsm_bts *bts, u_int8_t *attr, int attr_len) +{ + struct abis_om_hdr *oh; + struct msgb *msg = nm_msgb_alloc(); + u_int8_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, u_int8_t *attr, int attr_len) +{ + struct abis_om_hdr *oh; + struct msgb *msg = nm_msgb_alloc(); + u_int8_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); +} + +static int verify_chan_comb(struct gsm_bts_trx_ts *ts, u_int8_t chan_comb) +{ + int i; + + /* As it turns out, the BS-11 has some very peculiar restrictions + * on the channel combinations it allows */ + switch (ts->trx->bts->type) { + case GSM_BTS_TYPE_BS11: + switch (chan_comb) { + case NM_CHANC_TCHHalf: + case NM_CHANC_TCHHalf2: + /* not supported */ + 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) + return -EINVAL; + } + /* not allowed for TS0 of BCCH-TRX */ + if (ts->trx == ts->trx->bts->c0 && + ts->nr == 0) + 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)) + 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) + return -EINVAL; + break; + case NM_CHANC_BCCH: + /* allowed only for TS 2/4/6 of C0 */ + if (ts->trx != ts->trx->bts->c0) + return -EINVAL; + if (ts->nr != 2 && ts->nr != 4 && + ts->nr != 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: + return -EINVAL; + } + } else { + switch (chan_comb) { + case NM_CHANC_TCHFull: + case NM_CHANC_TCHHalf: + case NM_CHANC_IPAC_TCHFull_TCHHalf: + return 0; + default: + 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; + return -EINVAL; + case NM_CHANC_SDCCH: + case NM_CHANC_TCHFull: + case NM_CHANC_TCHHalf: + case NM_CHANC_IPAC_TCHFull_TCHHalf: + case NM_CHANC_IPAC_TCHFull_PDCH: + return 0; + } + } 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: + return -EINVAL; + } + } + break; + case 2: + case 3: + case 4: + case 5: + case 6: + case 7: + switch (chan_comb) { + case NM_CHANC_TCHFull: + case NM_CHANC_TCHHalf: + case NM_CHANC_IPAC_TCHFull_TCHHalf: + return 0; + case NM_CHANC_IPAC_PDCH: + case NM_CHANC_IPAC_TCHFull_PDCH: + if (ts->trx->nr == 0) + return 0; + else + return -EINVAL; + } + break; + } + return -EINVAL; + default: + /* unknown BTS type */ + return 0; + } + return 0; +} + +/* Chapter 8.6.3 */ +int abis_nm_set_channel_attr(struct gsm_bts_trx_ts *ts, u_int8_t chan_comb) +{ + struct gsm_bts *bts = ts->trx->bts; + struct abis_om_hdr *oh; + u_int8_t zero = 0x00; + struct msgb *msg = nm_msgb_alloc(); + u_int8_t len = 2 + 2; + + 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) < 0) { + msgb_free(msg); + DEBUGP(DNM, "Invalid Channel Combination!!!\n"); + 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, bts->tsc); /* 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, u_int8_t obj_class, u_int8_t i1, + u_int8_t i2, u_int8_t i3, int nack, u_int8_t *attr, int att_len) +{ + struct abis_om_hdr *oh; + struct msgb *msg = nm_msgb_alloc(); + u_int8_t msgtype = NM_MT_SW_ACT_REQ_ACK; + u_int8_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) { + u_int8_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, u_int8_t *rawmsg) +{ + struct msgb *msg = nm_msgb_alloc(); + struct abis_om_hdr *oh; + u_int8_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, u_int8_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, u_int8_t obj_class, u_int8_t i0, u_int8_t i1, u_int8_t i2) +{ + 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_OPSTART, obj_class, i0, i1, i2); + + debugp_foh((struct abis_om_fom_hdr *) oh->data); + 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, u_int8_t obj_class, u_int8_t i0, + u_int8_t i1, u_int8_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, u_int8_t e1_port0, u_int8_t ts0, + u_int8_t e1_port1, u_int8_t ts1) +{ + struct abis_om_hdr *oh; + struct msgb *msg = nm_msgb_alloc(); + u_int8_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, u_int8_t obj_class, + u_int8_t bts_nr, u_int8_t trx_nr, u_int8_t ts_nr, + u_int8_t test_nr, u_int8_t auton_report, struct msgb *msg) +{ + struct abis_om_hdr *oh; + + DEBUGP(DNM, "PEFORM TEST %s\n", get_value_string(test_names, 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 { + u_int16_t year; + u_int8_t month; + u_int8_t day; + u_int8_t hour; + u_int8_t min; + u_int8_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, u_int8_t idx, + u_int8_t attr_len, const u_int8_t *attr) +{ + struct abis_om_hdr *oh; + struct msgb *msg = nm_msgb_alloc(); + u_int8_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, u_int8_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, u_int8_t idx) +{ + struct abis_om_hdr *oh; + struct msgb *msg = nm_msgb_alloc(); + u_int8_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, u_int8_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, u_int8_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 u_int8_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, u_int8_t e1_port, + u_int8_t e1_timeslot, u_int8_t e1_subslot, + u_int8_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, u_int8_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(); + u_int8_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(); + u_int8_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(); + u_int8_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 u_int8_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, u_int8_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) { + u_int8_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), (u_int8_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), (u_int8_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 u_int8_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; + u_int8_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; + u_int8_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]; + u_int8_t win_size; + int forced; + struct llist_head file_list; + gsm_cbfn *user_cb; /* specified by the user */ +}; +static struct abis_nm_bs11_sw _g_bs11_sw, *g_bs11_sw = &_g_bs11_sw; + +struct file_list_entry { + struct llist_head list; + char fname[PATH_MAX]; +}; + +struct file_list_entry *fl_dequeue(struct llist_head *queue) +{ + struct llist_head *lh; + + if (llist_empty(queue)) + return NULL; + + lh = queue->next; + llist_del(lh); + + return llist_entry(lh, struct file_list_entry, list); +} + +static int bs11_read_swl_file(struct abis_nm_bs11_sw *bs11_sw) +{ + char linebuf[255]; + struct llist_head *lh, *lh2; + FILE *swl; + int rc = 0; + + swl = fopen(bs11_sw->swl_fname, "r"); + if (!swl) + return -ENODEV; + + /* zero the stale file list, if any */ + llist_for_each_safe(lh, lh2, &bs11_sw->file_list) { + llist_del(lh); + talloc_free(lh); + } + + while (fgets(linebuf, sizeof(linebuf), swl)) { + char file_id[12+1]; + char file_version[80+1]; + struct file_list_entry *fle; + static char dir[PATH_MAX]; + + if (strlen(linebuf) < 4) + continue; + + rc = sscanf(linebuf+4, "%12s:%80s\r\n", file_id, file_version); + if (rc < 0) { + perror("ERR parsing SWL file"); + rc = -EINVAL; + goto out; + } + if (rc < 2) + continue; + + fle = talloc_zero(tall_fle_ctx, struct file_list_entry); + if (!fle) { + rc = -ENOMEM; + goto out; + } + + /* construct new filename */ + strncpy(dir, bs11_sw->swl_fname, sizeof(dir)); + strncat(fle->fname, dirname(dir), sizeof(fle->fname) - 1); + strcat(fle->fname, "/"); + strncat(fle->fname, file_id, sizeof(fle->fname) - 1 -strlen(fle->fname)); + + llist_add_tail(&fle->list, &bs11_sw->file_list); + } + +out: + fclose(swl); + return rc; +} + +/* bs11 swload specific callback, passed to abis_nm core swload */ +static int bs11_swload_cbfn(unsigned int hook, unsigned int event, + struct msgb *msg, void *data, void *param) +{ + struct abis_nm_bs11_sw *bs11_sw = data; + struct file_list_entry *fle; + int rc = 0; + + switch (event) { + case NM_MT_LOAD_END_ACK: + fle = fl_dequeue(&bs11_sw->file_list); + if (fle) { + /* start download the next file of our file list */ + rc = abis_nm_software_load(bs11_sw->bts, 0xff, fle->fname, + bs11_sw->win_size, + bs11_sw->forced, + &bs11_swload_cbfn, bs11_sw); + talloc_free(fle); + } else { + /* activate the SWL */ + rc = abis_nm_software_activate(bs11_sw->bts, + bs11_sw->swl_fname, + bs11_swload_cbfn, + bs11_sw); + } + break; + case NM_MT_LOAD_SEG_ACK: + case NM_MT_LOAD_END_NACK: + case NM_MT_LOAD_INIT_ACK: + case NM_MT_LOAD_INIT_NACK: + case NM_MT_ACTIVATE_SW_NACK: + case NM_MT_ACTIVATE_SW_ACK: + default: + /* fallthrough to the user callback */ + if (bs11_sw->user_cb) + rc = bs11_sw->user_cb(hook, event, msg, NULL, NULL); + break; + } + + return rc; +} + +/* Siemens provides a SWL file that is a mere listing of all the other + * files that are part of a software release. We need to upload first + * the list file, and then each file that is listed in the list file */ +int abis_nm_bs11_load_swl(struct gsm_bts *bts, const char *fname, + u_int8_t win_size, int forced, gsm_cbfn *cbfn) +{ + struct abis_nm_bs11_sw *bs11_sw = g_bs11_sw; + struct file_list_entry *fle; + int rc = 0; + + INIT_LLIST_HEAD(&bs11_sw->file_list); + bs11_sw->bts = bts; + bs11_sw->win_size = win_size; + bs11_sw->user_cb = cbfn; + bs11_sw->forced = forced; + + strncpy(bs11_sw->swl_fname, fname, sizeof(bs11_sw->swl_fname)); + rc = bs11_read_swl_file(bs11_sw); + if (rc < 0) + return rc; + + /* dequeue next item in file list */ + fle = fl_dequeue(&bs11_sw->file_list); + if (!fle) + return -EINVAL; + + /* start download the next file of our file list */ + rc = abis_nm_software_load(bts, 0xff, fle->fname, win_size, forced, + bs11_swload_cbfn, bs11_sw); + talloc_free(fle); + return rc; +} + +#if 0 +static u_int8_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 u_int8_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 u_int8_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), (u_int8_t *) &aet); + + return abis_nm_sendmsg(bts, msg); +} + +int abis_nm_bs11_get_bport_line_cfg(struct gsm_bts *bts, u_int8_t bport) +{ + struct abis_om_hdr *oh; + struct msgb *msg = nm_msgb_alloc(); + u_int8_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, u_int8_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; + u_int8_t idstrlen = oh->data[0]; + struct tlv_parsed tp; + struct ipacc_ack_signal_data signal; + + 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, msg->trx->bts, foh->data, oh->length-sizeof(*foh)); + + debugp_foh(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(*((u_int16_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)) + DEBUGPC(DNM, " CAUSE=%s\n", + nack_cause_name(*TLVP_VAL(&tp, NM_ATT_NACK_CAUSES))); + else + DEBUGPC(DNM, "\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", + 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", + 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", + 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(msg->trx->bts, foh->obj_inst.trx_nr); + signal.msg_type = foh->msg_type; + dispatch_signal(SS_NM, S_NM_IPACC_NACK, &signal); + break; + case NM_MT_IPACC_SET_NVATTR_ACK: + signal.trx = gsm_bts_trx_by_nr(msg->trx->bts, foh->obj_inst.trx_nr); + signal.msg_type = foh->msg_type; + dispatch_signal(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, u_int8_t msg_type, + u_int8_t obj_class, u_int8_t bts_nr, + u_int8_t trx_nr, u_int8_t ts_nr, + u_int8_t *attr, int attr_len) +{ + struct msgb *msg = nm_msgb_alloc(); + struct abis_om_hdr *oh; + struct abis_om_fom_hdr *foh; + u_int8_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, u_int8_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, + u_int32_t ip, u_int16_t port, u_int8_t stream) +{ + struct in_addr ia; + u_int8_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; + *(u_int32_t *)(attr+6) = ia.s_addr; + + /* if ip == 0, we use the default IP */ + if (ip == 0) + attr_len -= 5; + + DEBUGP(DNM, "ip.access RSL CONNECT IP=%s PORT=%u STREAM=0x%02x\n", + inet_ntoa(ia), port, stream); + + return abis_nm_ipaccess_msg(trx->bts, NM_MT_IPACC_RSL_CONNECT, + NM_OC_BASEB_TRANSC, trx->bts->bts_nr, + trx->nr, 0xff, attr, attr_len); +} + +/* restart / reboot an ip.access nanoBTS */ +int abis_nm_ipaccess_restart(struct gsm_bts_trx *trx) +{ + struct abis_om_hdr *oh; + struct msgb *msg = nm_msgb_alloc(); + + oh = (struct abis_om_hdr *) msgb_put(msg, ABIS_OM_FOM_HDR_SIZE); + fill_om_fom_hdr(oh, 0, NM_MT_IPACC_RESTART, NM_OC_BASEB_TRANSC, + trx->bts->nr, trx->nr, 0xff); + + return abis_nm_sendmsg(trx->bts, msg); +} + +int abis_nm_ipaccess_set_attr(struct gsm_bts *bts, u_int8_t obj_class, + u_int8_t bts_nr, u_int8_t trx_nr, u_int8_t ts_nr, + u_int8_t *attr, u_int8_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(u_int8_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); + *((u_int16_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->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(u_int8_t res) +{ + return get_value_string(ipacc_testres_names, res); +} + +void ipac_parse_cgi(struct cell_global_id *cid, const u_int8_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(*((u_int16_t *)&buf[3])); + cid->ci = ntohs(*((u_int16_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, u_int8_t *buf) +{ + u_int8_t *cur = buf; + u_int16_t len; + + memset(binf, 0, sizeof(*binf)); + + if (cur[0] != NM_IPAC_EIE_BCCH_INFO) + return -EINVAL; + cur++; + + len = ntohs(*(u_int16_t *)cur); + cur += 2; + + binf->info_type = ntohs(*(u_int16_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(*(u_int16_t *)cur); + cur += 2; + + if (binf->info_type & IPAC_BINF_FRAME_OFFSET) + binf->frame_offset = ntohs(*(u_int16_t *)cur); + cur += 2; + + if (binf->info_type & IPAC_BINF_FRAME_NR_OFFSET) + binf->frame_nr_offset = ntohl(*(u_int32_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 new file mode 100644 index 000000000..754efe28d --- /dev/null +++ b/openbsc/src/libbsc/abis_nm_ipaccess.c @@ -0,0 +1,87 @@ +/* GSM Network Management (OML) messages on the A-bis interface + * Extensions for the ip.access A-bis over IP protocol*/ + +/* (C) 2008-2009 by Harald Welte + * + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + * + */ + +/* A list of all the 'embedded' attributes of ip.access */ +enum ipa_embedded_att { + IPA_ATT_ARFCN_WHITELIST = 0x01, + IPA_ATT_ARFCN_BLACKLIST = 0x02, + IPA_ATT_FREQ_ERR_LIST = 0x03, + IPA_ATT_CHAN_USAGE_LIST = 0x04, + IPA_ATT_BCCH_INF_TYPE = 0x05, + IPA_ATT_BCCH_INF = 0x06, + IPA_ATT_CONFIG = 0x07, + IPA_ATT_RESULT_DETAILS = 0x08, + IPA_ATT_RXLEV_THRESH = 0x09, + IPA_ATT_FREQ_SYNC_OPT = 0x0a, + IPA_ATT_MAC_ADDR = 0x0b, + IPA_ATT_HW_SW_COMPAT_NR = 0x0c, + IPA_ATT_MANUF_SER_NR = 0x0d, + IPA_ATT_OEM_ID = 0x0e, + IPA_ATT_DATETIME_MANUF = 0x0f, + IPA_ATT_DATETIME_CALIB = 0x10, + IPA_ATT_BEACON_INF = 0x11, + IPA_ATT_FREQ_ERR = 0x12, + IPA_ATT_SNMP_COMM_STRING = 0x13, + IPA_ATT_SNMP_TRAP_ADDR = 0x14, + IPA_ATT_SNMP_TRAP_PORT = 0x15, + IPA_ATT_SNMP_MAN_ADDR = 0x16, + IPA_ATT_SNMP_SYS_CONTACT = 0x17, + IPA_ATT_FACTORY_ID = 0x18, + IPA_ATT_FACTORY_SERIAL = 0x19, + IPA_ATT_LOGGED_EVT_IND = 0x1a, + IPA_ATT_LOCAL_ADD_TEXT = 0x1b, + IPA_ATT_FREQ_BANDS = 0x1c, + IPA_ATT_MAX_TA = 0x1d, + IPA_ATT_CIPH_ALG = 0x1e, + IPA_ATT_CHAN_TYPES = 0x1f, + IPA_ATT_CHAN_MODES = 0x20, + IPA_ATT_GPRS_CODING_SCHEMES = 0x21, + IPA_ATT_RTP_FEATURES = 0x22, + IPA_ATT_RSL_FEATURES = 0x23, + IPA_ATT_BTS_HW_CLASS = 0x24, + IPA_ATT_BTS_ID = 0x25, + IPA_ATT_BCAST_L2_MSG = 0x26, +}; + +/* append an ip.access channel list to the given msgb */ +static int ipa_chan_list_append(struct msgb *msg, u_int8_t ie, + u_int16_t *arfcns, int arfcn_count) +{ + int i; + u_int8_t *u8; + u_int16_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 new file mode 100644 index 000000000..996a85749 --- /dev/null +++ b/openbsc/src/libbsc/abis_nm_vty.c @@ -0,0 +1,197 @@ +/* VTY interface for A-bis OML (Netowrk Management) */ + +/* (C) 2009-2010 by Harald Welte + * + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + * + */ + +#include +#include +#include +#include + +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +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 "FIXME" + +DEFUN(oml_class_inst, oml_class_inst_cmd, + "bts <0-255> oml class " NM_OBJCLASS_VTY + " instance <0-255> <0-255> <0-255>", + "BTS related commands\n" "BTS Number\n" + "Manipulate the OML managed objects\n" + "Object Class\n" NM_OBJCLASS_VTY_HELP + "Object Instance\n" "BTS Number\n" "TRX Number\n" "TS Number\n") +{ + struct gsm_bts *bts; + struct oml_node_state *oms; + int bts_nr = atoi(argv[0]); + + bts = gsm_bts_num(bsc_gsmnet, bts_nr); + if (!bts) { + vty_out(vty, "%% No such BTS (%d)%s", bts_nr, VTY_NEWLINE); + return CMD_WARNING; + } + + oms = talloc_zero(tall_bsc_ctx, struct oml_node_state); + if (!oms) + return CMD_WARNING; + + oms->bts = bts; + oms->obj_class = get_string_value(abis_nm_obj_class_names, argv[1]); + oms->obj_inst[0] = atoi(argv[2]); + oms->obj_inst[1] = atoi(argv[3]); + oms->obj_inst[2] = atoi(argv[4]); + + vty->index = oms; + vty->node = OML_NODE; + + return CMD_SUCCESS; + +} + +DEFUN(oml_classnum_inst, oml_classnum_inst_cmd, + "bts <0-255> oml class <0-255> instance <0-255> <0-255> <0-255>", + "BTS related commands\n" "BTS Number\n" + "Manipulate the OML managed objects\n" + "Object Class\n" "Object Class\n" + "Object Instance\n" "BTS Number\n" "TRX Number\n" "TS Number\n") +{ + struct gsm_bts *bts; + struct oml_node_state *oms; + int bts_nr = atoi(argv[0]); + + bts = gsm_bts_num(bsc_gsmnet, bts_nr); + if (!bts) { + vty_out(vty, "%% No such BTS (%d)%s", bts_nr, VTY_NEWLINE); + return CMD_WARNING; + } + + oms = talloc_zero(tall_bsc_ctx, struct oml_node_state); + if (!oms) + return CMD_WARNING; + + oms->bts = bts; + oms->obj_class = atoi(argv[1]); + oms->obj_inst[0] = atoi(argv[2]); + oms->obj_inst[1] = atoi(argv[3]); + oms->obj_inst[2] = atoi(argv[4]); + + vty->index = oms; + vty->node = OML_NODE; + + return CMD_SUCCESS; +} + +DEFUN(oml_attrib_get, oml_attrib_get_cmd, + "attribute get <0-255>", + "OML Attribute Actions\n" "Get a single OML Attribute\n" + "OML Attribute Number\n") +{ + struct oml_node_state *oms = vty->index; + + /* FIXME */ + return CMD_SUCCESS; +} + +DEFUN(oml_attrib_set, oml_attrib_set_cmd, + "attribute set <0-255> .HEX", + "OML Attribute Actions\n" "Set a single OML Attribute\n" + "OML Attribute Number\n") +{ + struct oml_node_state *oms = vty->index; + + /* FIXME */ + 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); + + install_default(OML_NODE); + install_element(OML_NODE, &ournode_exit_cmd); + install_element(OML_NODE, &oml_attrib_get_cmd); + install_element(OML_NODE, &oml_attrib_set_cmd); + 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 new file mode 100644 index 000000000..e7a5f8386 --- /dev/null +++ b/openbsc/src/libbsc/abis_om2000.c @@ -0,0 +1,878 @@ +/* Ericsson RBS 2xxx GSM O&M (OM2000) messages on the A-bis interface + * implemented based on protocol trace analysis, no formal documentation */ + +/* (C) 2010-2011 by Harald Welte + * + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + * + */ + + +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#define OM_ALLOC_SIZE 1024 +#define OM_HEADROOM_SIZE 128 + +/* use following functions from abis_nm.c: + * om2k_msgb_alloc() + * abis_om2k_sendmsg() + */ + +struct abis_om2k_hdr { + struct abis_om_hdr om; + uint16_t msg_type; + struct abis_om2k_mo mo; + uint8_t data[0]; +} __attribute__ ((packed)); + +enum abis_om2k_msgtype { + OM2K_MSGT_ABORT_SP_CMD = 0x0000, + OM2K_MSGT_ABORT_SP_COMPL = 0x0002, + OM2K_MSGT_ALARM_REP_ACK = 0x0004, + OM2K_MSGT_ALARM_REP_NACK = 0x0005, + OM2K_MSGT_ALARM_REP = 0x0006, + OM2K_MSGT_ALARM_STATUS_REQ = 0x0008, + OM2K_MSGT_ALARM_STATUS_REQ_ACK = 0x000a, + OM2K_MSGT_ALARM_STATUS_REQ_REJ = 0x000b, + OM2K_MSGT_ALARM_STATUS_RES_ACK = 0x000c, + OM2K_MSGT_ALARM_STATUS_RES_NACK = 0x000d, + OM2K_MSGT_ALARM_STATUS_RES = 0x000e, + OM2K_MSGT_CAL_TIME_RESP = 0x0010, + OM2K_MSGT_CAL_TIME_REJ = 0x0011, + OM2K_MSGT_CAL_TIME_REQ = 0x0012, + + OM2K_MSGT_CON_CONF_REQ = 0x0014, + OM2K_MSGT_CON_CONF_REQ_ACK = 0x0016, + OM2K_MSGT_CON_CONF_REQ_REJ = 0x0017, + OM2K_MSGT_CON_CONF_RES_ACK = 0x0018, + OM2K_MSGT_CON_CONF_RES_NACK = 0x0019, + OM2K_MSGT_CON_CONF_RES = 0x001a, + + OM2K_MSGT_CONNECT_CMD = 0x001c, + OM2K_MSGT_CONNECT_COMPL = 0x001e, + OM2K_MSGT_CONNECT_REJ = 0x001f, + + OM2K_MSGT_DISABLE_REQ = 0x0028, + OM2K_MSGT_DISABLE_REQ_ACK = 0x002a, + OM2K_MSGT_DISABLE_REQ_REJ = 0x002b, + OM2K_MSGT_DISABLE_RES_ACK = 0x002c, + OM2K_MSGT_DISABLE_RES_NACK = 0x002d, + OM2K_MSGT_DISABLE_RES = 0x002e, + OM2K_MSGT_DISCONNECT_CMD = 0x0030, + OM2K_MSGT_DISCONNECT_COMPL = 0x0032, + OM2K_MSGT_DISCONNECT_REJ = 0x0033, + OM2K_MSGT_ENABLE_REQ = 0x0034, + OM2K_MSGT_ENABLE_REQ_ACK = 0x0036, + OM2K_MSGT_ENABLE_REQ_REJ = 0x0037, + OM2K_MSGT_ENABLE_RES_ACK = 0x0038, + OM2K_MSGT_ENABLE_RES_NACK = 0x0039, + OM2K_MSGT_ENABLE_RES = 0x003a, + + OM2K_MSGT_FAULT_REP_ACK = 0x0040, + OM2K_MSGT_FAULT_REP_NACK = 0x0041, + OM2K_MSGT_FAULT_REP = 0x0042, + + OM2K_MSGT_IS_CONF_REQ = 0x0060, + OM2K_MSGT_IS_CONF_REQ_ACK = 0x0062, + OM2K_MSGT_IS_CONF_REQ_REJ = 0x0063, + OM2K_MSGT_IS_CONF_RES_ACK = 0x0064, + OM2K_MSGT_IS_CONF_RES_NACK = 0x0065, + OM2K_MSGT_IS_CONF_RES = 0x0066, + + OM2K_MSGT_OP_INFO = 0x0074, + OM2K_MSGT_OP_INFO_ACK = 0x0076, + OM2K_MSGT_OP_INFO_REJ = 0x0077, + OM2K_MSGT_RESET_CMD = 0x0078, + OM2K_MSGT_RESET_COMPL = 0x007a, + OM2K_MSGT_RESET_REJ = 0x007b, + + OM2K_MSGT_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_NEGOT_REQ_ACK = 0x0104, + OM2K_MSGT_NEGOT_REQ_NACK = 0x0105, + OM2K_MSGT_NEGOT_REQ = 0x0106, +}; + +enum abis_om2k_dei { + OM2K_DEI_CAL_TIME = 0x0d, + OM2K_DEI_CON_CONN_LIST = 0x10, + OM2K_DEI_END_LIST_NR = 0x13, + OM2K_DEI_IS_CONN_LIST = 0x27, + OM2K_DEI_LIST_NR = 0x28, + OM2K_DEI_OP_INFO = 0x2e, + OM2K_DEI_NEGOT_REC1 = 0x90, + OM2K_DEI_NEGOT_REC2 = 0x91, +}; + +enum abis_om2k_mo_cls { + OM2K_MO_CLS_TRXC = 0x01, + OM2K_MO_CLS_TS = 0x03, + OM2K_MO_CLS_TF = 0x04, + OM2K_MO_CLS_IS = 0x05, + OM2K_MO_CLS_CON = 0x06, + OM2K_MO_CLS_DP = 0x07, + OM2K_MO_CLS_CF = 0x0a, + OM2K_MO_CLS_TX = 0x0b, + OM2K_MO_CLS_RX = 0x0c, +}; + +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 Rejecte" }, + { 0x0028, "Disable Request" }, + { 0x002a, "Disable Request Accept" }, + { 0x002b, "Disable Request Reject" }, + { 0x002c, "Disable Result ACK" }, + { 0x002d, "Disable Result NACK" }, + { 0x002e, "Disable Result" }, + { 0x0030, "Disconnect Command" }, + { 0x0032, "Disconnect Complete" }, + { 0x0033, "Disconnect Reject" }, + { 0x0034, "Enable Request" }, + { 0x0036, "Enable Request Accept" }, + { 0x0037, "Enable Request Reject" }, + { 0x0038, "Enable Result ACK" }, + { 0x0039, "Enable Result NACK" }, + { 0x003a, "Enable Result" }, + { 0x003c, "Escape Downlink Normal" }, + { 0x003d, "Escape Downlink NACK" }, + { 0x003e, "Escape Uplink Normal" }, + { 0x003f, "Escape Uplink NACK" }, + { 0x0040, "Fault Report ACK" }, + { 0x0041, "Fault Report NACK" }, + { 0x0042, "Fault Report" }, + { 0x0044, "File Package End Command" }, + { 0x0046, "File Package End Result" }, + { 0x0047, "File Package End Reject" }, + { 0x0048, "File Relation Request" }, + { 0x004a, "File Relation Response" }, + { 0x004b, "File Relation Request Reject" }, + { 0x004c, "File Segment Transfer" }, + { 0x004e, "File Segment Transfer Complete" }, + { 0x004f, "File Segment Transfer Reject" }, + { 0x0050, "HW Information Request" }, + { 0x0052, "HW Information Request Accept" }, + { 0x0053, "HW Information Request Reject" }, + { 0x0054, "HW Information Result ACK" }, + { 0x0055, "HW Information Result NACK" }, + { 0x0056, "HW Information Result" }, + { 0x0060, "IS Configuration Request" }, + { 0x0062, "IS Configuration Request Accept" }, + { 0x0063, "IS Configuration Request Reject" }, + { 0x0064, "IS Configuration Result ACK" }, + { 0x0065, "IS Configuration Result NACK" }, + { 0x0066, "IS Configuration Result" }, + { 0x0068, "Load Data End" }, + { 0x006a, "Load Data End Result" }, + { 0x006b, "Load Data End Reject" }, + { 0x006c, "Load Data Init" }, + { 0x006e, "Load Data Init Accept" }, + { 0x006f, "Load Data Init Reject" }, + { 0x0070, "Loop Control Command" }, + { 0x0072, "Loop Control Complete" }, + { 0x0073, "Loop Control Reject" }, + { 0x0074, "Operational Information" }, + { 0x0076, "Operational Information Accept" }, + { 0x0077, "Operational Information Reject" }, + { 0x0078, "Reset Command" }, + { 0x007a, "Reset Complete" }, + { 0x007b, "Reset Reject" }, + { 0x007c, "RX Configuration Request" }, + { 0x007e, "RX Configuration Request Accept" }, + { 0x007f, "RX Configuration Request Reject" }, + { 0x0080, "RX Configuration Result ACK" }, + { 0x0081, "RX Configuration Result NACK" }, + { 0x0082, "RX Configuration Result" }, + { 0x0084, "Start Request" }, + { 0x0086, "Start Request Accept" }, + { 0x0087, "Start Request Reject" }, + { 0x0088, "Start Result ACK" }, + { 0x0089, "Start Result NACK" }, + { 0x008a, "Start Result" }, + { 0x008c, "Status Request" }, + { 0x008e, "Status Response" }, + { 0x008f, "Status Reject" }, + { 0x0094, "Test Request" }, + { 0x0096, "Test Request Accept" }, + { 0x0097, "Test Request Reject" }, + { 0x0098, "Test Result ACK" }, + { 0x0099, "Test Result NACK" }, + { 0x009a, "Test Result" }, + { 0x00a0, "TF Configuration Request" }, + { 0x00a2, "TF Configuration Request Accept" }, + { 0x00a3, "TF Configuration Request Reject" }, + { 0x00a4, "TF Configuration Result ACK" }, + { 0x00a5, "TF Configuration Result NACK" }, + { 0x00a6, "TF Configuration Result" }, + { 0x00a8, "TS Configuration Request" }, + { 0x00aa, "TS Configuration Request Accept" }, + { 0x00ab, "TS Configuration Request Reject" }, + { 0x00ac, "TS Configuration Result ACK" }, + { 0x00ad, "TS Configuration Result NACK" }, + { 0x00ae, "TS Configuration Result" }, + { 0x00b0, "TX Configuration Request" }, + { 0x00b2, "TX Configuration Request Accept" }, + { 0x00b3, "TX Configuration Request Reject" }, + { 0x00b4, "TX Configuration Result ACK" }, + { 0x00b5, "TX Configuration Result NACK" }, + { 0x00b6, "TX Configuration Result" }, + { 0x00bc, "DIP Alarm Report ACK" }, + { 0x00bd, "DIP Alarm Report NACK" }, + { 0x00be, "DIP Alarm Report" }, + { 0x00c0, "DIP Alarm Status Request" }, + { 0x00c2, "DIP Alarm Status Response" }, + { 0x00c3, "DIP Alarm Status Reject" }, + { 0x00c4, "DIP Quality Report I ACK" }, + { 0x00c5, "DIP Quality Report I NACK" }, + { 0x00c6, "DIP Quality Report I" }, + { 0x00c8, "DIP Quality Report II ACK" }, + { 0x00c9, "DIP Quality Report II NACK" }, + { 0x00ca, "DIP Quality Report II" }, + { 0x00dc, "DP Configuration Request" }, + { 0x00de, "DP Configuration Request Accept" }, + { 0x00df, "DP Configuration Request Reject" }, + { 0x00e0, "DP Configuration Result ACK" }, + { 0x00e1, "DP Configuration Result NACK" }, + { 0x00e2, "DP Configuration Result" }, + { 0x00e4, "Capabilities HW Info Report ACK" }, + { 0x00e5, "Capabilities HW Info Report NACK" }, + { 0x00e6, "Capabilities HW Info Report" }, + { 0x00e8, "Capabilities Request" }, + { 0x00ea, "Capabilities Request Accept" }, + { 0x00eb, "Capabilities Request Reject" }, + { 0x00ec, "Capabilities Result ACK" }, + { 0x00ed, "Capabilities Result NACK" }, + { 0x00ee, "Capabilities Result" }, + { 0x00f0, "FM Configuration Request" }, + { 0x00f2, "FM Configuration Request Accept" }, + { 0x00f3, "FM Configuration Request Reject" }, + { 0x00f4, "FM Configuration Result ACK" }, + { 0x00f5, "FM Configuration Result NACK" }, + { 0x00f6, "FM Configuration Result" }, + { 0x00f8, "FM Report Request" }, + { 0x00fa, "FM Report Response" }, + { 0x00fb, "FM Report Reject" }, + { 0x00fc, "FM Start Command" }, + { 0x00fe, "FM Start Complete" }, + { 0x00ff, "FM Start Reject" }, + { 0x0100, "FM Stop Command" }, + { 0x0102, "FM Stop Complete" }, + { 0x0103, "FM Stop Reject" }, + { 0x0104, "Negotiation Request ACK" }, + { 0x0105, "Negotiation Request NACK" }, + { 0x0106, "Negotiation Request" }, + { 0x0108, "BTS Initiated Request ACK" }, + { 0x0109, "BTS Initiated Request NACK" }, + { 0x010a, "BTS Initiated Request" }, + { 0x010c, "Radio Channels Release Command" }, + { 0x010e, "Radio Channels Release Complete" }, + { 0x010f, "Radio Channels Release Reject" }, + { 0x0118, "Feature Control Command" }, + { 0x011a, "Feature Control Complete" }, + { 0x011b, "Feature Control Reject" }, + + { 0, NULL } +}; + +/* TS 12.21 Section 9.4: Attributes */ +static const struct value_string om2k_attr_vals[] = { + { 0x00, "Accordance indication" }, + { 0x01, "Alarm Id" }, + { 0x02, "Alarm Data" }, + { 0x03, "Alarm Severity" }, + { 0x04, "Alarm Status" }, + { 0x05, "Alarm Status Type" }, + { 0x06, "BCC" }, + { 0x07, "BS_AG_BKS_RES" }, + { 0x09, "BSIC" }, + { 0x0a, "BA_PA_MFRMS" }, + { 0x0b, "CBCH Indicator" }, + { 0x0c, "CCCH Options" }, + { 0x0d, "Calendar Time" }, + { 0x0f, "Channel Combination" }, + { 0x10, "CON Connection List" }, + { 0x11, "Data End Indication" }, + { 0x12, "DRX_DEV_MAX" }, + { 0x13, "End List Number" }, + { 0x14, "External Condition Map Class 1" }, + { 0x15, "External Condition Map Class 2" }, + { 0x16, "File Relation Indication" }, + { 0x17, "File Revision" }, + { 0x18, "File Segment Data" }, + { 0x19, "File Segment Length" }, + { 0x1a, "File Segment Sequence Number" }, + { 0x1b, "File Size" }, + { 0x1c, "Filling Marker" }, + { 0x1d, "FN Offset" }, + { 0x1e, "Frequency List" }, + { 0x1f, "Frequency Specifier RX" }, + { 0x20, "Frequency Specifier TX" }, + { 0x21, "HSN" }, + { 0x22, "ICM Indicator" }, + { 0x23, "Internal Fault Map Class 1A" }, + { 0x24, "Internal Fault Map Class 1B" }, + { 0x25, "Internal Fault Map Class 2A" }, + { 0x26, "Internal Fault Map Class 2A Extension" }, + { 0x27, "IS Connection List" }, + { 0x28, "List Number" }, + { 0x29, "File Package State Indication" }, + { 0x2a, "Local Access State" }, + { 0x2b, "MAIO" }, + { 0x2c, "MO State" }, + { 0x2d, "Ny1" }, + { 0x2e, "Operational Information" }, + { 0x2f, "Power" }, + { 0x30, "RU Position Data" }, + { 0x31, "Protocol Error" }, + { 0x32, "Reason Code" }, + { 0x33, "Receiver Diversity" }, + { 0x34, "Replacement Unit Map" }, + { 0x35, "Result Code" }, + { 0x36, "RU Revision Data" }, + { 0x38, "T3105" }, + { 0x39, "Test Loop Setting" }, + { 0x3a, "TF Mode" }, + { 0x3b, "TF Compensation Value" }, + { 0x3c, "Time Slot Number" }, + { 0x3d, "TSC" }, + { 0x3e, "RU Logical Id" }, + { 0x3f, "RU Serial Number Data" }, + { 0x40, "BTS Version" }, + { 0x41, "OML IWD Version" }, + { 0x42, "RWL IWD Version" }, + { 0x43, "OML Function Map 1" }, + { 0x44, "OML Function Map 2" }, + { 0x45, "RSL Function Map 1" }, + { 0x46, "RSL Function Map 2" }, + { 0x47, "Extended Range Indicator" }, + { 0x48, "Request Indicators" }, + { 0x49, "DIP Alarm Condition Map" }, + { 0x4a, "ES Incoming" }, + { 0x4b, "ES Outgoing" }, + { 0x4e, "SES Incoming" }, + { 0x4f, "SES Outgoing" }, + { 0x50, "Replacement Unit Map Extension" }, + { 0x52, "UAS Incoming" }, + { 0x53, "UAS Outgoing" }, + { 0x58, "DF Incoming" }, + { 0x5a, "DF Outgoing" }, + { 0x5c, "SF" }, + { 0x60, "S Bits Setting" }, + { 0x61, "CRC-4 Use Option" }, + { 0x62, "T Parameter" }, + { 0x63, "N Parameter" }, + { 0x64, "N1 Parameter" }, + { 0x65, "N3 Parameter" }, + { 0x66, "N4 Parameter" }, + { 0x67, "P Parameter" }, + { 0x68, "Q Parameter" }, + { 0x69, "BI_Q1" }, + { 0x6a, "BI_Q2" }, + { 0x74, "ICM Boundary Parameters" }, + { 0x77, "AFT" }, + { 0x78, "AFT RAI" }, + { 0x79, "Link Supervision Control" }, + { 0x7a, "Link Supervision Filtering Time" }, + { 0x7b, "Call Supervision Time" }, + { 0x7c, "Interval Length UAS Incoming" }, + { 0x7d, "Interval Length UAS Outgoing" }, + { 0x7e, "ICM Channel Rate" }, + { 0x7f, "Attribute Identifier" }, + { 0x80, "FM Frequency List" }, + { 0x81, "FM Frequency Report" }, + { 0x82, "FM Percentile" }, + { 0x83, "FM Clear Indication" }, + { 0x84, "HW Info Signature" }, + { 0x85, "MO Record" }, + { 0x86, "TF Synchronisation Source" }, + { 0x87, "TTA" }, + { 0x88, "End Segment Number" }, + { 0x89, "Segment Number" }, + { 0x8a, "Capabilities Signature" }, + { 0x8c, "File Relation List" }, + { 0x90, "Negotiation Record I" }, + { 0x91, "Negotiation Record II" }, + { 0x92, "Encryption Algorithm" }, + { 0x94, "Interference Rejection Combining" }, + { 0x95, "Dedication Information" }, + { 0x97, "Feature Code" }, + { 0x98, "FS Offset" }, + { 0x99, "ESB Timeslot" }, + { 0x9a, "Master TG Instance" }, + { 0x9b, "Master TX Chain Delay" }, + { 0x9c, "External Condition Class 2 Extension" }, + { 0x9d, "TSs MO State" }, + { 0, NULL } +}; + +const struct value_string om2k_mo_class_short_vals[] = { + { 0x01, "TRXC" }, + { 0x03, "TS" }, + { 0x04, "TF" }, + { 0x05, "IS" }, + { 0x06, "CON" }, + { 0x07, "DP" }, + { 0x0a, "CF" }, + { 0x0b, "TX" }, + { 0x0c, "RX" }, + { 0, NULL } +}; + +static struct msgb *om2k_msgb_alloc(void) +{ + return msgb_alloc_headroom(OM_ALLOC_SIZE, OM_HEADROOM_SIZE, + "OM2000"); +} + +static 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; +} + +static int abis_om2k_sendmsg(struct gsm_bts *bts, struct msgb *msg) +{ + struct abis_om2k_hdr *o2h; + int to_trx_oml; + + msg->l2h = msg->data; + o2h = (struct abis_om2k_hdr *) msg->l2h; + + switch (o2h->mo.class) { + case OM2K_MO_CLS_TRXC: + case OM2K_MO_CLS_TX: + case OM2K_MO_CLS_RX: + case OM2K_MO_CLS_TS: + /* Route through per-TRX OML Link to the appropriate TRX */ + to_trx_oml = 1; + msg->trx = gsm_bts_trx_by_nr(bts, o2h->mo.inst); + if (!msg->trx) { + LOGP(DNM, LOGL_ERROR, "MO=%s Tx Dropping msg to " + "non-existing TRX\n", om2k_mo_name(&o2h->mo)); + return -ENODEV; + } + break; + default: + /* Route through the IXU/DXU OML Link */ + msg->trx = bts->c0; + to_trx_oml = 0; + break; + } + + return _abis_nm_sendmsg(msg, to_trx_oml); +} + +static void fill_om2k_hdr(struct abis_om2k_hdr *o2h, const struct abis_om2k_mo *mo, + uint16_t msg_type, uint8_t attr_len) +{ + o2h->om.mdisc = ABIS_OM_MDISC_FOM; + o2h->om.placement = ABIS_OM_PLACEMENT_ONLY; + o2h->om.sequence = 0; + o2h->om.length = 6 + attr_len; + o2h->msg_type = htons(msg_type); + memcpy(&o2h->mo, mo, sizeof(o2h->mo)); +} + +const struct abis_om2k_mo om2k_mo_cf = { OM2K_MO_CLS_CF, 0, 0xFF, 0 }; +const struct abis_om2k_mo om2k_mo_is = { OM2K_MO_CLS_IS, 0, 0xFF, 0 }; +const struct abis_om2k_mo om2k_mo_con = { OM2K_MO_CLS_CON, 0, 0xFF, 0 }; + +static int abis_om2k_cal_time_resp(struct gsm_bts *bts) +{ + struct msgb *msg = om2k_msgb_alloc(); + struct abis_om2k_hdr *o2k; + time_t tm_t; + struct tm *tm; + + o2k = (struct abis_om2k_hdr *) msgb_put(msg, sizeof(*o2k)); + fill_om2k_hdr(o2k, &om2k_mo_cf, OM2K_MSGT_CAL_TIME_RESP, 7); + + 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, 0); + + 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, 2); + + 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)); + + return abis_om2k_sendmsg(bts, msg); +} + +int abis_om2k_tx_is_conf_req(struct gsm_bts *bts, struct om2k_is_conn_grp *cg, + unsigned int num_cg ) +{ + struct msgb *msg = om2k_msgb_alloc(); + struct abis_om2k_hdr *o2k; + + o2k = (struct abis_om2k_hdr *) msgb_put(msg, sizeof(*o2k)); + fill_om2k_hdr(o2k, &om2k_mo_is, OM2K_MSGT_IS_CONF_REQ, + 2 + 2 + TLV_GROSS_LEN(num_cg * sizeof(*cg))); + + 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_cg * sizeof(*cg), (uint8_t *)cg); + + return abis_om2k_sendmsg(bts, msg); +} + +int abis_om2k_tx_con_conf_req(struct gsm_bts *bts, uint8_t *data, + unsigned int len) +{ + struct msgb *msg = om2k_msgb_alloc(); + struct abis_om2k_hdr *o2k; + + o2k = (struct abis_om2k_hdr *) msgb_put(msg, sizeof(*o2k)); + fill_om2k_hdr(o2k, &om2k_mo_con, OM2K_MSGT_CON_CONF_REQ, + 2 + 2 + TLV_GROSS_LEN(len)); + + msgb_tv_put(msg, OM2K_DEI_LIST_NR, 1); + msgb_tv_put(msg, OM2K_DEI_END_LIST_NR, 1); + + msgb_tlv_put(msg, OM2K_DEI_CON_CONN_LIST, len, data); + + return abis_om2k_sendmsg(bts, msg); +} + + +static int abis_om2k_tx_negot_req_ack(struct gsm_bts *bts, const struct abis_om2k_mo *mo, + uint8_t *data, unsigned int len) +{ + struct msgb *msg = om2k_msgb_alloc(); + struct abis_om2k_hdr *o2k; + + o2k = (struct abis_om2k_hdr *) msgb_put(msg, sizeof(*o2k)); + fill_om2k_hdr(o2k, mo, OM2K_MSGT_NEGOT_REQ_ACK, 2+len); + + 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 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(msg->trx->bts, &o2h->mo, out_buf, out_cur - out_buf); +} + +static int om2k_rx_start_res(struct msgb *msg) +{ + struct abis_om2k_hdr *o2h = msgb_l2(msg); + int rc; + + rc = abis_om2k_tx_simple(msg->trx->bts, &o2h->mo, OM2K_MSGT_START_RES_ACK); + rc = abis_om2k_tx_op_info(msg->trx->bts, &o2h->mo, 1); + + return rc; +} + +static int om2k_rx_op_info_ack(struct msgb *msg) +{ + struct abis_om2k_hdr *o2h = msgb_l2(msg); + + /* FIXME: update Operational state in our structures */ + + return 0; +} + +int abis_om2k_rcvmsg(struct msgb *msg) +{ + struct gsm_bts *bts = msg->trx->bts; + struct abis_om2k_hdr *o2h = msgb_l2(msg); + struct abis_om_hdr *oh = &o2h->om; + uint16_t msg_type = ntohs(o2h->msg_type); + int rc = 0; + + /* Various consistency checks */ + if (oh->placement != ABIS_OM_PLACEMENT_ONLY) { + LOGP(DNM, LOGL_ERROR, "ABIS OML placement 0x%x not supported\n", + oh->placement); + if (oh->placement != ABIS_OM_PLACEMENT_FIRST) + return -EINVAL; + } + if (oh->sequence != 0) { + LOGP(DNM, LOGL_ERROR, "ABIS OML sequence 0x%x != 0x00\n", + oh->sequence); + return -EINVAL; + } + + msg->l3h = (unsigned char *)o2h + sizeof(*o2h); + + if (oh->mdisc != ABIS_OM_MDISC_FOM) { + LOGP(DNM, LOGL_ERROR, "unknown ABIS OM2000 message discriminator 0x%x\n", + oh->mdisc); + return -EINVAL; + } + + DEBUGP(DNM, "Rx MO=%s %s (%s)\n", om2k_mo_name(&o2h->mo), + get_value_string(om2k_msgcode_vals, msg_type), + hexdump(msg->l2h, msgb_l2len(msg))); + + switch (msg_type) { + case OM2K_MSGT_CAL_TIME_REQ: + rc = abis_om2k_cal_time_resp(bts); + break; + case OM2K_MSGT_FAULT_REP: + rc = abis_om2k_tx_simple(bts, &o2h->mo, OM2K_MSGT_FAULT_REP_ACK); + break; + case OM2K_MSGT_NEGOT_REQ: + rc = om2k_rx_negot_req(msg); + break; + case OM2K_MSGT_START_RES: + rc = om2k_rx_start_res(msg); + break; + case OM2K_MSGT_OP_INFO_ACK: + rc = om2k_rx_op_info_ack(msg); + break; + case OM2K_MSGT_IS_CONF_RES: + rc = abis_om2k_tx_simple(bts, &o2h->mo, OM2K_MSGT_IS_CONF_RES_ACK); + 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_CONNECT_COMPL: + rc = abis_om2k_tx_simple(bts, &o2h->mo, OM2K_MSGT_RESET_CMD); + break; + case OM2K_MSGT_RESET_COMPL: + rc = abis_om2k_tx_simple(bts, &o2h->mo, OM2K_MSGT_START_REQ); + break; + case OM2K_MSGT_ENABLE_RES: + rc = abis_om2k_tx_simple(bts, &o2h->mo, OM2K_MSGT_ENABLE_RES_ACK); + 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_STATUS_RESP: + break; + case OM2K_MSGT_START_REQ_ACK: + case OM2K_MSGT_CON_CONF_REQ_ACK: + case OM2K_MSGT_IS_CONF_REQ_ACK: + case OM2K_MSGT_ENABLE_REQ_ACK: + case OM2K_MSGT_ALARM_STATUS_REQ_ACK: + case OM2K_MSGT_DISABLE_REQ_ACK: + break; + default: + LOGP(DNM, LOGL_NOTICE, "Rx unhandled OM2000 msg %s\n", + get_value_string(om2k_msgcode_vals, msg_type)); + } + + msgb_free(msg); + return rc; +} diff --git a/openbsc/src/libbsc/abis_om2000_vty.c b/openbsc/src/libbsc/abis_om2000_vty.c new file mode 100644 index 000000000..5949c869c --- /dev/null +++ b/openbsc/src/libbsc/abis_om2000_vty.c @@ -0,0 +1,456 @@ +/* VTY interface for A-bis OM2000 */ + +/* (C) 2010-2011 by Harald Welte + * + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + * + */ + +#include +#include +#include +#include + +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +extern struct gsm_network *bsc_gsmnet; + +static struct cmd_node om2k_node = { + OM2K_NODE, + "%s(om2k)# ", + 1, +}; + +struct oml_node_state { + struct gsm_bts *bts; + struct abis_om2k_mo mo; +}; + +static int dummy_config_write(struct vty *v) +{ + return CMD_SUCCESS; +} + +/* FIXME: auto-generate those strings from the value_string lists */ +#define OM2K_OBJCLASS_VTY "(trxc|ts|tf|is|con|dp|cf|tx|rx)" +#define OM2K_OBJCLASS_VTY_HELP "TRX Controller\n" \ + "Timeslot\n" \ + "Timing Function\n" \ + "Interface Switch\n" \ + "Abis Concentrator\n" \ + "Digital Path\n" \ + "Central Function\n" \ + "Transmitter\n" \ + "Receiver\n" + +DEFUN(om2k_class_inst, om2k_class_inst_cmd, + "bts <0-255> om2000 class " OM2K_OBJCLASS_VTY + " <0-255> <0-255> <0-255>", + "BTS related commands\n" "BTS Number\n" + "Manipulate the OM2000 managed objects\n" + "Object Class\n" OM2K_OBJCLASS_VTY_HELP + "BTS Number\n" "Associated SO Instance\n" "Instance Number\n") +{ + struct gsm_bts *bts; + struct oml_node_state *oms; + int bts_nr = atoi(argv[0]); + + bts = gsm_bts_num(bsc_gsmnet, bts_nr); + if (!bts) { + vty_out(vty, "%% No such BTS (%d)%s", bts_nr, VTY_NEWLINE); + return CMD_WARNING; + } + + if (bts->type != GSM_BTS_TYPE_RBS2000) { + vty_out(vty, "%% BTS %d not an Ericsson RBS%s", + bts_nr, VTY_NEWLINE); + return CMD_WARNING; + } + + oms = talloc_zero(tall_bsc_ctx, struct oml_node_state); + if (!oms) + return CMD_WARNING; + + oms->bts = bts; + oms->mo.class = get_string_value(om2k_mo_class_short_vals, argv[1]); + oms->mo.bts = atoi(argv[2]); + oms->mo.assoc_so = atoi(argv[3]); + oms->mo.inst = atoi(argv[4]); + + vty->index = oms; + vty->node = OM2K_NODE; + + return CMD_SUCCESS; + +} + +DEFUN(om2k_classnum_inst, om2k_classnum_inst_cmd, + "bts <0-255> om2000 class <0-255> <0-255> <0-255> <0-255>", + "BTS related commands\n" "BTS Number\n" + "Manipulate the OML managed objects\n" + "Object Class\n" "Object Class\n" + "BTS Number\n" "Associated SO Instance\n" "Instance Number\n") +{ + struct gsm_bts *bts; + struct oml_node_state *oms; + int bts_nr = atoi(argv[0]); + + bts = gsm_bts_num(bsc_gsmnet, bts_nr); + if (!bts) { + vty_out(vty, "%% No such BTS (%d)%s", bts_nr, VTY_NEWLINE); + return CMD_WARNING; + } + + oms = talloc_zero(tall_bsc_ctx, struct oml_node_state); + if (!oms) + return CMD_WARNING; + + oms->bts = bts; + oms->mo.class = atoi(argv[1]); + oms->mo.bts = atoi(argv[2]); + oms->mo.assoc_so = atoi(argv[3]); + oms->mo.inst = atoi(argv[4]); + + vty->index = oms; + vty->node = OM2K_NODE; + + return CMD_SUCCESS; +} + +DEFUN(om2k_reset, om2k_reset_cmd, + "reset-command", + "Reset the MO\n") +{ + struct oml_node_state *oms = vty->index; + + abis_om2k_tx_reset_cmd(oms->bts, &oms->mo); + return CMD_SUCCESS; +} + +DEFUN(om2k_start, om2k_start_cmd, + "start-request", + "Start the MO\n") +{ + struct oml_node_state *oms = vty->index; + + abis_om2k_tx_start_req(oms->bts, &oms->mo); + return CMD_SUCCESS; +} + +DEFUN(om2k_status, om2k_status_cmd, + "status-request", + "Get the MO Status\n") +{ + struct oml_node_state *oms = vty->index; + + abis_om2k_tx_status_req(oms->bts, &oms->mo); + return CMD_SUCCESS; +} + +DEFUN(om2k_connect, om2k_connect_cmd, + "connect-command", + "Connect the MO\n") +{ + struct oml_node_state *oms = vty->index; + + abis_om2k_tx_connect_cmd(oms->bts, &oms->mo); + return CMD_SUCCESS; +} + +DEFUN(om2k_disconnect, om2k_disconnect_cmd, + "disconnect-command", + "Disconnect the MO\n") +{ + struct oml_node_state *oms = vty->index; + + abis_om2k_tx_disconnect_cmd(oms->bts, &oms->mo); + return CMD_SUCCESS; +} + +DEFUN(om2k_enable, om2k_enable_cmd, + "enable-request", + "Enable the MO\n") +{ + struct oml_node_state *oms = vty->index; + + abis_om2k_tx_enable_req(oms->bts, &oms->mo); + return CMD_SUCCESS; +} + +DEFUN(om2k_disable, om2k_disable_cmd, + "disable-request", + "Disable the MO\n") +{ + struct oml_node_state *oms = vty->index; + + abis_om2k_tx_disable_req(oms->bts, &oms->mo); + return CMD_SUCCESS; +} + +DEFUN(om2k_op_info, om2k_op_info_cmd, + "operational-info <0-1>", + "Set operational information\n") +{ + struct oml_node_state *oms = vty->index; + int oper = atoi(argv[0]); + + abis_om2k_tx_op_info(oms->bts, &oms->mo, oper); + return CMD_SUCCESS; +} + +DEFUN(om2k_test, om2k_test_cmd, + "test-request", + "Test the MO\n") +{ + struct oml_node_state *oms = vty->index; + + abis_om2k_tx_test_req(oms->bts, &oms->mo); + return CMD_SUCCESS; +} + +struct con_conn_group { + struct llist_head list; + + uint8_t cg; + uint16_t ccp; + uint8_t tag; + uint8_t tei; +}; + +static void add_con_list(struct gsm_bts *bts, uint8_t cg, uint16_t ccp, + uint8_t tag, uint8_t tei) +{ + struct con_conn_group *ent = talloc_zero(bts, struct con_conn_group); + + ent->cg = cg; + ent->ccp = ccp; + ent->tag = tag; + ent->tei = tei; + + llist_add_tail(&ent->list, &bts->rbs2000.con.conn_groups); +} + +static int del_con_list(struct gsm_bts *bts, uint8_t cg, uint16_t ccp, + uint8_t tag, uint8_t tei) +{ + struct con_conn_group *grp, *grp2; + + llist_for_each_entry_safe(grp, grp2, &bts->rbs2000.con.conn_groups, list) { + if (grp->cg == cg && grp->ccp == ccp && grp->tag == tag + && grp->tei == tei) { + llist_del(&grp->list); + talloc_free(grp); + return 0; + } + } + return -ENOENT; +} + +#define CON_LIST_HELP "CON connetiton list\n" \ + "Add entry to CON list\n" \ + "Delete entry from CON list\n" \ + "Connection Group Number\n" \ + "CON Connection Point\n" \ + +DEFUN(om2k_con_list_dec, om2k_con_list_dec_cmd, + "con-connection-list (add|del) <1-255> <0-1023> deconcentrated", + CON_LIST_HELP "De-concentrated in/outlet\n") +{ + struct oml_node_state *oms = vty->index; + struct gsm_bts *bts = oms->bts; + uint8_t cg = atoi(argv[1]); + uint16_t ccp = atoi(argv[2]); + + if (!strcmp(argv[0], "add")) + add_con_list(bts, cg, ccp, 0, 0xff); + else { + if (del_con_list(bts, cg, ccp, 0, 0xff) < 0) { + vty_out(vty, "%% No matching CON list entry%s", + VTY_NEWLINE); + return CMD_WARNING; + } + } + + return CMD_SUCCESS; +} + +DEFUN(om2k_con_list_tei, om2k_con_list_tei_cmd, + "con-connection-list (add|del) <1-255> <0-1023> tei <0-63>", + CON_LIST_HELP "Concentrated in/outlet with TEI\n" "TEI Number\n") +{ + struct oml_node_state *oms = vty->index; + struct gsm_bts *bts = oms->bts; + uint8_t cg = atoi(argv[1]); + uint16_t ccp = atoi(argv[2]); + uint8_t tei = atoi(argv[3]); + + if (!strcmp(argv[0], "add")) + add_con_list(bts, cg, ccp, cg, tei); + else { + if (del_con_list(bts, cg, ccp, cg, tei) < 0) { + vty_out(vty, "%% No matching CON list entry%s", + VTY_NEWLINE); + return CMD_WARNING; + } + } + + return CMD_SUCCESS; +} + +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; +} + +struct is_conn_group { + struct llist_head list; + uint16_t icp1; + uint16_t icp2; + uint8_t ci; +}; + +DEFUN(cfg_bts_is_conn_list, cfg_bts_is_conn_list_cmd, + "is-connection-list (add|del) <0-2047> <0-2047> <0-255>", + "Interface Switch Connnection List\n" + "Add to IS list\n" "Delete from IS list\n" + "ICP1\n" "ICP2\n" "Contiguity Index\n") +{ + struct gsm_bts *bts = vty->index; + uint16_t icp1 = atoi(argv[1]); + uint16_t icp2 = atoi(argv[2]); + uint8_t ci = atoi(argv[3]); + struct is_conn_group *grp, *grp2; + + if (!strcmp(argv[0], "add")) { + grp = talloc_zero(bts, struct is_conn_group); + grp->icp1 = icp1; + grp->icp2 = icp2; + grp->ci = ci; + llist_add_tail(&grp->list, &bts->rbs2000.is.conn_groups); + } else { + llist_for_each_entry_safe(grp, grp2, &bts->rbs2000.is.conn_groups, list) { + if (grp->icp1 == icp1 && grp->icp2 == icp2 + && grp->ci == ci) { + llist_del(&grp->list); + talloc_free(grp); + return CMD_SUCCESS; + } + } + vty_out(vty, "%% No matching IS Conn Group found!%s", + VTY_NEWLINE); + return CMD_WARNING; + } + + return CMD_SUCCESS; +} + + +DEFUN(om2k_is_conf_req, om2k_is_conf_req_cmd, + "is-conf-req", + "Send IS Configuration Request\n") +{ + struct oml_node_state *oms = vty->index; + struct gsm_bts *bts = oms->bts; + struct is_conn_group *grp; + unsigned int num_grps = 0, i = 0; + struct om2k_is_conn_grp *o2grps; + + /* count number of groups in linked list */ + llist_for_each_entry(grp, &bts->rbs2000.is.conn_groups, list) + num_grps++; + + if (!num_grps) { + vty_out(vty, "%% No IS connection groups configured!%s", + VTY_NEWLINE); + return CMD_WARNING; + } + + /* allocate buffer for oml group array */ + o2grps = 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(&o2grps[i++], grp->icp1, grp->icp2, grp->ci); + + /* send the actual OML request */ + abis_om2k_tx_is_conf_req(oms->bts, o2grps, num_grps); + + talloc_free(o2grps); + + return CMD_SUCCESS; +} + +void abis_om2k_config_write_bts(struct vty *vty, struct gsm_bts *bts) +{ + struct is_conn_group *igrp; + struct con_conn_group *cgrp; + + llist_for_each_entry(igrp, &bts->rbs2000.is.conn_groups, list) + vty_out(vty, " is-connection-list add %u %u %u%s", + igrp->icp1, igrp->icp2, igrp->ci, VTY_NEWLINE); + + llist_for_each_entry(cgrp, &bts->rbs2000.con.conn_groups, list) { + vty_out(vty, " con-connection-list add %u %u ", + cgrp->cg, cgrp->ccp); + if (cgrp->tei == 0xff) + vty_out(vty, "deconcentrated%s", VTY_NEWLINE); + else + vty_out(vty, "tei %u%s", cgrp->tei, VTY_NEWLINE); + } +} + +int abis_om2k_vty_init(void) +{ + install_element(ENABLE_NODE, &om2k_class_inst_cmd); + install_element(ENABLE_NODE, &om2k_classnum_inst_cmd); + install_node(&om2k_node, dummy_config_write); + + install_default(OM2K_NODE); + install_element(OM2K_NODE, &ournode_exit_cmd); + 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_is_conf_req_cmd); + install_element(OM2K_NODE, &om2k_con_list_dec_cmd); + install_element(OM2K_NODE, &om2k_con_list_tei_cmd); + + install_element(BTS_NODE, &cfg_bts_is_conn_list_cmd); + + return 0; +} diff --git a/openbsc/src/libbsc/abis_rsl.c b/openbsc/src/libbsc/abis_rsl.c new file mode 100644 index 000000000..07a7dc68c --- /dev/null +++ b/openbsc/src/libbsc/abis_rsl.c @@ -0,0 +1,1960 @@ +/* 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 + * + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + * + */ + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#define RSL_ALLOC_SIZE 1024 +#define RSL_ALLOC_HEADROOM 128 + +#define MAX(a, b) (a) >= (b) ? (a) : (b) + +static int rsl_send_imm_assignment(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; + dispatch_signal(SS_LCHAN, sig_no, &sig); +} + +static u_int8_t mdisc_by_msgtype(u_int8_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, + u_int8_t msg_type) +{ + dh->c.msg_discr = mdisc_by_msgtype(msg_type); + dh->c.msg_type = msg_type; + dh->ie_chan = RSL_IE_CHAN_NR; +} + +/* determine logical channel based on TRX and channel number IE */ +struct gsm_lchan *lchan_lookup(struct gsm_bts_trx *trx, u_int8_t chan_nr) +{ + struct gsm_lchan *lchan; + u_int8_t ts_nr = chan_nr & 0x07; + u_int8_t cbits = chan_nr >> 3; + u_int8_t lch_idx; + struct gsm_bts_trx_ts *ts = &trx->ts[ts_nr]; + + if (cbits == 0x01) { + lch_idx = 0; /* TCH/F */ + if (ts->pchan != GSM_PCHAN_TCH_F && + ts->pchan != GSM_PCHAN_PDCH && + ts->pchan != GSM_PCHAN_TCH_F_PDCH) + LOGP(DRSL, LOGL_ERROR, "chan_nr=0x%02x but pchan=%u\n", + chan_nr, ts->pchan); + } else if ((cbits & 0x1e) == 0x02) { + lch_idx = cbits & 0x1; /* TCH/H */ + if (ts->pchan != GSM_PCHAN_TCH_H) + LOGP(DRSL, LOGL_ERROR, "chan_nr=0x%02x but pchan=%u\n", + chan_nr, ts->pchan); + } else if ((cbits & 0x1c) == 0x04) { + lch_idx = cbits & 0x3; /* SDCCH/4 */ + if (ts->pchan != GSM_PCHAN_CCCH_SDCCH4) + LOGP(DRSL, LOGL_ERROR, "chan_nr=0x%02x but pchan=%u\n", + chan_nr, ts->pchan); + } else if ((cbits & 0x18) == 0x08) { + lch_idx = cbits & 0x7; /* SDCCH/8 */ + if (ts->pchan != GSM_PCHAN_SDCCH8_SACCH8C) + LOGP(DRSL, LOGL_ERROR, "chan_nr=0x%02x but pchan=%u\n", + chan_nr, ts->pchan); + } else if (cbits == 0x10 || cbits == 0x11 || cbits == 0x12) { + lch_idx = 0; + if (ts->pchan != GSM_PCHAN_CCCH && + ts->pchan != GSM_PCHAN_CCCH_SDCCH4) + LOGP(DRSL, LOGL_ERROR, "chan_nr=0x%02x but pchan=%u\n", + chan_nr, ts->pchan); + /* FIXME: we should not return first sdcch4 !!! */ + } else { + LOGP(DRSL, LOGL_ERROR, "unknown chan_nr=0x%02x\n", chan_nr); + return NULL; + } + + lchan = &ts->lchan[lch_idx]; + log_set_context(BSC_CTX_LCHAN, lchan); + if (lchan->conn) + log_set_context(BSC_CTX_SUBSCR, lchan->conn->subscr); + + return lchan; +} + +/* See Table 10.5.25 of GSM04.08 */ +static u_int8_t ts2chan_nr(const struct gsm_bts_trx_ts *ts, uint8_t lchan_nr) +{ + u_int8_t cbits, chan_nr; + + switch (ts->pchan) { + case GSM_PCHAN_TCH_F: + case GSM_PCHAN_PDCH: + case GSM_PCHAN_TCH_F_PDCH: + cbits = 0x01; + break; + case GSM_PCHAN_TCH_H: + cbits = 0x02; + cbits += lchan_nr; + break; + case GSM_PCHAN_CCCH_SDCCH4: + cbits = 0x04; + cbits += lchan_nr; + break; + case GSM_PCHAN_SDCCH8_SACCH8C: + cbits = 0x08; + cbits += lchan_nr; + break; + default: + case GSM_PCHAN_CCCH: + cbits = 0x10; + break; + } + + chan_nr = (cbits << 3) | (ts->nr & 0x7); + + return chan_nr; +} + +u_int8_t lchan2chan_nr(const struct gsm_lchan *lchan) +{ + return ts2chan_nr(lchan->ts, lchan->nr); +} + +/* As per TS 03.03 Section 2.2, the IMSI has 'not more than 15 digits' */ +u_int64_t str_to_imsi(const char *imsi_str) +{ + u_int64_t ret; + + ret = strtoull(imsi_str, NULL, 10); + + return ret; +} + +/* Table 5 Clause 7 TS 05.02 */ +unsigned int n_pag_blocks(int bs_ccch_sdcch_comb, unsigned int bs_ag_blks_res) +{ + if (!bs_ccch_sdcch_comb) + return 9 - bs_ag_blks_res; + else + return 3 - bs_ag_blks_res; +} + +/* Chapter 6.5.2 of TS 05.02 */ +unsigned int get_ccch_group(u_int64_t imsi, unsigned int bs_cc_chans, + unsigned int n_pag_blocks) +{ + return (imsi % 1000) % (bs_cc_chans * n_pag_blocks) / n_pag_blocks; +} + +/* Chapter 6.5.2 of TS 05.02 */ +unsigned int get_paging_group(u_int64_t imsi, unsigned int bs_cc_chans, + int n_pag_blocks) +{ + return (imsi % 1000) % (bs_cc_chans * n_pag_blocks) % n_pag_blocks; +} + +static struct msgb *rsl_msgb_alloc(void) +{ + return msgb_alloc_headroom(RSL_ALLOC_SIZE, RSL_ALLOC_HEADROOM, + "RSL"); +} + +#define MACBLOCK_SIZE 23 +static void pad_macblock(u_int8_t *out, const u_int8_t *in, int len) +{ + memcpy(out, in, len); + + if (len < MACBLOCK_SIZE) + memset(out+len, 0x2b, MACBLOCK_SIZE-len); +} + +/* Chapter 9.3.7: Encryption Information */ +static int build_encr_info(u_int8_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 u_int8_t *cause_v, u_int8_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]); +} + +/* Send a BCCH_INFO message as per Chapter 8.5.1 */ +int rsl_bcch_info(struct gsm_bts_trx *trx, u_int8_t type, + const u_int8_t *data, int len) +{ + struct abis_rsl_dchan_hdr *dh; + struct msgb *msg = rsl_msgb_alloc(); + + dh = (struct abis_rsl_dchan_hdr *) msgb_put(msg, sizeof*dh); + init_dchan_hdr(dh, RSL_MT_BCCH_INFO); + dh->chan_nr = RSL_CHAN_BCCH; + + msgb_tv_put(msg, RSL_IE_SYSINFO_TYPE, type); + msgb_tlv_put(msg, RSL_IE_FULL_BCCH_INFO, len, data); + + msg->trx = trx; + + return abis_rsl_sendmsg(msg); +} + +int rsl_sacch_filling(struct gsm_bts_trx *trx, u_int8_t type, + const u_int8_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->trx = trx; + + return abis_rsl_sendmsg(msg); +} + +int rsl_sacch_info_modify(struct gsm_lchan *lchan, u_int8_t type, + const u_int8_t *data, int len) +{ + struct abis_rsl_dchan_hdr *dh; + struct msgb *msg = rsl_msgb_alloc(); + u_int8_t chan_nr = 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->trx = lchan->ts->trx; + + 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; + u_int8_t chan_nr = 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->trx = lchan->ts->trx; + + 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; + u_int8_t chan_nr = 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->trx = lchan->ts->trx; + + return abis_rsl_sendmsg(msg); +} + +static int channel_mode_from_lchan(struct rsl_ie_chan_mode *cm, + struct gsm_lchan *lchan) +{ + memset(cm, 0, sizeof(cm)); + + /* FIXME: what to do with data calls ? */ + if (lchan->ts->trx->bts->network->dtx_enabled) + cm->dtx_dtu = 0x03; + else + cm->dtx_dtu = 0x00; + + /* set TCH Speech/Data */ + cm->spd_ind = lchan->rsl_cmode; + + if (lchan->rsl_cmode == RSL_CMOD_SPD_SIGN && + lchan->tch_mode != GSM48_CMODE_SIGN) + LOGP(DRSL, LOGL_ERROR, "unsupported: rsl_mode == signalling, " + "but tch_mode != signalling\n"); + + switch (lchan->type) { + case GSM_LCHAN_SDCCH: + cm->chan_rt = RSL_CMOD_CRT_SDCCH; + break; + case GSM_LCHAN_TCH_F: + cm->chan_rt = RSL_CMOD_CRT_TCH_Bm; + break; + case GSM_LCHAN_TCH_H: + cm->chan_rt = RSL_CMOD_CRT_TCH_Lm; + break; + case GSM_LCHAN_NONE: + case GSM_LCHAN_UNKNOWN: + default: + return -EINVAL; + } + + switch (lchan->tch_mode) { + case GSM48_CMODE_SIGN: + cm->chan_rate = 0; + break; + case GSM48_CMODE_SPEECH_V1: + cm->chan_rate = RSL_CMOD_SP_GSM1; + break; + case GSM48_CMODE_SPEECH_EFR: + cm->chan_rate = RSL_CMOD_SP_GSM2; + break; + case GSM48_CMODE_SPEECH_AMR: + cm->chan_rate = RSL_CMOD_SP_GSM3; + break; + case GSM48_CMODE_DATA_14k5: + cm->chan_rate = RSL_CMOD_SP_NT_14k5; + break; + case GSM48_CMODE_DATA_12k0: + cm->chan_rate = RSL_CMOD_SP_NT_12k0; + break; + case GSM48_CMODE_DATA_6k0: + cm->chan_rate = RSL_CMOD_SP_NT_6k0; + break; + default: + return -EINVAL; + } + + return 0; +} + +/* Chapter 8.4.1 */ +#if 0 +int rsl_chan_activate(struct gsm_bts_trx *trx, u_int8_t chan_nr, + u_int8_t act_type, + struct rsl_ie_chan_mode *chan_mode, + struct rsl_ie_chan_ident *chan_ident, + u_int8_t bs_power, u_int8_t ms_power, + u_int8_t ta) +{ + 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_CHAN_ACTIV); + dh->chan_nr = chan_nr; + + msgb_tv_put(msg, RSL_IE_ACT_TYPE, act_type); + /* For compatibility with Phase 1 */ + msgb_tlv_put(msg, RSL_IE_CHAN_MODE, sizeof(*chan_mode), + (u_int8_t *) chan_mode); + msgb_tlv_put(msg, RSL_IE_CHAN_IDENT, 4, + (u_int8_t *) chan_ident); +#if 0 + msgb_tlv_put(msg, RSL_IE_ENCR_INFO, 1, + (u_int8_t *) &encr_info); +#endif + msgb_tv_put(msg, RSL_IE_BS_POWER, bs_power); + msgb_tv_put(msg, RSL_IE_MS_POWER, ms_power); + msgb_tv_put(msg, RSL_IE_TIMING_ADVANCE, ta); + + msg->trx = trx; + + return abis_rsl_sendmsg(msg); +} +#endif + +int rsl_chan_activate_lchan(struct gsm_lchan *lchan, u_int8_t act_type, + u_int8_t ta, u_int8_t ho_ref) +{ + struct abis_rsl_dchan_hdr *dh; + struct msgb *msg; + int rc; + uint8_t *len; + + u_int8_t chan_nr = lchan2chan_nr(lchan); + struct rsl_ie_chan_mode cm; + struct gsm48_chan_desc cd; + + rc = channel_mode_from_lchan(&cm, lchan); + if (rc < 0) + return rc; + + memset(&cd, 0, sizeof(cd)); + gsm48_lchan2chan_desc(&cd, lchan); + + msg = rsl_msgb_alloc(); + dh = (struct abis_rsl_dchan_hdr *) msgb_put(msg, sizeof(*dh)); + init_dchan_hdr(dh, RSL_MT_CHAN_ACTIV); + dh->chan_nr = chan_nr; + + msgb_tv_put(msg, RSL_IE_ACT_TYPE, act_type); + msgb_tlv_put(msg, RSL_IE_CHAN_MODE, sizeof(cm), + (u_int8_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_tlv_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)) { + u_int8_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); + + if (lchan->tch_mode == GSM48_CMODE_SPEECH_AMR) + msgb_tlv_put(msg, RSL_IE_MR_CONFIG, sizeof(lchan->mr_conf), + (u_int8_t *) &lchan->mr_conf); + + msg->trx = lchan->ts->trx; + + 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; + + u_int8_t chan_nr = 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), + (u_int8_t *) &cm); + + if (lchan->encr.alg_id > RSL_ENC_ALG_A5(0)) { + u_int8_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); + } + + if (lchan->tch_mode == GSM48_CMODE_SPEECH_AMR) { + msgb_tlv_put(msg, RSL_IE_MR_CONFIG, sizeof(lchan->mr_conf), + (u_int8_t *) &lchan->mr_conf); + } + + msg->trx = lchan->ts->trx; + + 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; + u_int8_t chan_nr = lchan2chan_nr(lchan); + u_int8_t encr_info[MAX_A5_KEY_LEN+2]; + u_int8_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->trx = lchan->ts->trx; + + 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 = lchan2chan_nr(lchan); + + msg->lchan = lchan; + msg->trx = lchan->ts->trx; + + DEBUGP(DRSL, "%s DEACTivate SACCH CMD\n", gsm_lchan_name(lchan)); + + return abis_rsl_sendmsg(msg); +} + +static void error_timeout_cb(void *data) +{ + struct gsm_lchan *lchan = data; + if (lchan->state != LCHAN_S_REL_ERR) { + LOGP(DRSL, LOGL_ERROR, "%s error timeout but not in error state: %d\n", + gsm_lchan_name(lchan), lchan->state); + return; + } + + /* go back to the none state */ + LOGP(DRSL, LOGL_NOTICE, "%s is back in operation.\n", gsm_lchan_name(lchan)); + rsl_lchan_set_state(lchan, LCHAN_S_NONE); +} + +/* 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) +{ + struct abis_rsl_dchan_hdr *dh; + struct msgb *msg; + + 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 = lchan2chan_nr(lchan); + + msg->lchan = lchan; + msg->trx = lchan->ts->trx; + + DEBUGP(DRSL, "%s RF Channel Release CMD due error %d\n", gsm_lchan_name(lchan), error); + + if (error) { + /* + * the nanoBTS sends RLL release indications after the channel release. This can + * be a problem when we have reassigned the channel to someone else and then can + * not figure out who used this channel. + */ + rsl_lchan_set_state(lchan, LCHAN_S_REL_ERR); + lchan->error_timer.data = lchan; + lchan->error_timer.cb = error_timeout_cb; + bsc_schedule_timer(&lchan->error_timer, + msg->trx->bts->network->T3111 + 2, 0); + } + + /* BTS will respond by RF CHAN REL ACK */ + return abis_rsl_sendmsg(msg); +} + +static int rsl_rx_rf_chan_rel_ack(struct gsm_lchan *lchan) +{ + + DEBUGP(DRSL, "%s RF CHANNEL RELEASE ACK\n", gsm_lchan_name(lchan)); + + 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)); + bsc_del_timer(&lchan->T3111); + /* we have an error timer pending to release that */ + if (lchan->state != LCHAN_S_REL_ERR) + rsl_lchan_set_state(lchan, LCHAN_S_NONE); + lchan_free(lchan); + + return 0; +} + +int rsl_paging_cmd(struct gsm_bts *bts, u_int8_t paging_group, u_int8_t len, + u_int8_t *ms_ident, u_int8_t chan_needed) +{ + struct abis_rsl_dchan_hdr *dh; + struct msgb *msg = rsl_msgb_alloc(); + + dh = (struct abis_rsl_dchan_hdr *) msgb_put(msg, sizeof(*dh)); + init_dchan_hdr(dh, RSL_MT_PAGING_CMD); + dh->chan_nr = RSL_CHAN_PCH_AGCH; + + msgb_tv_put(msg, RSL_IE_PAGING_GROUP, paging_group); + msgb_tlv_put(msg, RSL_IE_MS_IDENTITY, len-2, ms_ident+2); + msgb_tv_put(msg, RSL_IE_CHAN_NEEDED, chan_needed); + + msg->trx = bts->c0; + + return abis_rsl_sendmsg(msg); +} + +int imsi_str2bcd(u_int8_t *bcd_out, const char *str_in) +{ + int i, len = strlen(str_in); + + for (i = 0; i < len; i++) { + int num = str_in[i] - 0x30; + if (num < 0 || num > 9) + return -1; + if (i % 2 == 0) + bcd_out[i/2] = num; + else + bcd_out[i/2] |= (num << 4); + } + + return 0; +} + +/* Chapter 8.5.6 */ +int rsl_imm_assign_cmd(struct gsm_bts *bts, u_int8_t len, u_int8_t *val) +{ + struct msgb *msg = rsl_msgb_alloc(); + struct abis_rsl_dchan_hdr *dh; + u_int8_t buf[MACBLOCK_SIZE]; + + dh = (struct abis_rsl_dchan_hdr *) msgb_put(msg, sizeof(*dh)); + init_dchan_hdr(dh, RSL_MT_IMMEDIATE_ASSIGN_CMD); + dh->chan_nr = RSL_CHAN_PCH_AGCH; + + switch (bts->type) { + case GSM_BTS_TYPE_BS11: + msgb_tlv_put(msg, RSL_IE_IMM_ASS_INFO, len, val); + break; + default: + /* If phase 2, construct a FULL_IMM_ASS_INFO */ + pad_macblock(buf, val, len); + msgb_tlv_put(msg, RSL_IE_FULL_IMM_ASS_INFO, MACBLOCK_SIZE, buf); + break; + } + + msg->trx = bts->c0; + + 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 = lchan2chan_nr(lchan); + msgb_tv_put(msg, RSL_IE_SIEMENS_MRPCI, *(u_int8_t *)mrpci); + + DEBUGP(DRSL, "%s TX Siemens MRPCI 0x%02x\n", + gsm_lchan_name(lchan), *(u_int8_t *)mrpci); + + msg->trx = lchan->ts->trx; + + 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, u_int8_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, lchan2chan_nr(msg->lchan), + link_id, 1); + + msg->trx = msg->lchan->ts->trx; + + 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, u_int8_t link_id) +{ + struct msgb *msg; + + msg = rsl_rll_simple(RSL_MT_EST_REQ, lchan2chan_nr(lchan), + link_id, 0); + msg->trx = lchan->ts->trx; + + return abis_rsl_sendmsg(msg); +} + +/* 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, u_int8_t link_id, u_int8_t reason) +{ + + struct msgb *msg; + + msg = rsl_rll_simple(RSL_MT_REL_REQ, lchan2chan_nr(lchan), + link_id, 0); + /* 0 is normal release, 1 is local end */ + msgb_tv_put(msg, RSL_IE_RELEASE_MODE, reason); + + /* FIXME: start some timer in case we don't receive a REL ACK ? */ + + msg->trx = lchan->ts->trx; + + return abis_rsl_sendmsg(msg); +} + +int rsl_lchan_set_state(struct gsm_lchan *lchan, int state) +{ + lchan->state = state; + return 0; +} + +/* Chapter 8.4.2: Channel Activate Acknowledge */ +static int rsl_rx_chan_act_ack(struct msgb *msg) +{ + struct abis_rsl_dchan_hdr *rslh = msgb_l2(msg); + + /* BTS has confirmed channel activation, we now need + * to assign the activated channel to the MS */ + if (rslh->ie_chan != RSL_IE_CHAN_NR) + return -EINVAL; + + if (msg->lchan->state != LCHAN_S_ACT_REQ) + LOGP(DRSL, LOGL_NOTICE, "%s CHAN ACT ACK, but state %s\n", + gsm_lchan_name(msg->lchan), + gsm_lchans_name(msg->lchan->state)); + rsl_lchan_set_state(msg->lchan, LCHAN_S_ACTIVE); + + if (msg->lchan->rqd_ref) { + rsl_send_imm_assignment(msg->lchan); + talloc_free(msg->lchan->rqd_ref); + msg->lchan->rqd_ref = NULL; + msg->lchan->rqd_ta = 0; + } + + send_lchan_signal(S_LCHAN_ACTIVATE_ACK, msg->lchan, NULL); + + return 0; +} + +/* Chapter 8.4.3: Channel Activate NACK */ +static int rsl_rx_chan_act_nack(struct msgb *msg) +{ + struct abis_rsl_dchan_hdr *dh = msgb_l2(msg); + struct tlv_parsed tp; + + 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 u_int8_t *cause = TLVP_VAL(&tp, RSL_IE_CAUSE); + print_rsl_cause(LOGL_ERROR, cause, + TLVP_LEN(&tp, RSL_IE_CAUSE)); + if (*cause != RSL_ERR_RCH_ALR_ACTV_ALLOC) + rsl_lchan_set_state(msg->lchan, LCHAN_S_NONE); + } else + rsl_lchan_set_state(msg->lchan, LCHAN_S_NONE); + + LOGPC(DRSL, LOGL_ERROR, "\n"); + + send_lchan_signal(S_LCHAN_ACTIVATE_NACK, msg->lchan, NULL); + + lchan_free(msg->lchan); + 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; + + /* FIXME: print which channel */ + LOGP(DRSL, LOGL_NOTICE, "%s CONNECTION FAIL: RELEASING ", + gsm_lchan_name(msg->lchan)); + + 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"); + /* FIXME: only free it after channel release ACK */ + counter_inc(msg->lchan->ts->trx->bts->network->stats.chan.rf_fail); + return rsl_rf_chan_release(msg->lchan, 1); +} + +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_meas_rep *mr) +{ + int i; + + DEBUGP(DMEAS, "MEASUREMENT RESULT NR=%d ", 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); + u_int8_t len; + const u_int8_t *val; + int rc; + + /* check if this channel is actually active */ + /* FIXME: maybe this check should be way more generic/centralized */ + if (msg->lchan->state != LCHAN_S_ACTIVE) { + LOGP(DRSL, LOGL_DEBUG, "%s: MEAS RES for inactive channel\n", + gsm_lchan_name(msg->lchan)); + return 0; + } + + memset(mr, 0, sizeof(*mr)); + mr->lchan = msg->lchan; + + rsl_tlv_parse(&tp, dh->data, msgb_l2len(msg)-sizeof(*dh)); + + if (!TLVP_PRESENT(&tp, RSL_IE_MEAS_RES_NR) || + !TLVP_PRESENT(&tp, RSL_IE_UPLINK_MEAS) || + !TLVP_PRESENT(&tp, RSL_IE_BS_POWER)) + return -EIO; + + /* Mandatory Parts */ + mr->nr = *TLVP_VAL(&tp, RSL_IE_MEAS_RES_NR); + + len = TLVP_LEN(&tp, RSL_IE_UPLINK_MEAS); + val = TLVP_VAL(&tp, RSL_IE_UPLINK_MEAS); + if (len >= 3) { + if (val[0] & 0x40) + mr->flags |= MEAS_REP_F_DL_DTX; + mr->ul.full.rx_lev = val[0] & 0x3f; + mr->ul.sub.rx_lev = val[1] & 0x3f; + mr->ul.full.rx_qual = val[2]>>3 & 0x7; + mr->ul.sub.rx_qual = val[2] & 0x7; + } + + mr->bs_power = *TLVP_VAL(&tp, RSL_IE_BS_POWER); + + /* Optional Parts */ + if (TLVP_PRESENT(&tp, RSL_IE_MS_TIMING_OFFSET)) + mr->ms_timing_offset = + *TLVP_VAL(&tp, RSL_IE_MS_TIMING_OFFSET); + + if (TLVP_PRESENT(&tp, RSL_IE_L1_INFO)) { + val = TLVP_VAL(&tp, RSL_IE_L1_INFO); + mr->flags |= MEAS_REP_F_MS_L1; + mr->ms_l1.pwr = ms_pwr_dbm(msg->trx->bts->band, val[0] >> 3); + if (val[0] & 0x04) + mr->flags |= MEAS_REP_F_FPC; + mr->ms_l1.ta = val[1]; + } + if (TLVP_PRESENT(&tp, RSL_IE_L3_INFO)) { + msg->l3h = (u_int8_t *) TLVP_VAL(&tp, RSL_IE_L3_INFO); + rc = gsm48_parse_meas_rep(mr, msg); + if (rc < 0) + return rc; + } + + print_meas_rep(mr); + + send_lchan_signal(S_LCHAN_MEAS_REP, msg->lchan, mr); + + return 0; +} + +/* Chapter 8.4.7 */ +static int rsl_rx_hando_det(struct msgb *msg) +{ + struct abis_rsl_dchan_hdr *dh = msgb_l2(msg); + struct tlv_parsed tp; + + DEBUGP(DRSL, "%s HANDOVER DETECT ", gsm_lchan_name(msg->lchan)); + + rsl_tlv_parse(&tp, dh->data, msgb_l2len(msg)-sizeof(*dh)); + + if (TLVP_PRESENT(&tp, RSL_IE_ACCESS_DELAY)) + DEBUGPC(DRSL, "access delay = %u\n", + *TLVP_VAL(&tp, RSL_IE_ACCESS_DELAY)); + else + DEBUGPC(DRSL, "\n"); + + send_lchan_signal(S_LCHAN_HANDOVER_DETECT, msg->lchan, NULL); + + return 0; +} + +static int abis_rsl_rx_dchan(struct msgb *msg) +{ + struct abis_rsl_dchan_hdr *rslh = msgb_l2(msg); + int rc = 0; + char *ts_name; + + msg->lchan = lchan_lookup(msg->trx, rslh->chan_nr); + ts_name = gsm_lchan_name(msg->lchan); + + switch (rslh->c.msg_type) { + case RSL_MT_CHAN_ACTIV_ACK: + DEBUGP(DRSL, "%s CHANNEL ACTIVATE ACK\n", ts_name); + rc = rsl_rx_chan_act_ack(msg); + break; + case RSL_MT_CHAN_ACTIV_NACK: + rc = rsl_rx_chan_act_nack(msg); + break; + case RSL_MT_CONN_FAIL: + rc = rsl_rx_conn_fail(msg); + break; + case RSL_MT_MEAS_RES: + rc = rsl_rx_meas_res(msg); + break; + case RSL_MT_HANDO_DET: + rc = rsl_rx_hando_det(msg); + break; + case RSL_MT_RF_CHAN_REL_ACK: + rc = rsl_rx_rf_chan_rel_ack(msg->lchan); + break; + case RSL_MT_MODE_MODIFY_ACK: + DEBUGP(DRSL, "%s CHANNEL MODE MODIFY ACK\n", ts_name); + break; + case RSL_MT_MODE_MODIFY_NACK: + LOGP(DRSL, LOGL_ERROR, "%s CHANNEL MODE MODIFY NACK\n", ts_name); + break; + case RSL_MT_IPAC_PDCH_ACT_ACK: + DEBUGPC(DRSL, "%s IPAC PDCH ACT ACK\n", ts_name); + msg->lchan->ts->flags |= TS_F_PDCH_MODE; + break; + case RSL_MT_IPAC_PDCH_ACT_NACK: + LOGP(DRSL, LOGL_ERROR, "%s IPAC PDCH ACT NACK\n", ts_name); + break; + case RSL_MT_IPAC_PDCH_DEACT_ACK: + DEBUGP(DRSL, "%s IPAC PDCH DEACT ACK\n", ts_name); + msg->lchan->ts->flags &= ~TS_F_PDCH_MODE; + break; + case RSL_MT_IPAC_PDCH_DEACT_NACK: + LOGP(DRSL, LOGL_ERROR, "%s IPAC PDCH DEACT NACK\n", ts_name); + break; + case RSL_MT_PHY_CONTEXT_CONF: + case RSL_MT_PREPROC_MEAS_RES: + case RSL_MT_TALKER_DET: + case RSL_MT_LISTENER_DET: + case RSL_MT_REMOTE_CODEC_CONF_REP: + case RSL_MT_MR_CODEC_MOD_ACK: + case RSL_MT_MR_CODEC_MOD_NACK: + case RSL_MT_MR_CODEC_MOD_PER: + LOGP(DRSL, LOGL_NOTICE, "%s Unimplemented Abis RSL DChan " + "msg 0x%02x\n", ts_name, rslh->c.msg_type); + break; + default: + LOGP(DRSL, LOGL_NOTICE, "%s unknown Abis RSL DChan msg 0x%02x\n", + ts_name, rslh->c.msg_type); + return -EINVAL; + } + + return rc; +} + +static int rsl_rx_error_rep(struct msgb *msg) +{ + struct abis_rsl_common_hdr *rslh = msgb_l2(msg); + struct tlv_parsed tp; + + LOGP(DRSL, LOGL_ERROR, "%s ERROR REPORT ", gsm_trx_name(msg->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); + 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(msg->trx)); + break; + case RSL_MT_OVERLOAD: + /* indicate CCCH / ACCH / processor overload */ + LOGP(DRSL, LOGL_ERROR, "%s CCCH/ACCH/CPU Overload\n", + gsm_trx_name(msg->trx)); + break; + default: + LOGP(DRSL, LOGL_NOTICE, "%s Unknown Abis RSL TRX message " + "type 0x%02x\n", gsm_trx_name(msg->trx), rslh->msg_type); + return -EINVAL; + } + return rc; +} + +/* If T3101 expires, we never received a response to IMMEDIATE ASSIGN */ +static void t3101_expired(void *data) +{ + struct gsm_lchan *lchan = data; + + rsl_rf_chan_release(lchan, 1); +} + +/* If T3111 expires, we will send the RF Channel Request */ +static void t3111_expired(void *data) +{ + struct gsm_lchan *lchan = data; + + rsl_rf_chan_release(lchan, 0); +} + +#define GSM48_LEN2PLEN(a) (((a) << 2) | 1) + +/* Format an IMM ASS REJ according to 04.08 Chapter 9.1.20 */ +static int rsl_send_imm_ass_rej(struct gsm_bts *bts, + unsigned int num_req_refs, + struct gsm48_req_ref *rqd_refs, + uint8_t wait_ind) +{ + uint8_t buf[GSM_MACBLOCK_LEN]; + struct gsm48_imm_ass_rej *iar = (struct gsm48_imm_ass_rej *)buf; + + /* create IMMEDIATE ASSIGN REJECT 04.08 message */ + memset(iar, 0, sizeof(*iar)); + iar->proto_discr = GSM48_PDISC_RR; + iar->msg_type = GSM48_MT_RR_IMM_ASS; + 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; + + return rsl_imm_assign_cmd(bts, sizeof(iar), (uint8_t *) iar); +} + +/* MS has requested a channel on the RACH */ +static int rsl_rx_chan_rqd(struct msgb *msg) +{ + struct gsm_bts *bts = msg->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; + u_int8_t rqd_ta; + int is_lu; + + u_int16_t arfcn; + u_int8_t ts_number, subch; + + /* parse request reference to be used in immediate assign */ + if (rqd_hdr->data[0] != RSL_IE_REQ_REFERENCE) + return -EINVAL; + + rqd_ref = (struct gsm48_req_ref *) &rqd_hdr->data[1]; + + /* parse access delay and use as TA */ + if (rqd_hdr->data[sizeof(struct gsm48_req_ref)+1] != RSL_IE_ACCESS_DELAY) + return -EINVAL; + rqd_ta = rqd_hdr->data[sizeof(struct gsm48_req_ref)+2]; + + /* determine channel type (SDCCH/TCH_F/TCH_H) based on + * request reference RA */ + lctype = get_ctype_by_chreq(bts->network, rqd_ref->ra); + chreq_reason = get_reason_by_chreq(rqd_ref->ra, bts->network->neci); + + counter_inc(bts->network->stats.chreq.total); + + /* + * We want LOCATION UPDATES to succeed and will assign a TCH + * if we have no SDCCH available. + */ + is_lu = !!(chreq_reason == GSM_CHREQ_REASON_LOCATION_UPD); + + /* check availability / allocate channel */ + lchan = lchan_alloc(bts, lctype, is_lu); + if (!lchan) { + LOGP(DRSL, LOGL_NOTICE, "BTS %d CHAN RQD: no resources for %s 0x%x\n", + msg->lchan->ts->trx->bts->nr, gsm_lchant_name(lctype), rqd_ref->ra); + counter_inc(bts->network->stats.chreq.no_channel); + /* FIXME gather multiple CHAN RQD and reject up to 4 at the same time */ + if (bts->network->T3122) + rsl_send_imm_ass_rej(bts, 1, rqd_ref, bts->network->T3122 & 0xff); + return -ENOMEM; + } + + if (lchan->state != LCHAN_S_NONE) + LOGP(DRSL, LOGL_NOTICE, "%s lchan_alloc() returned channel " + "in state %s\n", gsm_lchan_name(lchan), + gsm_lchans_name(lchan->state)); + rsl_lchan_set_state(lchan, LCHAN_S_ACT_REQ); + + /* 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; + + ts_number = lchan->ts->nr; + 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; + + /* FIXME: Start another timer or assume the BTS sends a ACK/NACK? */ + rsl_chan_activate_lchan(lchan, 0x00, rqd_ta, 0); + + DEBUGP(DRSL, "%s Activating ARFCN(%u) SS(%u) lctype %s " + "r=%s ra=0x%02x\n", gsm_lchan_name(lchan), arfcn, subch, + gsm_lchant_name(lchan->type), gsm_chreq_name(chreq_reason), + rqd_ref->ra); + return 0; +} + +static int rsl_send_imm_assignment(struct gsm_lchan *lchan) +{ + struct gsm_bts *bts = lchan->ts->trx->bts; + u_int8_t buf[GSM_MACBLOCK_LEN]; + struct gsm48_imm_ass *ia = (struct gsm48_imm_ass *) buf; + + /* create IMMEDIATE ASSIGN 04.08 messge */ + memset(ia, 0, sizeof(*ia)); + /* we set ia->l2_plen once we know the length of the MA below */ + ia->proto_discr = GSM48_PDISC_RR; + ia->msg_type = GSM48_MT_RR_IMM_ASS; + ia->page_mode = GSM48_PM_SAME; + gsm48_lchan2chan_desc(&ia->chan_desc, lchan); + + /* use request reference extracted from CHAN_RQD */ + memcpy(&ia->req_ref, lchan->rqd_ref, sizeof(ia->req_ref)); + ia->timing_advance = lchan->rqd_ta; + if (!lchan->ts->hopping.enabled) { + ia->mob_alloc_len = 0; + } else { + ia->mob_alloc_len = lchan->ts->hopping.ma_len; + memcpy(ia->mob_alloc, lchan->ts->hopping.ma_data, ia->mob_alloc_len); + } + /* we need to subtract 1 byte from sizeof(*ia) since ia includes the l2_plen field */ + ia->l2_plen = GSM48_LEN2PLEN((sizeof(*ia)-1) + ia->mob_alloc_len); + + /* Start timer T3101 to wait for GSM48_MT_RR_PAG_RESP */ + lchan->T3101.cb = t3101_expired; + lchan->T3101.data = lchan; + bsc_schedule_timer(&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, (u_int8_t *) ia); +} + +/* MS has requested a channel on the RACH */ +static int rsl_rx_ccch_load(struct msgb *msg) +{ + struct abis_rsl_dchan_hdr *rslh = msgb_l2(msg); + u_int16_t pg_buf_space; + u_int16_t rach_slot_count = -1; + u_int16_t rach_busy_count = -1; + u_int16_t rach_access_count = -1; + + switch (rslh->data[0]) { + case RSL_IE_PAGING_LOAD: + pg_buf_space = rslh->data[1] << 8 | rslh->data[2]; + if (is_ipaccess_bts(msg->trx->bts) && pg_buf_space == 0xffff) { + /* paging load below configured threshold, use 50 as default */ + pg_buf_space = 50; + } + paging_update_buffer_space(msg->trx->bts, pg_buf_space); + break; + case RSL_IE_RACH_LOAD: + if (msg->data_len >= 7) { + rach_slot_count = rslh->data[2] << 8 | rslh->data[3]; + rach_busy_count = rslh->data[4] << 8 | rslh->data[5]; + rach_access_count = rslh->data[6] << 8 | rslh->data[7]; + } + break; + default: + break; + } + + return 0; +} + +static int abis_rsl_rx_cchan(struct msgb *msg) +{ + struct abis_rsl_dchan_hdr *rslh = msgb_l2(msg); + int rc = 0; + + msg->lchan = lchan_lookup(msg->trx, rslh->chan_nr); + + switch (rslh->c.msg_type) { + case RSL_MT_CHAN_RQD: + /* MS has requested a channel on the RACH */ + rc = rsl_rx_chan_rqd(msg); + break; + case RSL_MT_CCCH_LOAD_IND: + /* current load on the CCCH */ + rc = rsl_rx_ccch_load(msg); + break; + case RSL_MT_DELETE_IND: + /* CCCH overloaded, IMM_ASSIGN was dropped */ + case RSL_MT_CBCH_LOAD_IND: + /* current load on the CBCH */ + LOGP(DRSL, LOGL_NOTICE, "Unimplemented Abis RSL TRX message " + "type 0x%02x\n", rslh->c.msg_type); + break; + default: + LOGP(DRSL, LOGL_NOTICE, "Unknown Abis RSL TRX message type " + "0x%02x\n", rslh->c.msg_type); + return -EINVAL; + } + + return rc; +} + +static int rsl_rx_rll_err_ind(struct msgb *msg) +{ + struct abis_rsl_rll_hdr *rllh = msgb_l2(msg); + u_int8_t *rlm_cause = rllh->data; + + LOGP(DRLL, LOGL_ERROR, "%s ERROR INDICATION cause=%s\n", + gsm_lchan_name(msg->lchan), + rsl_rlm_cause_name(rlm_cause[1])); + + rll_indication(msg->lchan, rllh->link_id, BSC_RLLR_IND_ERR_IND); + + if (rlm_cause[1] == RLL_CAUSE_T200_EXPIRED) { + counter_inc(msg->lchan->ts->trx->bts->network->stats.chan.rll_err); + return rsl_rf_chan_release(msg->lchan, 1); + } + + return 0; +} + +static void rsl_handle_release(struct gsm_lchan *lchan) +{ + int sapi; + struct gsm_bts *bts; + + /* maybe we have only brought down one RLL */ + 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; + } + + + + /* wait a bit to send the RF Channel Release */ + lchan->T3111.cb = t3111_expired; + lchan->T3111.data = lchan; + bts = lchan->ts->trx->bts; + bsc_schedule_timer(&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 abis_rsl_rll_hdr *rllh = msgb_l2(msg); + int rc = 0; + char *ts_name; + u_int8_t sapi = rllh->link_id & 7; + + msg->lchan = lchan_lookup(msg->trx, rllh->chan_nr); + ts_name = gsm_lchan_name(msg->lchan); + DEBUGP(DRLL, "%s SAPI=%u ", ts_name, sapi); + + switch (rllh->c.msg_type) { + case RSL_MT_DATA_IND: + DEBUGPC(DRLL, "DATA INDICATION\n"); + if (msgb_l2len(msg) > + sizeof(struct abis_rsl_common_hdr) + sizeof(*rllh) && + rllh->data[0] == RSL_IE_L3_INFO) { + msg->l3h = &rllh->data[3]; + return gsm0408_rcvmsg(msg, rllh->link_id); + } + break; + case RSL_MT_EST_IND: + DEBUGPC(DRLL, "ESTABLISH INDICATION\n"); + /* lchan is established, stop T3101 */ + msg->lchan->sapis[rllh->link_id & 0x7] = LCHAN_SAPI_MS; + bsc_del_timer(&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); + rsl_lchan_rll_release(msg->lchan, rllh->link_id); + 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); + rsl_lchan_rll_release(msg->lchan, rllh->link_id); + break; + case RSL_MT_ERROR_IND: + rc = rsl_rx_rll_err_ind(msg); + break; + case RSL_MT_UNIT_DATA_IND: + LOGP(DRLL, LOGL_NOTICE, "unimplemented Abis RLL message " + "type 0x%02x\n", rllh->c.msg_type); + break; + default: + LOGP(DRLL, LOGL_NOTICE, "unknown Abis RLL message " + "type 0x%02x\n", rllh->c.msg_type); + } + return rc; +} + +static u_int8_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; + } + case GSM48_CMODE_SPEECH_EFR: + switch (lchan->type) { + case GSM_LCHAN_TCH_F: + return 0x01; + /* there's no half-rate EFR */ + default: + 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; + } + default: + break; + } + LOGP(DRSL, LOGL_ERROR, "Cannot determine ip.access speech mode for " + "tch_mode == 0x%02x\n", lchan->tch_mode); + return 0; +} + +static u_int8_t ipa_rtp_pt_for_lchan(struct gsm_lchan *lchan) +{ + struct gsm_network *net = lchan->ts->trx->bts->network; + + /* allow to hardcode the rtp payload */ + if (net->hardcoded_rtp_payload != 0) + return net->hardcoded_rtp_payload; + + 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; + } + 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; + } + case GSM48_CMODE_SPEECH_AMR: + switch (lchan->type) { + case GSM_LCHAN_TCH_F: + return RTP_PT_AMR_FULL; + case GSM_LCHAN_TCH_H: + return RTP_PT_AMR_HALF; + default: + 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; + u_int16_t port, conn_id; + + if (TLVP_PRESENT(tv, RSL_IE_IPAC_LOCAL_IP)) { + ip.s_addr = *((u_int32_t *) TLVP_VAL(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 = *((u_int16_t *) TLVP_VAL(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 = *((u_int16_t *) TLVP_VAL(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 = *((u_int32_t *) TLVP_VAL(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 = *((u_int16_t *) TLVP_VAL(tv, RSL_IE_IPAC_REMOTE_PORT)); + port = ntohs(port); + DEBUGPC(DRSL, "REMOTE_PORT=%u ", port); + lchan->abis_ip.connect_port = port; + } +} + +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 = 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->trx = lchan->ts->trx; + + return abis_rsl_sendmsg(msg); +} + +int rsl_ipacc_mdcx(struct gsm_lchan *lchan, u_int32_t ip, u_int16_t port, + u_int8_t rtp_payload2) +{ + struct msgb *msg = rsl_msgb_alloc(); + struct abis_rsl_dchan_hdr *dh; + u_int32_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 = 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 = (u_int32_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->trx = lchan->ts->trx; + + 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; + u_int8_t msg_type; + + if (act) + msg_type = RSL_MT_IPAC_PDCH_ACT; + else + msg_type = RSL_MT_IPAC_PDCH_DEACT; + + dh = (struct abis_rsl_dchan_hdr *) msgb_put(msg, sizeof(*dh)); + init_dchan_hdr(dh, msg_type); + dh->c.msg_discr = ABIS_RSL_MDISC_DED_CHAN; + dh->chan_nr = ts2chan_nr(ts, 0); + + DEBUGP(DRSL, "%s IPAC_PDCH_%sACT\n", gsm_ts_name(ts), + act ? "" : "DE"); + + msg->trx = ts->trx; + + 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); + + dispatch_signal(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); + dispatch_signal(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)); + + dispatch_signal(SS_ABISIP, S_ABISIP_DLCX_IND, msg->lchan); + + return 0; +} + +static int abis_rsl_rx_ipacc(struct msgb *msg) +{ + struct abis_rsl_rll_hdr *rllh = msgb_l2(msg); + char *ts_name; + int rc = 0; + + msg->lchan = lchan_lookup(msg->trx, rllh->chan_nr); + ts_name = gsm_lchan_name(msg->lchan); + + switch (rllh->c.msg_type) { + case RSL_MT_IPAC_CRCX_ACK: + DEBUGP(DRSL, "%s IPAC_CRCX_ACK ", ts_name); + rc = abis_rsl_rx_ipacc_crcx_ack(msg); + break; + case RSL_MT_IPAC_CRCX_NACK: + /* somehow the BTS was unable to bind the lchan to its local + * port?!? */ + LOGP(DRSL, LOGL_ERROR, "%s IPAC_CRCX_NACK\n", ts_name); + break; + case RSL_MT_IPAC_MDCX_ACK: + /* the BTS tells us that a connect operation was successful */ + DEBUGP(DRSL, "%s IPAC_MDCX_ACK ", ts_name); + rc = abis_rsl_rx_ipacc_mdcx_ack(msg); + break; + case RSL_MT_IPAC_MDCX_NACK: + /* somehow the BTS was unable to connect the lchan to a remote + * port */ + LOGP(DRSL, LOGL_ERROR, "%s IPAC_MDCX_NACK\n", ts_name); + break; + case RSL_MT_IPAC_DLCX_IND: + DEBUGP(DRSL, "%s IPAC_DLCX_IND ", ts_name); + rc = abis_rsl_rx_ipacc_dlcx_ind(msg); + break; + default: + LOGP(DRSL, LOGL_NOTICE, "Unknown ip.access msg_type 0x%02x\n", + rllh->c.msg_type); + break; + } + DEBUGPC(DRSL, "\n"); + + return rc; +} + + +/* Entry-point where L2 RSL from BTS enters */ +int abis_rsl_rcvmsg(struct msgb *msg) +{ + struct abis_rsl_common_hdr *rslh; + int rc = 0; + + if (!msg) { + DEBUGP(DRSL, "Empty RSL msg?..\n"); + return -1; + } + + if (msgb_l2len(msg) < sizeof(*rslh)) { + DEBUGP(DRSL, "Truncated RSL message with l2len: %u\n", msgb_l2len(msg)); + 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); + return -EINVAL; + } + msgb_free(msg); + return rc; +} + +/* From Table 10.5.33 of GSM 04.08 */ +int rsl_number_of_paging_subchannels(struct gsm_bts *bts) +{ + if (bts->si_common.chan_desc.ccch_conf == RSL_BCCH_CCCH_CONF_1_C) { + return MAX(1, (3 - bts->si_common.chan_desc.bs_ag_blks_res)) + * (bts->si_common.chan_desc.bs_pa_mfrms + 2); + } else { + return (9 - bts->si_common.chan_desc.bs_ag_blks_res) + * (bts->si_common.chan_desc.bs_pa_mfrms + 2); + } +} + +int rsl_sms_cb_command(struct gsm_bts *bts, uint8_t chan_number, + uint8_t 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->chan_nr = RSL_CHAN_SDCCH4_ACCH; /* TODO: check the chan config */ + + msgb_tv_put(cb_cmd, RSL_IE_CB_CMD_TYPE, cb_command); + msgb_tlv_put(cb_cmd, RSL_IE_SMSCB_MSG, len, data); + + cb_cmd->trx = bts->c0; + + return abis_rsl_sendmsg(cb_cmd); +} diff --git a/openbsc/src/libbsc/bsc_api.c b/openbsc/src/libbsc/bsc_api.c new file mode 100644 index 000000000..0f09aecd3 --- /dev/null +++ b/openbsc/src/libbsc/bsc_api.c @@ -0,0 +1,675 @@ +/* GSM 08.08 like API for OpenBSC. The bridge from MSC to BSC */ + +/* (C) 2010 by Holger Hans Peter Freyther + * (C) 2010 by On-Waves + * (C) 2009 by Harald Welte + * + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include + +#define GSM0808_T10_VALUE 6, 0 + +static LLIST_HEAD(sub_connections); + +static void rll_ind_cb(struct gsm_lchan *, uint8_t, void *, enum bsc_rllr_ind); +static void send_sapi_reject(struct gsm_subscriber_connection *conn, int link_id); +static void handle_release(struct gsm_subscriber_connection *conn, struct bsc_api *bsc, struct gsm_lchan *lchan); +static void handle_chan_ack(struct gsm_subscriber_connection *conn, struct bsc_api *bsc, struct gsm_lchan *lchan); +static void handle_chan_nack(struct gsm_subscriber_connection *conn, struct bsc_api *bsc, struct gsm_lchan *lchan); + +/* GSM 08.08 3.2.2.33 */ +static u_int8_t lchan_to_chosen_channel(struct gsm_lchan *lchan) +{ + u_int8_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: + LOGP(DMSC, LOGL_ERROR, "Unknown lchan type: %p\n", lchan); + break; + } + + return channel_mode << 4 | channel; +} + +static u_int8_t chan_mode_to_speech(struct gsm_lchan *lchan) +{ + int mode = 0; + + switch (lchan->tch_mode) { + case GSM48_CMODE_SPEECH_V1: + mode = 1; + break; + case GSM48_CMODE_SPEECH_EFR: + mode = 0x11; + break; + case GSM48_CMODE_SPEECH_AMR: + mode = 0x21; + break; + case GSM48_CMODE_SIGN: + case GSM48_CMODE_DATA_14k5: + case GSM48_CMODE_DATA_12k0: + case GSM48_CMODE_DATA_6k0: + case GSM48_CMODE_DATA_3k6: + default: + LOGP(DMSC, LOGL_ERROR, "Using non speech mode: %d\n", mode); + return 0; + break; + } + + /* assume to always do AMR HR on any TCH type */ + if (lchan->type == GSM_LCHAN_TCH_H || + lchan->tch_mode == GSM48_CMODE_SPEECH_AMR) + mode |= 0x4; + + return mode; +} + +static void assignment_t10_timeout(void *_conn) +{ + struct bsc_api *api; + struct gsm_subscriber_connection *conn = + (struct gsm_subscriber_connection *) _conn; + + LOGP(DMSC, LOGL_ERROR, "Assigment T10 timeout on %p\n", conn); + + /* normal release on the secondary channel */ + lchan_release(conn->secondary_lchan, 0, 1); + conn->secondary_lchan = NULL; + + /* inform them about the failure */ + api = conn->bts->network->bsc_api; + api->assign_fail(conn, GSM0808_CAUSE_NO_RADIO_RESOURCE_AVAILABLE, NULL); +} + +/* + * 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; + + /* 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) { + new_lchan->mr_conf.ver = 1; + new_lchan->mr_conf.icmi = 1; + new_lchan->mr_conf.m5_90 = 1; + } + + if (rsl_chan_activate_lchan(new_lchan, 0x1, 0, 0) < 0) { + LOGP(DHO, LOGL_ERROR, "could not activate channel\n"); + lchan_free(new_lchan); + return -1; + } + + /* remember that we have the channel */ + conn->secondary_lchan = new_lchan; + new_lchan->conn = conn; + + rsl_lchan_set_state(new_lchan, LCHAN_S_ACT_REQ); + return 0; +} + +struct gsm_subscriber_connection *subscr_con_allocate(struct gsm_lchan *lchan) +{ + struct gsm_subscriber_connection *conn; + + conn = talloc_zero(lchan->ts->trx->bts->network, struct gsm_subscriber_connection); + if (!conn) + return NULL; + + /* Configure the time and start it so it will be closed */ + conn->lchan = lchan; + conn->bts = lchan->ts->trx->bts; + lchan->conn = conn; + llist_add_tail(&conn->entry, &sub_connections); + return conn; +} + +/* TODO: move subscriber put here... */ +void subscr_con_free(struct gsm_subscriber_connection *conn) +{ + if (!conn) + return; + + + if (conn->subscr) { + subscr_put(conn->subscr); + conn->subscr = NULL; + } + + + if (conn->ho_lchan) { + LOGP(DNM, LOGL_ERROR, "The ho_lchan should have been cleared.\n"); + conn->ho_lchan->conn = NULL; + } + + if (conn->lchan) { + LOGP(DNM, LOGL_ERROR, "The lchan should have been cleared.\n"); + conn->lchan->conn = NULL; + } + + if (conn->secondary_lchan) { + LOGP(DNM, LOGL_ERROR, "The secondary_lchan should have been cleared.\n"); + conn->secondary_lchan->conn = NULL; + } + + llist_del(&conn->entry); + talloc_free(conn); +} + +int bsc_api_init(struct gsm_network *network, struct bsc_api *api) +{ + network->bsc_api = api; + return 0; +} + +int gsm0808_submit_dtap(struct gsm_subscriber_connection *conn, + struct msgb *msg, int link_id, int allow_sach) +{ + 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->trx = msg->lchan->ts->trx; + + /* If we are on a TCH and need to submit a SMS (on SAPI=3) we need to use the SACH */ + if (allow_sach && sapi != 0) { + if (conn->lchan->type == GSM_LCHAN_TCH_F || conn->lchan->type == GSM_LCHAN_TCH_H) + link_id |= 0x40; + } + + msg->l3h = msg->data; + if (conn->lchan->sapis[sapi] == LCHAN_SAPI_UNUSED) { + 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 { + return rsl_data_request(msg, link_id); + } +} + +/** + * 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 and that when we have a TCH it is capable of + * handling the audio codec. On top of that it is assumed that we are using + * AMR 5.9 when assigning a TCH/H. + */ +int gsm0808_assign_req(struct gsm_subscriber_connection *conn, int chan_mode, int full_rate) +{ + struct bsc_api *api; + api = conn->bts->network->bsc_api; + + if (conn->lchan->type == GSM_LCHAN_SDCCH) { + if (handle_new_assignment(conn, chan_mode, full_rate) != 0) + goto error; + } else { + LOGP(DMSC, LOGL_NOTICE, + "Sending ChanModify for speech %d %d\n", chan_mode, full_rate); + if (chan_mode == GSM48_CMODE_SPEECH_AMR) { + conn->lchan->mr_conf.ver = 1; + conn->lchan->mr_conf.icmi = 1; + conn->lchan->mr_conf.m5_90 = 1; + } + + gsm48_lchan_modify(conn->lchan, chan_mode); + } + + /* we will now start the timer to complete the assignment */ + conn->T10.cb = assignment_t10_timeout; + conn->T10.data = conn; + bsc_schedule_timer(&conn->T10, GSM0808_T10_VALUE); + return 0; + +error: + api->assign_fail(conn, 0, NULL); + return -1; +} + +int gsm0808_page(struct gsm_bts *bts, unsigned int page_group, unsigned int mi_len, + uint8_t *mi, int chan_type) +{ + return rsl_paging_cmd(bts, page_group, mi_len, mi, chan_type); +} + +static void handle_ass_compl(struct gsm_subscriber_connection *conn, + struct msgb *msg) +{ + struct gsm48_hdr *gh; + struct bsc_api *api = conn->bts->network->bsc_api; + + if (conn->secondary_lchan != msg->lchan) { + LOGP(DMSC, LOGL_ERROR, "Assignment Compl should occur on second lchan.\n"); + return; + } + + gh = msgb_l3(msg); + if (msgb_l3len(msg) - sizeof(*gh) != 1) { + LOGP(DMSC, LOGL_ERROR, "Assignment Compl invalid: %lu\n", + msgb_l3len(msg) - sizeof(*gh)); + return; + } + + /* swap channels */ + bsc_del_timer(&conn->T10); + + lchan_release(conn->lchan, 0, 1); + conn->lchan = conn->secondary_lchan; + conn->secondary_lchan = NULL; + + if (is_ipaccess_bts(conn->bts) && conn->lchan->tch_mode != GSM48_CMODE_SIGN) + rsl_ipacc_crcx(conn->lchan); + + api->assign_compl(conn, gh->data[0], + lchan_to_chosen_channel(conn->lchan), + conn->lchan->encr.alg_id, + chan_mode_to_speech(conn->lchan)); +} + +static void handle_ass_fail(struct gsm_subscriber_connection *conn, + struct msgb *msg) +{ + struct bsc_api *api = conn->bts->network->bsc_api; + uint8_t *rr_failure; + struct gsm48_hdr *gh; + + + if (conn->lchan != msg->lchan) { + LOGP(DMSC, LOGL_ERROR, "Assignment failure should occur on primary lchan.\n"); + return; + } + + /* stop the timer and release it */ + bsc_del_timer(&conn->T10); + lchan_release(conn->secondary_lchan, 0, 1); + conn->secondary_lchan = NULL; + + gh = msgb_l3(msg); + if (msgb_l3len(msg) - sizeof(*gh) != 1) { + LOGP(DMSC, LOGL_ERROR, "assignemnt failure unhandled: %lu\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 dispatch_dtap(struct gsm_subscriber_connection *conn, + uint8_t link_id, struct msgb *msg) +{ + struct bsc_api *api = msg->lchan->ts->trx->bts->network->bsc_api; + struct gsm48_hdr *gh; + uint8_t pdisc; + int rc; + + if (msgb_l3len(msg) < sizeof(*gh)) { + LOGP(DMSC, LOGL_ERROR, "Message too short for a GSM48 header.\n"); + return; + } + + gh = msgb_l3(msg); + pdisc = gh->proto_discr & 0x0f; + switch (pdisc) { + case GSM48_PDISC_RR: + switch (gh->msg_type) { + case GSM48_MT_RR_CIPH_M_COMPL: + if (api->cipher_mode_compl) + return 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: + bsc_del_timer(&conn->T10); + rc = gsm48_rx_rr_modif_ack(msg); + if (rc < 0 && api->assign_fail) { + api->assign_fail(conn, + GSM0808_CAUSE_NO_RADIO_RESOURCE_AVAILABLE, + NULL); + } else if (rc >= 0 && api->assign_compl) + api->assign_compl(conn, 0, + lchan_to_chosen_channel(conn->lchan), + conn->lchan->encr.alg_id, + chan_mode_to_speech(conn->lchan)); + return; + break; + } + break; + case GSM48_PDISC_MM: + break; + } + + /* default case */ + if (api->dtap) + api->dtap(conn, link_id, msg); +} + +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) { + dispatch_dtap(lchan->conn, link_id, msg); + } else { + rc = BSC_API_CONN_POL_REJECT; + lchan->conn = subscr_con_allocate(msg->lchan); + if (!lchan->conn) { + lchan_release(lchan, 0, 0); + return -1; + } + + rc = api->compl_l3(lchan->conn, msg, 0); + + if (rc != BSC_API_CONN_POL_ACCEPT) { + lchan->conn->lchan = NULL; + subscr_con_free(lchan->conn); + lchan_release(lchan, 0, 0); + } + } + + return 0; +} + +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, 1); + + if (conn->lchan) + lchan_release(conn->lchan, 1, 0); + + conn->lchan = NULL; + conn->secondary_lchan = NULL; + conn->ho_lchan = NULL; + conn->bts = NULL; + + bsc_del_timer(&conn->T10); + + return 0; +} + +static void send_sapi_reject(struct gsm_subscriber_connection *conn, int link_id) +{ + struct bsc_api *api; + + if (!conn) + return; + + api = conn->bts->network->bsc_api; + if (!api || !api->sapi_n_reject) + return; + + api->sapi_n_reject(conn, link_id); +} + +static void rll_ind_cb(struct gsm_lchan *lchan, uint8_t link_id, void *_data, enum bsc_rllr_ind rllr_ind) +{ + struct msgb *msg = _data; + + /* + * There seems to be a small window that the RLL timer can + * fire after a lchan_release call and before the S_CHALLOC_FREED + * is called. Check if a conn is set before proceeding. + */ + if (!lchan->conn) + return; + + switch (rllr_ind) { + case BSC_RLLR_IND_EST_CONF: + rsl_data_request(msg, OBSC_LINKID_CB(msg)); + break; + case BSC_RLLR_IND_REL_IND: + case BSC_RLLR_IND_ERR_IND: + case BSC_RLLR_IND_TIMEOUT: + send_sapi_reject(lchan->conn, OBSC_LINKID_CB(msg)); + msgb_free(msg); + break; + } +} + +static int bsc_handle_lchan_signal(unsigned int subsys, unsigned int signal, + void *handler_data, void *signal_data) +{ + struct bsc_api *bsc; + struct gsm_lchan *lchan; + struct lchan_signal_data *lchan_data; + + if (subsys != SS_LCHAN) + return 0; + + + lchan_data = signal_data; + if (!lchan_data->lchan || !lchan_data->lchan->conn) + return 0; + + lchan = lchan_data->lchan; + bsc = lchan->ts->trx->bts->network->bsc_api; + if (!bsc) + return 0; + + switch (signal) { + case S_LCHAN_UNEXPECTED_RELEASE: + handle_release(lchan->conn, bsc, lchan); + break; + case S_LCHAN_ACTIVATE_ACK: + handle_chan_ack(lchan->conn, bsc, lchan); + break; + case S_LCHAN_ACTIVATE_NACK: + handle_chan_nack(lchan->conn, bsc, lchan); + break; + } + + return 0; +} + +static void handle_release(struct gsm_subscriber_connection *conn, + struct bsc_api *bsc, struct gsm_lchan *lchan) +{ + int destruct = 1; + + if (conn->secondary_lchan == lchan) { + bsc_del_timer(&conn->T10); + conn->secondary_lchan = NULL; + + bsc->assign_fail(conn, + GSM0808_CAUSE_RADIO_INTERFACE_FAILURE, + NULL); + } + + /* clear the connection now */ + if (bsc->clear_request) + destruct = bsc->clear_request(conn, 0); + + /* now give up all channels */ + if (conn->lchan == lchan) + conn->lchan = NULL; + if (conn->ho_lchan == lchan) { + bsc_clear_handover(conn, 0); + conn->ho_lchan = NULL; + } + lchan->conn = NULL; + + gsm0808_clear(conn); + + if (destruct) + subscr_con_free(conn); +} + +static void handle_chan_ack(struct gsm_subscriber_connection *conn, + struct bsc_api *api, struct gsm_lchan *lchan) +{ + if (conn->secondary_lchan != lchan) + return; + + LOGP(DMSC, LOGL_NOTICE, "Sending assignment on chan: %p\n", lchan); + gsm48_send_rr_ass_cmd(conn->lchan, lchan, 0x3); +} + +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) +{ + register_signal_handler(SS_LCHAN, bsc_handle_lchan_signal, NULL); +} diff --git a/openbsc/src/libbsc/bsc_init.c b/openbsc/src/libbsc/bsc_init.c new file mode 100644 index 000000000..752056c37 --- /dev/null +++ b/openbsc/src/libbsc/bsc_init.c @@ -0,0 +1,440 @@ +/* A hackish minimal BSC (+MSC +HLR) implementation */ + +/* (C) 2008-2010 by Harald Welte + * (C) 2009 by Holger Hans Peter Freyther + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* global pointer to the gsm network data structure */ +extern struct gsm_network *bsc_gsmnet; + +static void patch_nm_tables(struct gsm_bts *bts); + +/* Callback function for NACK on the OML NM */ +static int oml_msg_nack(struct nm_nack_signal_data *nack) +{ + int i; + + if (nack->mt == NM_MT_SET_BTS_ATTR_NACK) { + + LOGP(DNM, LOGL_FATAL, "Failed to set BTS attributes. That is fatal. " + "Was the bts type and frequency properly specified?\n"); + exit(-1); + } else { + LOGP(DNM, LOGL_ERROR, "Got a NACK going to drop the OML links.\n"); + for (i = 0; i < bsc_gsmnet->num_bts; ++i) { + struct gsm_bts *bts = gsm_bts_num(bsc_gsmnet, i); + if (is_ipaccess_bts(bts)) + ipaccess_drop_oml(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); + dispatch_signal(SS_GLOBAL, S_GLOBAL_BTS_CLOSE_OM, bts); + } + + return 0; +} + +static int generate_and_rsl_si(struct gsm_bts_trx *trx, enum osmo_sysinfo_type i) +{ + struct gsm_bts *bts = trx->bts; + int rc; + + /* Only generate SI if this SI is not in "static" (user-defined) mode */ + if (!(bts->si_mode_static & (1 << i))) { + rc = gsm_generate_si(bts, i); + if (rc < 0) + return rc; + } + + DEBUGP(DRR, "SI%s: %s\n", gsm_sitype_name(i), + 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, gsm_sitype2rsl(i), + GSM_BTS_SI(bts, i), rc); + break; + default: + rc = rsl_bcch_info(trx, gsm_sitype2rsl(i), + GSM_BTS_SI(bts, i), rc); + break; + } + + return rc; +} + +/* set all system information types */ +static int set_system_infos(struct gsm_bts_trx *trx) +{ + int i, rc; + struct gsm_bts *bts = trx->bts; + + bts->si_common.cell_sel_par.ms_txpwr_max_ccch = + ms_pwr_ctl_lvl(bts->band, bts->ms_max_power); + bts->si_common.cell_sel_par.neci = bts->network->neci; + + /* First, we determine which of the SI messages we actually need */ + + if (trx == bts->c0) { + /* 1...4 are always present on a C0 TRX */ + for (i = SYSINFO_TYPE_1; i <= SYSINFO_TYPE_4; i++) + bts->si_valid |= (1 << i); + + /* 13 is always present on a C0 TRX of a GPRS BTS */ + if (bts->gprs.mode != BTS_GPRS_NONE) + bts->si_valid |= (1 << SYSINFO_TYPE_13); + } + + /* 5 and 6 are always present on every TRX */ + bts->si_valid |= (1 << SYSINFO_TYPE_5); + bts->si_valid |= (1 << SYSINFO_TYPE_6); + + /* Second, we generate and send the selected SI via RSL */ + for (i = SYSINFO_TYPE_1; i < _MAX_SYSINFO_TYPE; i++) { + if (!(bts->si_valid & (1 << i))) + continue; + + rc = generate_and_rsl_si(trx, i); + if (rc < 0) + goto err_out; + } + + return 0; +err_out: + LOGP(DRR, LOGL_ERROR, "Cannot generate SI %u for BTS %u, most likely " + "a problem with neighbor cell list generation\n", + i, bts->nr); + return rc; +} + +/* 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 = 1; 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 = 1; i < 1024; i++) { + if (!bitvec_get_bit_pos(cell_chan, i)) + continue; + /* set the corresponding bit in the MA */ + bitnum = (ts->hopping.ma_len * 8) - 1 - n_chan; + if (bitvec_get_bit_pos(ts_arfcn, i)) + bitvec_set_bit_pos(ma, bitnum, 1); + else + bitvec_set_bit_pos(ma, bitnum, 0); + n_chan++; + } + + /* ARFCN 0 is special: It is coded last in the bitmask */ + if (bitvec_get_bit_pos(cell_chan, 0)) { + n_chan++; + /* set the corresponding bit in the MA */ + bitnum = (ts->hopping.ma_len * 8) - 1 - n_chan; + if (bitvec_get_bit_pos(ts_arfcn, 0)) + bitvec_set_bit_pos(ma, bitnum, 1); + else + bitvec_set_bit_pos(ma, bitnum, 0); + } + + return 0; +} + +static void bootstrap_rsl(struct gsm_bts_trx *trx) +{ + unsigned int i; + + LOGP(DRSL, LOGL_NOTICE, "bootstrapping RSL for BTS/TRX (%u/%u) " + "on ARFCN %u using MCC=%u MNC=%u LAC=%u CID=%u BSIC=%u TSC=%u\n", + trx->bts->nr, trx->nr, trx->arfcn, bsc_gsmnet->country_code, + bsc_gsmnet->network_code, trx->bts->location_area_code, + trx->bts->cell_identity, trx->bts->bsic, trx->bts->tsc); + set_system_infos(trx); + + for (i = 0; i < ARRAY_SIZE(trx->ts); i++) + generate_ma_for_ts(&trx->ts[i]); +} + +/* Callback function to be called every time we receive a signal from INPUT */ +static int inp_sig_cb(unsigned int subsys, unsigned int signal, + void *handler_data, void *signal_data) +{ + struct input_signal_data *isd = signal_data; + struct gsm_bts_trx *trx = isd->trx; + int ts_no, lchan_no; + + if (subsys != SS_INPUT) + return -EINVAL; + + switch (signal) { + case S_INP_TEI_UP: + if (isd->link_type == E1INP_SIGN_RSL) + bootstrap_rsl(trx); + break; + case S_INP_TEI_DN: + LOGP(DMI, LOGL_ERROR, "Lost some E1 TEI link: %d %p\n", isd->link_type, trx); + + if (isd->link_type == E1INP_SIGN_OML) + counter_inc(trx->bts->network->stats.bts.oml_fail); + else if (isd->link_type == E1INP_SIGN_RSL) + counter_inc(trx->bts->network->stats.bts.rsl_fail); + + /* + * free all allocated channels. change the nm_state so the + * trx and trx_ts becomes unusable and chan_alloc.c can not + * allocate from it. + */ + for (ts_no = 0; ts_no < ARRAY_SIZE(trx->ts); ++ts_no) { + struct gsm_bts_trx_ts *ts = &trx->ts[ts_no]; + + for (lchan_no = 0; lchan_no < ARRAY_SIZE(ts->lchan); ++lchan_no) { + if (ts->lchan[lchan_no].state != LCHAN_S_NONE) + lchan_free(&ts->lchan[lchan_no]); + lchan_reset(&ts->lchan[lchan_no]); + } + + ts->nm_state.operational = 0; + ts->nm_state.availability = 0; + } + + trx->nm_state.operational = 0; + trx->nm_state.availability = 0; + trx->bb_transc.nm_state.operational = 0; + trx->bb_transc.nm_state.availability = 0; + + abis_nm_clear_queue(trx->bts); + break; + default: + break; + } + + return 0; +} + +static int bootstrap_bts(struct gsm_bts *bts) +{ + int i, n; + + /* 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 < 1 || + (bts->c0->arfcn > 124 && bts->c0->arfcn < 955) || + bts->c0->arfcn > 1023) { + LOGP(DNM, LOGL_ERROR, "GSM900 channel must be between 1-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 */ + bts->si_common.chan_desc.att = 1; + bts->si_common.chan_desc.bs_pa_mfrms = RSL_BS_PA_MFRMS_5; + bts->si_common.chan_desc.bs_ag_blks_res = 1; + + /* 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; + 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; + } + + /* some defaults for our system information */ + bts->si_common.cell_options.radio_link_timeout = 7; /* 12 */ + + /* allow/disallow DTXu */ + if (bts->network->dtx_enabled) + bts->si_common.cell_options.dtx = 0; + else + bts->si_common.cell_options.dtx = 2; + + bts->si_common.cell_options.pwrc = 0; /* PWRC not set */ + + bts->si_common.cell_sel_par.acs = 0; + + bts->si_common.ncc_permitted = 0xff; + + paging_init(bts); + + return 0; +} + +int bsc_bootstrap_network(int (*mncc_recv)(struct gsm_network *, struct msgb *), + const char *config_file) +{ + struct telnet_connection dummy_conn; + struct gsm_bts *bts; + int rc; + + /* initialize our data structures */ + bsc_gsmnet = gsm_network_init(1, 1, mncc_recv); + if (!bsc_gsmnet) + return -ENOMEM; + + bsc_gsmnet->name_long = talloc_strdup(bsc_gsmnet, "OpenBSC"); + bsc_gsmnet->name_short = talloc_strdup(bsc_gsmnet, "OpenBSC"); + + /* our vty command code expects vty->priv to point to a telnet_connection */ + dummy_conn.priv = bsc_gsmnet; + rc = vty_read_config_file(config_file, &dummy_conn); + if (rc < 0) { + LOGP(DNM, LOGL_FATAL, "Failed to parse the config file: '%s'\n", config_file); + return rc; + } + + rc = telnet_init(tall_bsc_ctx, bsc_gsmnet, 4242); + if (rc < 0) + return rc; + + register_signal_handler(SS_NM, nm_sig_cb, NULL); + register_signal_handler(SS_INPUT, inp_sig_cb, NULL); + + llist_for_each_entry(bts, &bsc_gsmnet->bts_list, list) { + bootstrap_bts(bts); + if (!is_ipaccess_bts(bts)) + rc = e1_reconfig_bts(bts); + + if (rc < 0) { + fprintf(stderr, "Error in E1 input driver setup\n"); + exit (1); + } + } + + /* initialize nanoBTS support omce */ + rc = ipaccess_setup(bsc_gsmnet); + + return 0; +} diff --git a/openbsc/src/libbsc/bsc_msc.c b/openbsc/src/libbsc/bsc_msc.c new file mode 100644 index 000000000..508697ab1 --- /dev/null +++ b/openbsc/src/libbsc/bsc_msc.c @@ -0,0 +1,259 @@ +/* Routines to talk to the MSC using the IPA Protocol */ +/* + * (C) 2010 by Holger Hans Peter Freyther + * (C) 2010 by On-Waves + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + * + */ + +#include +#include +#include + +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +static void connection_loss(struct bsc_msc_connection *con) +{ + struct bsc_fd *fd; + + fd = &con->write_queue.bfd; + + close(fd->fd); + fd->fd = -1; + fd->cb = write_queue_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 Connection timeout.\n"); + bsc_msc_lost(con); +} + +/* called in the case of a non blocking connect */ +static int msc_connection_connect(struct bsc_fd *fd, unsigned int what) +{ + int rc; + int val; + struct bsc_msc_connection *con; + struct write_queue *queue; + + socklen_t len = sizeof(val); + + if ((what & BSC_FD_WRITE) == 0) { + LOGP(DMSC, LOGL_ERROR, "Callback but not writable.\n"); + return -1; + } + + queue = container_of(fd, struct write_queue, bfd); + con = container_of(queue, struct bsc_msc_connection, write_queue); + + /* From here on we will either be connected or reconnect */ + bsc_del_timer(&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 socket failed.\n"); + goto error; + } + if (val != 0) { + LOGP(DMSC, LOGL_ERROR, "Not connected to the MSC: %d\n", val); + goto error; + } + + + /* go to full operation */ + fd->cb = write_queue_bfd_cb; + fd->when = BSC_FD_READ | BSC_FD_EXCEPT; + + con->is_connected = 1; + LOGP(DMSC, LOGL_NOTICE, "(Re)Connected to the MSC.\n"); + if (con->connected) + con->connected(con); + return 0; + +error: + bsc_unregister_fd(fd); + connection_loss(con); + return -1; +} +static void setnonblocking(struct bsc_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_fd *fd; + struct sockaddr_in sin; + int on = 1, ret; + + LOGP(DMSC, LOGL_NOTICE, "Attempting to connect MSC at %s:%d\n", con->ip, con->port); + + con->is_connected = 0; + + 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, + &con->prio, sizeof(con->prio)); + if (ret != 0) + LOGP(DMSC, LOGL_ERROR, "Failed to set prio to %d. %s\n", + con->prio, strerror(errno)); + + memset(&sin, 0, sizeof(sin)); + sin.sin_family = AF_INET; + sin.sin_port = htons(con->port); + inet_aton(con->ip, &sin.sin_addr); + + setsockopt(fd->fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)); + ret = connect(fd->fd, (struct sockaddr *) &sin, sizeof(sin)); + + if (ret == -1 && errno == EINPROGRESS) { + LOGP(DMSC, LOGL_ERROR, "MSC Connection in progress\n"); + fd->when = BSC_FD_WRITE; + fd->cb = msc_connection_connect; + con->timeout_timer.cb = msc_con_timeout; + con->timeout_timer.data = con; + bsc_schedule_timer(&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 = write_queue_bfd_cb; + con->is_connected = 1; + if (con->connected) + con->connected(con); + } + + ret = bsc_register_fd(fd); + if (ret < 0) { + perror("Registering the fd failed"); + close(fd->fd); + return ret; + } + + return ret; +} + +struct bsc_msc_connection *bsc_msc_create(const char *ip, int port, int prio) +{ + 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->ip = ip; + con->port = port; + con->prio = prio; + write_queue_init(&con->write_queue, 100); + return con; +} + +void bsc_msc_lost(struct bsc_msc_connection *con) +{ + write_queue_clear(&con->write_queue); + bsc_del_timer(&con->timeout_timer); + + if (con->write_queue.bfd.fd >= 0) + bsc_unregister_fd(&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.\n"); + bsc_msc_connect(con); +} + +void bsc_msc_schedule_connect(struct bsc_msc_connection *con) +{ + LOGP(DMSC, LOGL_NOTICE, "Attempting to reconnect to the MSC.\n"); + con->reconnect_timer.cb = reconnect_msc; + con->reconnect_timer.data = con; + bsc_schedule_timer(&con->reconnect_timer, 5, 0); +} + +struct msgb *bsc_msc_id_get_resp(const char *token) +{ + 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; + } + + msg->l2h = msgb_v_put(msg, IPAC_MSGT_ID_RESP); + msgb_l16tv_put(msg, strlen(token) + 1, + IPAC_IDTAG_UNITNAME, (u_int8_t *) token); + return msg; +} diff --git a/openbsc/src/libbsc/bsc_rll.c b/openbsc/src/libbsc/bsc_rll.c new file mode 100644 index 000000000..722f3fafc --- /dev/null +++ b/openbsc/src/libbsc/bsc_rll.c @@ -0,0 +1,141 @@ +/* GSM BSC Radio Link Layer API + * 3GPP TS 08.58 version 8.6.0 Release 1999 / ETSI TS 100 596 V8.6.0 */ + +/* (C) 2009 by Harald Welte + * + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + * + */ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +struct bsc_rll_req { + struct llist_head list; + struct timer_list timer; + + struct gsm_lchan *lchan; + u_int8_t link_id; + + void (*cb)(struct gsm_lchan *lchan, u_int8_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, u_int8_t sapi, + void (*cb)(struct gsm_lchan *, u_int8_t, void *, + enum bsc_rllr_ind), + void *data) +{ + struct bsc_rll_req *rllr = talloc_zero(tall_bsc_ctx, struct bsc_rll_req); + u_int8_t link_id; + if (!rllr) + return -ENOMEM; + + link_id = sapi; + + /* If we are a TCH and not in signalling mode, we need to + * indicate that the new RLL connection is to be made on the SACCH */ + if ((lchan->type == GSM_LCHAN_TCH_F || + lchan->type == GSM_LCHAN_TCH_H) && sapi != 0) + link_id |= 0x40; + + rllr->lchan = lchan; + rllr->link_id = link_id; + rllr->cb = cb; + rllr->data = data; + + llist_add(&rllr->list, &bsc_rll_reqs); + + rllr->timer.cb = &timer_cb; + rllr->timer.data = rllr; + + bsc_schedule_timer(&rllr->timer, 10, 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, u_int8_t link_id, u_int8_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)) { + bsc_del_timer(&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) { + bsc_del_timer(&rllr->timer); + complete_rllr(rllr, BSC_RLLR_IND_ERR_IND); + } + } + + return 0; +} + +static __attribute__((constructor)) void on_dso_load_rll(void) +{ + register_signal_handler(SS_CHALLOC, rll_lchan_signal, NULL); +} diff --git a/openbsc/src/libbsc/bsc_vty.c b/openbsc/src/libbsc/bsc_vty.c new file mode 100644 index 000000000..ed45afd4d --- /dev/null +++ b/openbsc/src/libbsc/bsc_vty.c @@ -0,0 +1,2773 @@ +/* OpenBSC interface to quagga VTY */ +/* (C) 2009-2010 by Harald Welte + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + * + */ + +#include +#include +#include + +#include +#include +#include +#include +#include + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "../bscconfig.h" + +/* 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 } +}; + +struct cmd_node net_node = { + GSMNET_NODE, + "%s(network)#", + 1, +}; + +struct cmd_node bts_node = { + BTS_NODE, + "%s(bts)#", + 1, +}; + +struct cmd_node trx_node = { + TRX_NODE, + "%s(trx)#", + 1, +}; + +struct cmd_node ts_node = { + TS_NODE, + "%s(ts)#", + 1, +}; + +extern struct gsm_network *bsc_gsmnet; + +struct gsm_network *gsmnet_from_vty(struct vty *v) +{ + /* In case we read from the config file, the vty->priv cannot + * point to a struct telnet_connection, and thus conn->priv + * will not point to the gsm_network structure */ +#if 0 + struct telnet_connection *conn = v->priv; + return (struct gsm_network *) conn->priv; +#else + return bsc_gsmnet; +#endif +} + +static int dummy_config_write(struct vty *v) +{ + return CMD_SUCCESS; +} + +static void net_dump_nmstate(struct vty *vty, struct gsm_nm_state *nms) +{ + vty_out(vty,"Oper '%s', Admin %u, Avail '%s'%s", + nm_opstate_name(nms->operational), nms->administrative, + nm_avail_name(nms->availability), VTY_NEWLINE); +} + +static void dump_pchan_load_vty(struct vty *vty, char *prefix, + const struct pchan_load *pl) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(pl->pchan); i++) { + const struct load_counter *lc = &pl->pchan[i]; + unsigned int percent; + + if (lc->total == 0) + continue; + + percent = (lc->used * 100) / lc->total; + + vty_out(vty, "%s%20s: %3u%% (%u/%u)%s", prefix, + gsm_pchan_name(i), percent, lc->used, lc->total, + VTY_NEWLINE); + } +} + +static void net_dump_vty(struct vty *vty, struct gsm_network *net) +{ + struct pchan_load pl; + + vty_out(vty, "BSC is on Country Code %u, Network Code %u " + "and has %u BTS%s", net->country_code, net->network_code, + net->num_bts, VTY_NEWLINE); + vty_out(vty, " Long network name: '%s'%s", + net->name_long, VTY_NEWLINE); + vty_out(vty, " Short network name: '%s'%s", + net->name_short, VTY_NEWLINE); + vty_out(vty, " Authentication policy: %s%s", + gsm_auth_policy_name(net->auth_policy), VTY_NEWLINE); + vty_out(vty, " Location updating reject cause: %u%s", + net->reject_cause, VTY_NEWLINE); + vty_out(vty, " Encryption: A5/%u%s", net->a5_encryption, + VTY_NEWLINE); + vty_out(vty, " NECI (TCH/H): %u%s", net->neci, + VTY_NEWLINE); + vty_out(vty, " Use TCH for Paging any: %d%s", net->pag_any_tch, + VTY_NEWLINE); + vty_out(vty, " RRLP Mode: %s%s", rrlp_mode_name(net->rrlp.mode), + VTY_NEWLINE); + vty_out(vty, " MM Info: %s%s", net->send_mm_info ? "On" : "Off", + VTY_NEWLINE); + vty_out(vty, " Handover: %s%s", net->handover.active ? "On" : "Off", + VTY_NEWLINE); + network_chan_load(&pl, net); + vty_out(vty, " Current Channel Load:%s", VTY_NEWLINE); + dump_pchan_load_vty(vty, " ", &pl); + + /* show rf */ + if (net->msc_data) + vty_out(vty, " Last RF Command: %s%s", + net->msc_data->rf_ctl->last_state_command, + VTY_NEWLINE); +} + +DEFUN(show_net, show_net_cmd, "show network", + SHOW_STR "Display information about a GSM NETWORK\n") +{ + struct gsm_network *net = gsmnet_from_vty(vty); + net_dump_vty(vty, net); + + return CMD_SUCCESS; +} + +static void e1isl_dump_vty(struct vty *vty, struct e1inp_sign_link *e1l) +{ + struct e1inp_line *line; + + if (!e1l) { + vty_out(vty, " None%s", VTY_NEWLINE); + return; + } + + line = e1l->ts->line; + + vty_out(vty, " E1 Line %u, Type %s: Timeslot %u, Mode %s%s", + line->num, line->driver->name, e1l->ts->num, + e1inp_signtype_name(e1l->type), VTY_NEWLINE); + vty_out(vty, " E1 TEI %u, SAPI %u%s", + e1l->tei, e1l->sapi, VTY_NEWLINE); +} + +static void bts_dump_vty(struct vty *vty, struct gsm_bts *bts) +{ + struct pchan_load pl; + + vty_out(vty, "BTS %u is of %s type in band %s, has CI %u LAC %u, " + "BSIC %u, TSC %u and %u TRX%s", + bts->nr, btstype2str(bts->type), gsm_band_name(bts->band), + bts->cell_identity, + bts->location_area_code, bts->bsic, bts->tsc, + bts->num_trx, VTY_NEWLINE); + vty_out(vty, "Description: %s%s", + bts->description ? bts->description : "(null)", VTY_NEWLINE); + vty_out(vty, "MS Max power: %u dBm%s", bts->ms_max_power, VTY_NEWLINE); + vty_out(vty, "Minimum Rx Level for Access: %i dBm%s", + rxlev2dbm(bts->si_common.cell_sel_par.rxlev_acc_min), + VTY_NEWLINE); + vty_out(vty, "Cell Reselection Hysteresis: %u dBm%s", + bts->si_common.cell_sel_par.cell_resel_hyst*2, VTY_NEWLINE); + vty_out(vty, "RACH TX-Integer: %u%s", bts->si_common.rach_control.tx_integer, + VTY_NEWLINE); + vty_out(vty, "RACH Max transmissions: %u%s", + rach_max_trans_raw2val(bts->si_common.rach_control.max_trans), + VTY_NEWLINE); + if (bts->si_common.rach_control.cell_bar) + vty_out(vty, " CELL IS BARRED%s", VTY_NEWLINE); + vty_out(vty, "System Information present: 0x%08x, static: 0x%08x%s", + bts->si_valid, bts->si_mode_static, VTY_NEWLINE); + if (is_ipaccess_bts(bts)) + vty_out(vty, " Unit ID: %u/%u/0, OML Stream ID 0x%02x%s", + bts->ip_access.site_id, bts->ip_access.bts_id, + bts->oml_tei, VTY_NEWLINE); + vty_out(vty, " NM State: "); + net_dump_nmstate(vty, &bts->nm_state); + vty_out(vty, " Site Mgr NM State: "); + net_dump_nmstate(vty, &bts->site_mgr.nm_state); + vty_out(vty, " Paging: FIXME pending requests, %u free slots%s", + 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 [number]", + 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->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->nm_state.administrative == NM_STATE_LOCKED ? 1 : 0, + VTY_NEWLINE); + vty_out(vty, " arfcn %u%s", trx->arfcn, VTY_NEWLINE); + vty_out(vty, " nominal power %u%s", trx->nominal_power, VTY_NEWLINE); + vty_out(vty, " max_power_red %u%s", trx->max_power_red, VTY_NEWLINE); + config_write_e1_link(vty, &trx->rsl_e1_link, " rsl "); + vty_out(vty, " rsl e1 tei %u%s", trx->rsl_tei, VTY_NEWLINE); + + if (trx->bts->model->config_write_trx) + trx->bts->model->config_write_trx(vty, trx); + + for (i = 0; i < TRX_NR_TS; i++) + config_write_ts_single(vty, &trx->ts[i]); +} + +static void config_write_bts_gprs(struct vty *vty, struct gsm_bts *bts) +{ + unsigned int i; + vty_out(vty, " gprs mode %s%s", bts_gprs_mode_name(bts->gprs.mode), + VTY_NEWLINE); + if (bts->gprs.mode == BTS_GPRS_NONE) + return; + + vty_out(vty, " gprs routing area %u%s", bts->gprs.rac, + VTY_NEWLINE); + vty_out(vty, " gprs 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); + } +} + +static void config_write_bts_single(struct vty *vty, struct gsm_bts *bts) +{ + struct gsm_bts_trx *trx; + int i; + + vty_out(vty, " bts %u%s", bts->nr, VTY_NEWLINE); + vty_out(vty, " type %s%s", btstype2str(bts->type), VTY_NEWLINE); + if (bts->description) + vty_out(vty, " description %s%s", bts->description, VTY_NEWLINE); + vty_out(vty, " band %s%s", gsm_band_name(bts->band), VTY_NEWLINE); + vty_out(vty, " cell_identity %u%s", bts->cell_identity, VTY_NEWLINE); + vty_out(vty, " location_area_code %u%s", bts->location_area_code, + VTY_NEWLINE); + vty_out(vty, " training_sequence_code %u%s", bts->tsc, 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); + } + + if (bts->si_common.chan_desc.t3212) + vty_out(vty, " periodic location update %u%s", + bts->si_common.chan_desc.t3212 * 6, 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); + + 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); + 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), + hexdump_nospc(bts->si_buf[i], sizeof(bts->si_buf[i])), + VTY_NEWLINE); + } + } + if (is_ipaccess_bts(bts)) { + vty_out(vty, " ip.access unit_id %u %u%s", + bts->ip_access.site_id, bts->ip_access.bts_id, VTY_NEWLINE); + vty_out(vty, " oml ip.access stream_id %u%s", bts->oml_tei, VTY_NEWLINE); + } else { + config_write_e1_link(vty, &bts->oml_e1_link, " oml "); + vty_out(vty, " oml e1 tei %u%s", bts->oml_tei, VTY_NEWLINE); + } + + /* 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); + } + } + + config_write_bts_gprs(vty, bts); + + 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 int config_write_bts(struct vty *v) +{ + struct gsm_network *gsmnet = gsmnet_from_vty(v); + struct gsm_bts *bts; + + llist_for_each_entry(bts, &gsmnet->bts_list, list) + config_write_bts_single(v, bts); + + return CMD_SUCCESS; +} + +static int config_write_net(struct vty *vty) +{ + struct gsm_network *gsmnet = gsmnet_from_vty(vty); + + vty_out(vty, "network%s", VTY_NEWLINE); + vty_out(vty, " network country code %u%s", gsmnet->country_code, VTY_NEWLINE); + vty_out(vty, " mobile network code %u%s", gsmnet->network_code, VTY_NEWLINE); + vty_out(vty, " short name %s%s", gsmnet->name_short, VTY_NEWLINE); + vty_out(vty, " long name %s%s", gsmnet->name_long, VTY_NEWLINE); + vty_out(vty, " auth policy %s%s", gsm_auth_policy_name(gsmnet->auth_policy), VTY_NEWLINE); + vty_out(vty, " location updating reject cause %u%s", + gsmnet->reject_cause, VTY_NEWLINE); + vty_out(vty, " encryption a5 %u%s", gsmnet->a5_encryption, VTY_NEWLINE); + vty_out(vty, " neci %u%s", gsmnet->neci, VTY_NEWLINE); + vty_out(vty, " paging any use tch %d%s", gsmnet->pag_any_tch, VTY_NEWLINE); + vty_out(vty, " rrlp mode %s%s", rrlp_mode_name(gsmnet->rrlp.mode), + VTY_NEWLINE); + vty_out(vty, " mm info %u%s", gsmnet->send_mm_info, VTY_NEWLINE); + vty_out(vty, " handover %u%s", gsmnet->handover.active, VTY_NEWLINE); + vty_out(vty, " handover window rxlev averaging %u%s", + gsmnet->handover.win_rxlev_avg, VTY_NEWLINE); + vty_out(vty, " handover window rxqual averaging %u%s", + gsmnet->handover.win_rxqual_avg, VTY_NEWLINE); + vty_out(vty, " handover window rxlev neighbor averaging %u%s", + gsmnet->handover.win_rxlev_avg_neigh, VTY_NEWLINE); + vty_out(vty, " handover power budget interval %u%s", + gsmnet->handover.pwr_interval, VTY_NEWLINE); + vty_out(vty, " handover power budget hysteresis %u%s", + gsmnet->handover.pwr_hysteresis, VTY_NEWLINE); + vty_out(vty, " handover maximum distance %u%s", + gsmnet->handover.max_distance, VTY_NEWLINE); + vty_out(vty, " timer t3101 %u%s", gsmnet->T3101, VTY_NEWLINE); + vty_out(vty, " timer t3103 %u%s", gsmnet->T3103, VTY_NEWLINE); + vty_out(vty, " timer t3105 %u%s", gsmnet->T3105, VTY_NEWLINE); + vty_out(vty, " timer t3107 %u%s", gsmnet->T3107, VTY_NEWLINE); + vty_out(vty, " timer t3109 %u%s", gsmnet->T3109, VTY_NEWLINE); + vty_out(vty, " timer t3111 %u%s", gsmnet->T3111, VTY_NEWLINE); + vty_out(vty, " timer t3113 %u%s", gsmnet->T3113, VTY_NEWLINE); + vty_out(vty, " timer t3115 %u%s", gsmnet->T3115, VTY_NEWLINE); + vty_out(vty, " timer t3117 %u%s", gsmnet->T3117, VTY_NEWLINE); + vty_out(vty, " timer t3119 %u%s", gsmnet->T3119, VTY_NEWLINE); + vty_out(vty, " timer t3122 %u%s", gsmnet->T3122, VTY_NEWLINE); + vty_out(vty, " timer t3141 %u%s", gsmnet->T3141, VTY_NEWLINE); + vty_out(vty, " dtx-used %u%s", gsmnet->dtx_enabled, VTY_NEWLINE); + vty_out(vty, " subscriber-keep-in-ram %d%s", + gsmnet->keep_subscr, VTY_NEWLINE); + + return CMD_SUCCESS; +} + +static void trx_dump_vty(struct vty *vty, struct gsm_bts_trx *trx) +{ + vty_out(vty, "TRX %u of BTS %u is on ARFCN %u%s", + trx->nr, trx->bts->nr, trx->arfcn, VTY_NEWLINE); + vty_out(vty, "Description: %s%s", + trx->description ? trx->description : "(null)", VTY_NEWLINE); + vty_out(vty, " RF Nominal Power: %d dBm, reduced by %u dB, " + "resulting BS power: %d dBm%s", + trx->nominal_power, trx->max_power_red, + trx->nominal_power - trx->max_power_red, VTY_NEWLINE); + vty_out(vty, " NM State: "); + net_dump_nmstate(vty, &trx->nm_state); + vty_out(vty, " Baseband Transceiver NM State: "); + net_dump_nmstate(vty, &trx->bb_transc.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 [bts_nr] [trx_nr]", + 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", + ts->trx->bts->nr, ts->trx->nr, ts->nr, + gsm_pchan_name(ts->pchan)); + if (ts->pchan == GSM_PCHAN_TCH_F_PDCH) + vty_out(vty, " (%s mode)", + ts->flags & TS_F_PDCH_MODE ? "PDCH" : "TCH/F"); + vty_out(vty, "%s", VTY_NEWLINE); + vty_out(vty, " NM State: "); + net_dump_nmstate(vty, &ts->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 [bts_nr] [trx_nr] [ts_nr]", + 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 (subscr->name) + vty_out(vty, " Name: '%s'%s", subscr->name, VTY_NEWLINE); + if (subscr->extension) + vty_out(vty, " Extension: %s%s", subscr->extension, + VTY_NEWLINE); + if (subscr->imsi) + vty_out(vty, " IMSI: %s%s", subscr->imsi, VTY_NEWLINE); + if (subscr->tmsi != GSM_RESERVED_TMSI) + vty_out(vty, " TMSI: %08X%s", subscr->tmsi, + VTY_NEWLINE); + + vty_out(vty, " Use count: %u%s", subscr->use_count, VTY_NEWLINE); +} + +static void meas_rep_dump_uni_vty(struct vty *vty, + struct gsm_meas_rep_unidir *mru, + const char *prefix, + const char *dir) +{ + vty_out(vty, "%s RXL-FULL-%s: %4d dBm, RXL-SUB-%s: %4d dBm ", + prefix, dir, rxlev2dbm(mru->full.rx_lev), + dir, rxlev2dbm(mru->sub.rx_lev)); + vty_out(vty, "RXQ-FULL-%s: %d, RXQ-SUB-%s: %d%s", + dir, mru->full.rx_qual, dir, mru->sub.rx_qual, + VTY_NEWLINE); +} + +static void meas_rep_dump_vty(struct vty *vty, struct gsm_meas_rep *mr, + const char *prefix) +{ + vty_out(vty, "%sMeasurement Report:%s", prefix, VTY_NEWLINE); + vty_out(vty, "%s Flags: %s%s%s%s%s", prefix, + mr->flags & MEAS_REP_F_UL_DTX ? "DTXu " : "", + mr->flags & MEAS_REP_F_DL_DTX ? "DTXd " : "", + mr->flags & MEAS_REP_F_FPC ? "FPC " : "", + mr->flags & MEAS_REP_F_DL_VALID ? " " : "DLinval ", + VTY_NEWLINE); + if (mr->flags & MEAS_REP_F_MS_TO) + vty_out(vty, "%s MS Timing Offset: %u%s", prefix, + mr->ms_timing_offset, VTY_NEWLINE); + if (mr->flags & MEAS_REP_F_MS_L1) + vty_out(vty, "%s L1 MS Power: %u dBm, Timing Advance: %u%s", + prefix, mr->ms_l1.pwr, mr->ms_l1.ta, VTY_NEWLINE); + if (mr->flags & MEAS_REP_F_DL_VALID) + meas_rep_dump_uni_vty(vty, &mr->dl, prefix, "dl"); + meas_rep_dump_uni_vty(vty, &mr->ul, prefix, "ul"); +} + +static void lchan_dump_full_vty(struct vty *vty, struct gsm_lchan *lchan) +{ + int idx; + + vty_out(vty, "BTS %u, TRX %u, Timeslot %u, Lchan %u: Type %s%s", + lchan->ts->trx->bts->nr, lchan->ts->trx->nr, lchan->ts->nr, + lchan->nr, gsm_lchant_name(lchan->type), VTY_NEWLINE); + vty_out(vty, " Connection: %u, State: %s%s", + lchan->conn ? 1: 0, + gsm_lchans_name(lchan->state), VTY_NEWLINE); + vty_out(vty, " BS Power: %u dBm, MS Power: %u dBm%s", + lchan->ts->trx->nominal_power - lchan->ts->trx->max_power_red + - lchan->bs_power*2, + ms_pwr_dbm(lchan->ts->trx->bts->band, lchan->ms_power), + VTY_NEWLINE); + if (lchan->conn && lchan->conn->subscr) { + vty_out(vty, " Subscriber:%s", VTY_NEWLINE); + subscr_dump_vty(vty, lchan->conn->subscr); + } else + vty_out(vty, " No Subscriber%s", VTY_NEWLINE); + if (is_ipaccess_bts(lchan->ts->trx->bts)) { + struct in_addr ia; + ia.s_addr = htonl(lchan->abis_ip.bound_ip); + vty_out(vty, " Bound IP: %s Port %u RTP_TYPE2=%u CONN_ID=%u%s", + inet_ntoa(ia), lchan->abis_ip.bound_port, + lchan->abis_ip.rtp_payload2, lchan->abis_ip.conn_id, + VTY_NEWLINE); + } + + /* we want to report the last measurement report */ + idx = calc_initial_idx(ARRAY_SIZE(lchan->meas_rep), + lchan->meas_rep_idx, 1); + meas_rep_dump_vty(vty, &lchan->meas_rep[idx], " "); +} + +static void lchan_dump_short_vty(struct vty *vty, struct gsm_lchan *lchan) +{ + struct gsm_meas_rep *mr; + int idx; + + /* we want to report the last measurement report */ + idx = calc_initial_idx(ARRAY_SIZE(lchan->meas_rep), + lchan->meas_rep_idx, 1); + mr = &lchan->meas_rep[idx]; + + vty_out(vty, "BTS %u, TRX %u, Timeslot %u, Lchan %u, Type %s - " + "L1 MS Power: %u dBm RXL-FULL-dl: %4d dBm RXL-FULL-ul: %4d dBm%s", + lchan->ts->trx->bts->nr, lchan->ts->trx->nr, lchan->ts->nr, + lchan->nr, gsm_lchant_name(lchan->type), mr->ms_l1.pwr, + rxlev2dbm(mr->dl.full.rx_lev), + rxlev2dbm(mr->ul.full.rx_lev), + VTY_NEWLINE); +} + +static int 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 >= 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; + } + ts = &trx->ts[ts_nr]; + } + 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); + 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]; + for (lchan_nr = 0; lchan_nr < TS_MAX_LCHAN; + lchan_nr++) { + lchan = &ts->lchan[lchan_nr]; + if (lchan->type == GSM_LCHAN_NONE) + continue; + dump_cb(vty, lchan); + } + } + } + } + + return CMD_SUCCESS; +} + + +DEFUN(show_lchan, + show_lchan_cmd, + "show lchan [bts_nr] [trx_nr] [ts_nr] [lchan_nr]", + SHOW_STR "Display information about a logical channel\n" + "BTS Number\n" "TRX Number\n" "Timeslot Number\n" + "Logical Channel Number\n") + +{ + return lchan_summary(vty, argc, argv, lchan_dump_full_vty); +} + +DEFUN(show_lchan_summary, + show_lchan_summary_cmd, + "show lchan summary [bts_nr] [trx_nr] [ts_nr] [lchan_nr]", + SHOW_STR "Display information about a logical channel\n" + "BTS Number\n" "TRX Number\n" "Timeslot Number\n" + "Logical Channel Number\n") +{ + return lchan_summary(vty, argc, argv, lchan_dump_short_vty); +} + +static void e1drv_dump_vty(struct vty *vty, struct e1inp_driver *drv) +{ + vty_out(vty, "E1 Input Driver %s%s", drv->name, VTY_NEWLINE); +} + +DEFUN(show_e1drv, + show_e1drv_cmd, + "show e1_driver", + SHOW_STR "Display information about available E1 drivers\n") +{ + struct e1inp_driver *drv; + + llist_for_each_entry(drv, &e1inp_driver_list, list) + e1drv_dump_vty(vty, drv); + + return CMD_SUCCESS; +} + +static void e1line_dump_vty(struct vty *vty, struct e1inp_line *line) +{ + vty_out(vty, "E1 Line Number %u, Name %s, Driver %s%s", + line->num, line->name ? line->name : "", + line->driver->name, VTY_NEWLINE); +} + +DEFUN(show_e1line, + show_e1line_cmd, + "show e1_line [line_nr]", + SHOW_STR "Display information about a E1 line\n" + "E1 Line Number\n") +{ + struct e1inp_line *line; + + if (argc >= 1) { + int num = atoi(argv[0]); + llist_for_each_entry(line, &e1inp_line_list, list) { + if (line->num == num) { + e1line_dump_vty(vty, line); + return CMD_SUCCESS; + } + } + return CMD_WARNING; + } + + llist_for_each_entry(line, &e1inp_line_list, list) + e1line_dump_vty(vty, line); + + return CMD_SUCCESS; +} + +static void e1ts_dump_vty(struct vty *vty, struct e1inp_ts *ts) +{ + if (ts->type == E1INP_TS_TYPE_NONE) + return; + vty_out(vty, "E1 Timeslot %2u of Line %u is Type %s%s", + ts->num, ts->line->num, e1inp_tstype_name(ts->type), + VTY_NEWLINE); +} + +DEFUN(show_e1ts, + show_e1ts_cmd, + "show e1_timeslot [line_nr] [ts_nr]", + SHOW_STR "Display information about a E1 timeslot\n" + "E1 Line Number\n" "E1 Timeslot Number\n") +{ + struct e1inp_line *line = NULL; + struct e1inp_ts *ts; + int ts_nr; + + if (argc == 0) { + llist_for_each_entry(line, &e1inp_line_list, list) { + for (ts_nr = 0; ts_nr < NUM_E1_TS; ts_nr++) { + ts = &line->ts[ts_nr]; + e1ts_dump_vty(vty, ts); + } + } + return CMD_SUCCESS; + } + if (argc >= 1) { + int num = atoi(argv[0]); + llist_for_each_entry(line, &e1inp_line_list, list) { + if (line->num == num) + break; + } + if (!line || line->num != num) { + vty_out(vty, "E1 line %s is invalid%s", + argv[0], VTY_NEWLINE); + return CMD_WARNING; + } + } + if (argc >= 2) { + ts_nr = atoi(argv[1]); + if (ts_nr > NUM_E1_TS) { + vty_out(vty, "E1 timeslot %s is invalid%s", + argv[1], VTY_NEWLINE); + return CMD_WARNING; + } + ts = &line->ts[ts_nr]; + e1ts_dump_vty(vty, ts); + return CMD_SUCCESS; + } else { + for (ts_nr = 0; ts_nr < NUM_E1_TS; ts_nr++) { + ts = &line->ts[ts_nr]; + e1ts_dump_vty(vty, ts); + } + return CMD_SUCCESS; + } + return CMD_SUCCESS; +} + +static void paging_dump_vty(struct vty *vty, struct gsm_paging_request *pag) +{ + vty_out(vty, "Paging on BTS %u%s", pag->bts->nr, VTY_NEWLINE); + subscr_dump_vty(vty, pag->subscr); +} + +static void bts_paging_dump_vty(struct vty *vty, struct gsm_bts *bts) +{ + struct gsm_paging_request *pag; + + llist_for_each_entry(pag, &bts->paging.pending_requests, entry) + paging_dump_vty(vty, pag); +} + +DEFUN(show_paging, + show_paging_cmd, + "show paging [bts_nr]", + 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; +} + +#define NETWORK_STR "Configure the GSM network\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") +{ + 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 <1-999>", + "Set the GSM mobile network code") +{ + 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") +{ + struct gsm_network *gsmnet = gsmnet_from_vty(vty); + + bsc_replace_string(gsmnet, &gsmnet->name_short, argv[0]); + return CMD_SUCCESS; +} + +DEFUN(cfg_net_name_long, + cfg_net_name_long_cmd, + "long name NAME", + "Set the long GSM network name") +{ + struct gsm_network *gsmnet = gsmnet_from_vty(vty); + + bsc_replace_string(gsmnet, &gsmnet->name_long, argv[0]); + return CMD_SUCCESS; +} + +DEFUN(cfg_net_auth_policy, + cfg_net_auth_policy_cmd, + "auth policy (closed|accept-all|token)", + "Authentication (not cryptographic)\n" + "Set the GSM network authentication policy\n" + "Require the MS to be activated in HLR\n" + "Accept all MS, whether in HLR or not\n" + "Use SMS-token based authentication\n") +{ + enum gsm_auth_policy policy = gsm_auth_policy_parse(argv[0]); + struct gsm_network *gsmnet = gsmnet_from_vty(vty); + + gsmnet->auth_policy = policy; + + return CMD_SUCCESS; +} + +DEFUN(cfg_net_reject_cause, + cfg_net_reject_cause_cmd, + "location updating reject cause <2-111>", + "Set the reject cause of location updating reject\n") +{ + 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)", + "Encryption options\n" + "A5 encryption\n" "A5/0: No encryption\n" + "A5/1: Encryption\n" "A5/2: Export-grade Encryption\n") +{ + struct gsm_network *gsmnet = gsmnet_from_vty(vty); + + gsmnet->a5_encryption= atoi(argv[0]); + + return CMD_SUCCESS; +} + +DEFUN(cfg_net_neci, + cfg_net_neci_cmd, + "neci (0|1)", + "New Establish Cause Indication\n" + "Don't set the NECI bit\n" "Set the NECI bit\n") +{ + struct gsm_network *gsmnet = gsmnet_from_vty(vty); + + gsmnet->neci = atoi(argv[0]); + gsm_net_update_ctype(gsmnet); + return CMD_SUCCESS; +} + +DEFUN(cfg_net_rrlp_mode, cfg_net_rrlp_mode_cmd, + "rrlp mode (none|ms-based|ms-preferred|ass-preferred)", + "Radio Resource Location Protocol\n" + "Set the Radio Resource Location Protocol Mode\n" + "Don't send RRLP request\n" + "Request MS-based location\n" + "Request any location, prefer MS-based\n" + "Request any location, prefer MS-assisted\n") +{ + struct gsm_network *gsmnet = gsmnet_from_vty(vty); + + gsmnet->rrlp.mode = rrlp_mode_parse(argv[0]); + + return CMD_SUCCESS; +} + +DEFUN(cfg_net_mm_info, cfg_net_mm_info_cmd, + "mm info (0|1)", + "Whether to send MM INFO after LOC UPD ACCEPT") +{ + struct gsm_network *gsmnet = gsmnet_from_vty(vty); + + gsmnet->send_mm_info = atoi(argv[0]); + + return CMD_SUCCESS; +} + +#define HANDOVER_STR "Handover Options\n" + +DEFUN(cfg_net_handover, cfg_net_handover_cmd, + "handover (0|1)", + HANDOVER_STR + "Don't perform in-call handover\n" + "Perform in-call handover\n") +{ + int enable = atoi(argv[0]); + struct gsm_network *gsmnet = gsmnet_from_vty(vty); + + if (enable && ipacc_rtp_direct) { + vty_out(vty, "%% Cannot enable handover unless RTP Proxy mode " + "is enabled by using the -P command line option%s", + VTY_NEWLINE); + return CMD_WARNING; + } + gsmnet->handover.active = enable; + + return CMD_SUCCESS; +} + +#define HO_WIN_STR HANDOVER_STR "Measurement Window\n" +#define HO_WIN_RXLEV_STR HO_WIN_STR "Received Level Averaging\n" +#define HO_WIN_RXQUAL_STR HO_WIN_STR "Received Quality Averaging\n" +#define HO_PBUDGET_STR HANDOVER_STR "Power Budget\n" + +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") +{ + 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") +{ + 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 + "How many RxQual measurements are used for averaging") +{ + 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)") +{ + 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") +{ + 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") +{ + 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") +{ + 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) \ +{ \ + 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.") +DECLARE_TIMER(3103, "Set the timeout value for HANDOVER.") +DECLARE_TIMER(3105, "Currently not used.") +DECLARE_TIMER(3107, "Currently not used.") +DECLARE_TIMER(3109, "Currently not used.") +DECLARE_TIMER(3111, "Set the RSL timeout to wait before releasing the RF Channel.") +DECLARE_TIMER(3113, "Set the time to try paging a subscriber.") +DECLARE_TIMER(3115, "Currently not used.") +DECLARE_TIMER(3117, "Currently not used.") +DECLARE_TIMER(3119, "Currently not used.") +DECLARE_TIMER(3122, "Waiting time (seconds) after IMM ASS REJECT") +DECLARE_TIMER(3141, "Currently not used.") + +DEFUN(cfg_net_dtx, + cfg_net_dtx_cmd, + "dtx-used (0|1)", + "Enable the usage of DTX.\n" + "DTX is enabled/disabled") +{ + struct gsm_network *gsmnet = gsmnet_from_vty(vty); + gsmnet->dtx_enabled = atoi(argv[0]); + return CMD_SUCCESS; +} + +DEFUN(cfg_net_subscr_keep, + cfg_net_subscr_keep_cmd, + "subscriber-keep-in-ram (0|1)", + "Keep unused subscribers in RAM.\n" + "Delete unused subscribers\n" "Keep unused subscribers\n") +{ + struct gsm_network *gsmnet = gsmnet_from_vty(vty); + gsmnet->keep_subscr = atoi(argv[0]); + return CMD_SUCCESS; +} + +/* per-BTS configuration */ +DEFUN(cfg_bts, + cfg_bts_cmd, + "bts BTS_NR", + "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(gsmnet, GSM_BTS_TYPE_UNKNOWN, + HARDCODED_TSC, HARDCODED_BSIC); + } else + bts = gsm_bts_num(gsmnet, bts_nr); + + if (!bts) { + vty_out(vty, "%% Unable to allocate BTS %u%s", + gsmnet->num_bts, VTY_NEWLINE); + return CMD_WARNING; + } + + vty->index = bts; + vty->index_sub = &bts->description; + vty->node = BTS_NODE; + + return CMD_SUCCESS; +} + +DEFUN(cfg_bts_type, + cfg_bts_type_cmd, + "type TYPE", + "Set the BTS type\n") +{ + struct gsm_bts *bts = vty->index; + int rc; + + rc = gsm_set_bts_type(bts, parse_btstype(argv[0])); + if (rc < 0) + return CMD_WARNING; + + return CMD_SUCCESS; +} + +DEFUN(cfg_bts_band, + cfg_bts_band_cmd, + "band BAND", + "Set the frequency band of this BTS\n") +{ + struct gsm_bts *bts = vty->index; + int band = gsm_band_parse(argv[0]); + + if (band < 0) { + vty_out(vty, "%% BAND %d is not a valid GSM band%s", + band, VTY_NEWLINE); + return CMD_WARNING; + } + + bts->band = band; + + return CMD_SUCCESS; +} + +DEFUN(cfg_bts_ci, + cfg_bts_ci_cmd, + "cell_identity <0-65535>", + "Set the Cell identity of this BTS\n") +{ + 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") +{ + struct gsm_bts *bts = vty->index; + int lac = atoi(argv[0]); + + if (lac < 0 || lac > 0xffff) { + vty_out(vty, "%% LAC %d is not in the valid range (0-65535)%s", + lac, VTY_NEWLINE); + return CMD_WARNING; + } + + if (lac == GSM_LAC_RESERVED_DETACHED || lac == GSM_LAC_RESERVED_ALL_BTS) { + vty_out(vty, "%% LAC %d is reserved by GSM 04.08%s", + lac, VTY_NEWLINE); + return CMD_WARNING; + } + + bts->location_area_code = lac; + + return CMD_SUCCESS; +} + + +DEFUN(cfg_bts_tsc, + cfg_bts_tsc_cmd, + "training_sequence_code <0-255>", + "Set the Training Sequence Code (TSC) of this BTS\n") +{ + struct gsm_bts *bts = vty->index; + int tsc = atoi(argv[0]); + + if (tsc < 0 || tsc > 0xff) { + vty_out(vty, "%% TSC %d is not in the valid range (0-255)%s", + tsc, VTY_NEWLINE); + return CMD_WARNING; + } + bts->tsc = tsc; + + return CMD_SUCCESS; +} + +DEFUN(cfg_bts_bsic, + cfg_bts_bsic_cmd, + "base_station_id_code <0-63>", + "Set the Base Station Identity Code (BSIC) of this BTS\n") +{ + 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>", + "Set the ip.access BTS Unit ID of this 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; +} + +#define OML_STR "Organization & Maintenance Link\n" +#define IPA_STR "ip.access Specific Options\n" + +DEFUN(cfg_bts_stream_id, + cfg_bts_stream_id_cmd, + "oml ip.access stream_id <0-255>", + OML_STR IPA_STR + "Set the ip.access Stream ID of the OML link of this BTS\n") +{ + struct gsm_bts *bts = vty->index; + int stream_id = atoi(argv[0]); + + 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; + + return CMD_SUCCESS; +} + +#define OML_E1_STR OML_STR "E1 Line\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 interface to be used for OML\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") +{ + 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") +{ + 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") +{ + struct gsm_bts *bts = vty->index; + bts->si_common.rach_control.max_trans = rach_max_trans_val2raw(atoi(argv[0])); + 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 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") +{ + 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?") +{ + 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)", + "Should this cell allow emergency calls?") +{ + 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_ms_max_power, cfg_bts_ms_max_power_cmd, + "ms max power <0-40>", + "Maximum transmit power of the MS") +{ + struct gsm_bts *bts = vty->index; + + bts->ms_max_power = atoi(argv[0]); + + return CMD_SUCCESS; +} + +DEFUN(cfg_bts_cell_resel_hyst, cfg_bts_cell_resel_hyst_cmd, + "cell reselection hysteresis <0-14>", + "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 (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 Bar Qualify") +{ + 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 Re-Selection 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.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 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", + "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 in seconds (by 20s increments)") +{ + 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", + "Set cell selection penalty time to reserved value 31\n" + "(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 <0-1530>", + "Periodic Location Updating Interval in Minutes") +{ + struct gsm_bts *bts = vty->index; + + bts->si_common.chan_desc.t3212 = atoi(argv[0]) / 6; + + 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") +{ + 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") +{ + 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") +{ + 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") +{ + 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") +{ + 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 FREE_NR", + "Only page when having a certain amount of free slots. -1 to disable") +{ + 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") +{ + 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_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]); + + if (mode != BTS_GPRS_NONE && + !gsm_bts_has_feature(bts, BTS_FEAT_GPRS)) { + vty_out(vty, "This BTS type does not support %s%s", argv[0], + VTY_NEWLINE); + return CMD_WARNING; + } + if (mode == BTS_GPRS_EGPRS && + !gsm_bts_has_feature(bts, BTS_FEAT_EGPRS)) { + vty_out(vty, "This BTS type does not support %s%s", argv[0], + VTY_NEWLINE); + return CMD_WARNING; + } + + bts->gprs.mode = mode; + + return CMD_SUCCESS; +} + +#define SI_TEXT "System Information Messages\n" +#define SI_TYPE_TEXT "(1|2|3|4|5|6|7|8|9|10|13|16|17|18|19|20|2bis|2ter|2quater|5bis|5ter)" +#define SI_TYPE_HELP "System Information Type 1\n" \ + "System Information Type 2\n" \ + "System Information Type 3\n" \ + "System Information Type 4\n" \ + "System Information Type 5\n" \ + "System Information Type 6\n" \ + "System Information Type 7\n" \ + "System Information Type 8\n" \ + "System Information Type 9\n" \ + "System Information Type 10\n" \ + "System Information Type 13\n" \ + "System Information Type 16\n" \ + "System Information Type 17\n" \ + "System Information Type 18\n" \ + "System Information Type 19\n" \ + "System Information Type 20\n" \ + "System Information Type 2bis\n" \ + "System Information Type 2ter\n" \ + "System Information Type 2quater\n" \ + "System Information Type 5bis\n" \ + "System Information Type 5ter\n" + +DEFUN(cfg_bts_si_mode, cfg_bts_si_mode_cmd, + "system-information " SI_TYPE_TEXT " mode (static|computed)", + SI_TEXT SI_TYPE_HELP + "System Information Mode\n" + "Static user-specified\n" + "Dynamic, BSC-computed\n") +{ + struct gsm_bts *bts = vty->index; + int type; + + type = get_string_value(osmo_sitype_strs, argv[0]); + if (type < 0) { + vty_out(vty, "Error SI Type%s", VTY_NEWLINE); + return CMD_WARNING; + } + + if (!strcmp(argv[1], "static")) + bts->si_mode_static |= (1 << type); + else + bts->si_mode_static &= ~(1 << type); + + return CMD_SUCCESS; +} + +DEFUN(cfg_bts_si_static, cfg_bts_si_static_cmd, + "system-information " SI_TYPE_TEXT " static HEXSTRING", + SI_TEXT SI_TYPE_HELP + "Static System Information filling\n" + "Static user-specified SI content in HEX notation\n") +{ + struct gsm_bts *bts = vty->index; + int rc, type; + + type = get_string_value(osmo_sitype_strs, argv[0]); + if (type < 0) { + vty_out(vty, "Error SI Type%s", VTY_NEWLINE); + return CMD_WARNING; + } + + if (!(bts->si_mode_static & (1 << type))) { + vty_out(vty, "SI Type %s is not configured in static mode%s", + get_value_string(osmo_sitype_strs, type), VTY_NEWLINE); + return CMD_WARNING; + } + + /* Fill buffer with padding pattern */ + memset(bts->si_buf[type], 0x2b, sizeof(bts->si_buf[type])); + + /* Parse the user-specified SI in hex format, [partially] overwriting padding */ + rc = hexparse(argv[1], bts->si_buf[type], sizeof(bts->si_buf[0])); + if (rc < 0 || rc > sizeof(bts->si_buf[0])) { + vty_out(vty, "Error parsing HEXSTRING%s", VTY_NEWLINE); + return CMD_WARNING; + } + + /* Mark this SI as present */ + bts->si_valid |= (1 << type); + + return CMD_SUCCESS; +} + +DEFUN(cfg_bts_neigh_mode, cfg_bts_neigh_mode_cmd, + "neighbor-list mode (automatic|manual|manual-si5)", + "Neighbor List\n" "Mode of Neighbor List generation\n" + "Automatically from all BTS in this OpenBSC\n" "Manual\n" + "Manual with different lists for SI2 and SI5\n") +{ + struct gsm_bts *bts = vty->index; + int mode = get_string_value(bts_neigh_mode_strs, argv[0]); + + switch (mode) { + case NL_MODE_MANUAL_SI5SEP: + case NL_MODE_MANUAL: + /* make sure we clear the current list when switching to + * manual mode */ + if (bts->neigh_list_manual_mode == 0) + memset(&bts->si_common.data.neigh_list, 0, + sizeof(bts->si_common.data.neigh_list)); + break; + default: + break; + } + + bts->neigh_list_manual_mode = mode; + + return CMD_SUCCESS; +} + +DEFUN(cfg_bts_neigh, cfg_bts_neigh_cmd, + "neighbor-list (add|del) arfcn <0-1024>", + "Neighbor List\n" "Add to manual neighbor list\n" + "Delete from manual neighbor list\n" "ARFCN of neighbor\n" + "ARFCN of neighbor\n") +{ + struct gsm_bts *bts = vty->index; + struct bitvec *bv = &bts->si_common.neigh_list; + uint16_t arfcn = atoi(argv[1]); + + if (!bts->neigh_list_manual_mode) { + vty_out(vty, "%% Cannot configure neighbor list in " + "automatic mode%s", VTY_NEWLINE); + return CMD_WARNING; + } + + if (!strcmp(argv[0], "add")) + bitvec_set_bit_pos(bv, arfcn, 1); + else + bitvec_set_bit_pos(bv, arfcn, 0); + + return CMD_SUCCESS; +} + +DEFUN(cfg_bts_si5_neigh, cfg_bts_si5_neigh_cmd, + "si5 neighbor-list (add|del) arfcn <0-1024>", + "SI5 Neighbor List\n" "Add to manual SI5 neighbor list\n" + "Delete from SI5 manual neighbor list\n" "ARFCN of neighbor\n" + "ARFCN of neighbor\n") +{ + struct gsm_bts *bts = vty->index; + struct bitvec *bv = &bts->si_common.si5_neigh_list; + uint16_t arfcn = atoi(argv[1]); + + if (!bts->neigh_list_manual_mode) { + vty_out(vty, "%% Cannot configure neighbor list in " + "automatic mode%s", VTY_NEWLINE); + return CMD_WARNING; + } + + if (!strcmp(argv[0], "add")) + bitvec_set_bit_pos(bv, arfcn, 1); + else + bitvec_set_bit_pos(bv, arfcn, 0); + + return CMD_SUCCESS; +} + +#define TRX_TEXT "Radio Transceiver\n" + +/* per TRX configuration */ +DEFUN(cfg_trx, + cfg_trx_cmd, + "trx TRX_NR", + 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-1024>", + "Set the ARFCN for this TRX\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 dB\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 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)", + "E1 interface 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>", + "Set the TEI to be used for RSL") +{ + 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)", + "Turn off RF of the TRX.\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") +{ + 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", + "Physical Channel configuration (TCH/SDCCH/...)") +{ + 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; +} + +#define HOPPING_STR "Configure frequency hopping\n" + +DEFUN(cfg_ts_hopping, + cfg_ts_hopping_cmd, + "hopping enabled (0|1)", + HOPPING_STR "Enable or disable frequency hopping\n" + "Disable frequency hopping\n" "Enable frequency hopping\n") +{ + struct gsm_bts_trx_ts *ts = vty->index; + int enabled = atoi(argv[0]); + + if (enabled && !gsm_bts_has_feature(ts->trx->bts, BTS_FEAT_HOPPING)) { + vty_out(vty, "BTS model does not support hopping%s", + VTY_NEWLINE); + return CMD_WARNING; + } + + ts->hopping.enabled = enabled; + + return CMD_SUCCESS; +} + +DEFUN(cfg_ts_hsn, + cfg_ts_hsn_cmd, + "hopping sequence-number <0-63>", + HOPPING_STR + "Which hopping sequence to use for this channel") +{ + 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") +{ + 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 sub-slot connected to this on-air timeslot") +{ + 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", + counter_get(net->stats.chreq.total), + counter_get(net->stats.chreq.no_channel), VTY_NEWLINE); + vty_out(vty, "Channel Failures : %lu rf_failures, %lu rll failures%s", + counter_get(net->stats.chan.rf_fail), + counter_get(net->stats.chan.rll_err), VTY_NEWLINE); + vty_out(vty, "Paging : %lu attempted, %lu complete, %lu expired%s", + counter_get(net->stats.paging.attempted), + counter_get(net->stats.paging.completed), + counter_get(net->stats.paging.expired), VTY_NEWLINE); + vty_out(vty, "BTS failures : %lu OML, %lu RSL%s", + counter_get(net->stats.bts.oml_fail), + counter_get(net->stats.bts.rsl_fail), VTY_NEWLINE); +} + +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 log_target *tgt = osmo_log_vty2tgt(vty); + + if (!tgt) + return CMD_WARNING; + + log_set_imsi_filter(tgt, argv[0]); + return CMD_SUCCESS; +} + + +DEFUN(drop_bts, + drop_bts_cmd, + "drop bts connection <0-65535> (oml|rsl)", + "Debug/Simulation command to drop ipaccess BTS\n" + "BTS NR\n" "Connection Type\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(pdch_act, pdch_act_cmd, + "bts <0-255> trx <0-255> timeslot <0-7> pdch (activate|deactivate)", + "BTS related commands\n" "BTS Number\n" "Transceiver\n" "Transceiver Number\n" + "TRX Timeslot\n" "Timeslot Number\n" "Packet Data Channel\n" + "Activate Dynamic PDCH/TCH (-> PDCH mode)\n" + "Deactivate Dynamic PDCH/TCH (-> TCH mode)\n") +{ + struct gsm_bts *bts; + struct gsm_bts_trx *trx; + struct gsm_bts_trx_ts *ts; + int bts_nr = atoi(argv[0]); + int trx_nr = atoi(argv[1]); + int ts_nr = atoi(argv[2]); + int activate; + + bts = gsm_bts_num(bsc_gsmnet, bts_nr); + if (!bts) { + vty_out(vty, "%% No such BTS (%d)%s", bts_nr, VTY_NEWLINE); + return CMD_WARNING; + } + + if (!is_ipaccess_bts(bts)) { + vty_out(vty, "%% This command only works for ipaccess BTS%s", + VTY_NEWLINE); + return CMD_WARNING; + } + + trx = gsm_bts_trx_num(bts, trx_nr); + if (!trx) { + vty_out(vty, "%% No such TRX (%d)%s", trx_nr, VTY_NEWLINE); + return CMD_WARNING; + } + + ts = &trx->ts[ts_nr]; + if (ts->pchan != GSM_PCHAN_TCH_F_PDCH) { + vty_out(vty, "%% Timeslot %u is not in dynamic TCH_F/PDCH " + "mode%s", ts_nr, VTY_NEWLINE); + return CMD_WARNING; + } + + if (!strcmp(argv[3], "activate")) + activate = 1; + else + activate = 0; + + rsl_ipacc_pdch_activate(ts, activate); + + return CMD_SUCCESS; + +} + +extern int bsc_vty_init_extra(void); +extern const char *openbsc_copyright; + +int bsc_vty_init(void) +{ + install_element_ve(&show_net_cmd); + install_element_ve(&show_bts_cmd); + install_element_ve(&show_trx_cmd); + install_element_ve(&show_ts_cmd); + install_element_ve(&show_lchan_cmd); + install_element_ve(&show_lchan_summary_cmd); + install_element_ve(&logging_fltr_imsi_cmd); + + install_element_ve(&show_e1drv_cmd); + install_element_ve(&show_e1line_cmd); + install_element_ve(&show_e1ts_cmd); + + install_element_ve(&show_paging_cmd); + + logging_vty_add_cmds(); + install_element(CFG_LOG_NODE, &logging_fltr_imsi_cmd); + + install_element(CONFIG_NODE, &cfg_net_cmd); + install_node(&net_node, config_write_net); + install_default(GSMNET_NODE); + install_element(GSMNET_NODE, &ournode_exit_cmd); + install_element(GSMNET_NODE, &ournode_end_cmd); + install_element(GSMNET_NODE, &cfg_net_ncc_cmd); + install_element(GSMNET_NODE, &cfg_net_mnc_cmd); + install_element(GSMNET_NODE, &cfg_net_name_short_cmd); + install_element(GSMNET_NODE, &cfg_net_name_long_cmd); + install_element(GSMNET_NODE, &cfg_net_auth_policy_cmd); + install_element(GSMNET_NODE, &cfg_net_reject_cause_cmd); + install_element(GSMNET_NODE, &cfg_net_encryption_cmd); + install_element(GSMNET_NODE, &cfg_net_neci_cmd); + install_element(GSMNET_NODE, &cfg_net_rrlp_mode_cmd); + install_element(GSMNET_NODE, &cfg_net_mm_info_cmd); + install_element(GSMNET_NODE, &cfg_net_handover_cmd); + install_element(GSMNET_NODE, &cfg_net_ho_win_rxlev_avg_cmd); + install_element(GSMNET_NODE, &cfg_net_ho_win_rxqual_avg_cmd); + install_element(GSMNET_NODE, &cfg_net_ho_win_rxlev_avg_neigh_cmd); + install_element(GSMNET_NODE, &cfg_net_ho_pwr_interval_cmd); + install_element(GSMNET_NODE, &cfg_net_ho_pwr_hysteresis_cmd); + install_element(GSMNET_NODE, &cfg_net_ho_max_distance_cmd); + install_element(GSMNET_NODE, &cfg_net_T3101_cmd); + install_element(GSMNET_NODE, &cfg_net_T3103_cmd); + install_element(GSMNET_NODE, &cfg_net_T3105_cmd); + install_element(GSMNET_NODE, &cfg_net_T3107_cmd); + install_element(GSMNET_NODE, &cfg_net_T3109_cmd); + install_element(GSMNET_NODE, &cfg_net_T3111_cmd); + install_element(GSMNET_NODE, &cfg_net_T3113_cmd); + install_element(GSMNET_NODE, &cfg_net_T3115_cmd); + install_element(GSMNET_NODE, &cfg_net_T3117_cmd); + install_element(GSMNET_NODE, &cfg_net_T3119_cmd); + install_element(GSMNET_NODE, &cfg_net_T3122_cmd); + install_element(GSMNET_NODE, &cfg_net_T3141_cmd); + install_element(GSMNET_NODE, &cfg_net_dtx_cmd); + install_element(GSMNET_NODE, &cfg_net_subscr_keep_cmd); + install_element(GSMNET_NODE, &cfg_net_pag_any_tch_cmd); + + install_element(GSMNET_NODE, &cfg_bts_cmd); + install_node(&bts_node, config_write_bts); + install_default(BTS_NODE); + install_element(BTS_NODE, &ournode_exit_cmd); + install_element(BTS_NODE, &ournode_end_cmd); + install_element(BTS_NODE, &cfg_bts_type_cmd); + install_element(BTS_NODE, &cfg_description_cmd); + install_element(BTS_NODE, &cfg_no_description_cmd); + install_element(BTS_NODE, &cfg_bts_band_cmd); + install_element(BTS_NODE, &cfg_bts_ci_cmd); + install_element(BTS_NODE, &cfg_bts_lac_cmd); + install_element(BTS_NODE, &cfg_bts_tsc_cmd); + install_element(BTS_NODE, &cfg_bts_bsic_cmd); + install_element(BTS_NODE, &cfg_bts_unit_id_cmd); + install_element(BTS_NODE, &cfg_bts_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_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_ms_max_power_cmd); + install_element(BTS_NODE, &cfg_bts_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_gprs_mode_cmd); + install_element(BTS_NODE, &cfg_bts_gprs_ns_timer_cmd); + install_element(BTS_NODE, &cfg_bts_gprs_rac_cmd); + install_element(BTS_NODE, &cfg_bts_gprs_bvci_cmd); + install_element(BTS_NODE, &cfg_bts_gprs_cell_timer_cmd); + install_element(BTS_NODE, &cfg_bts_gprs_nsei_cmd); + install_element(BTS_NODE, &cfg_bts_gprs_nsvci_cmd); + install_element(BTS_NODE, &cfg_bts_gprs_nsvc_lport_cmd); + install_element(BTS_NODE, &cfg_bts_gprs_nsvc_rport_cmd); + install_element(BTS_NODE, &cfg_bts_gprs_nsvc_rip_cmd); + install_element(BTS_NODE, &cfg_bts_pag_free_cmd); + install_element(BTS_NODE, &cfg_bts_si_mode_cmd); + install_element(BTS_NODE, &cfg_bts_si_static_cmd); + install_element(BTS_NODE, &cfg_bts_neigh_mode_cmd); + install_element(BTS_NODE, &cfg_bts_neigh_cmd); + install_element(BTS_NODE, &cfg_bts_si5_neigh_cmd); + + install_element(BTS_NODE, &cfg_trx_cmd); + install_node(&trx_node, dummy_config_write); + install_default(TRX_NODE); + install_element(TRX_NODE, &ournode_exit_cmd); + install_element(TRX_NODE, &ournode_end_cmd); + 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); + install_default(TS_NODE); + install_element(TS_NODE, &ournode_exit_cmd); + install_element(TS_NODE, &ournode_end_cmd); + install_element(TS_NODE, &cfg_ts_pchan_cmd); + install_element(TS_NODE, &cfg_ts_hopping_cmd); + install_element(TS_NODE, &cfg_ts_hsn_cmd); + install_element(TS_NODE, &cfg_ts_maio_cmd); + install_element(TS_NODE, &cfg_ts_arfcn_add_cmd); + install_element(TS_NODE, &cfg_ts_arfcn_del_cmd); + install_element(TS_NODE, &cfg_ts_e1_subslot_cmd); + + install_element(ENABLE_NODE, &drop_bts_cmd); + install_element(ENABLE_NODE, &pdch_act_cmd); + + abis_nm_vty_init(); + abis_om2k_vty_init(); + e1inp_vty_init(); + + bsc_vty_init_extra(); + + return 0; +} diff --git a/openbsc/src/libbsc/bts_ericsson_rbs2000.c b/openbsc/src/libbsc/bts_ericsson_rbs2000.c new file mode 100644 index 000000000..27d5ce6f7 --- /dev/null +++ b/openbsc/src/libbsc/bts_ericsson_rbs2000.c @@ -0,0 +1,164 @@ +/* Ericsson RBS-2xxx specific code */ + +/* (C) 2011 by Harald Welte + * + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + * + */ + +#include + +#include + +#include +#include +#include +#include +#include + +#include "../libabis/input/lapd.h" + +static void bootstrap_om_bts(struct gsm_bts *bts) +{ + LOGP(DNM, LOGL_NOTICE, "bootstrapping OML for BTS %u\n", bts->nr); + abis_om2k_tx_start_req(bts, &om2k_mo_cf); + /* FIXME */ +} + +static void bootstrap_om_trx(struct gsm_bts_trx *trx) +{ + 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 (start) + lapd_sap_start(ts->driver.dahdi.lapd, link->tei, link->sapi); + else + lapd_sap_stop(ts->driver.dahdi.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_GLOBAL) + return 0; + + switch (signal) { + case S_GLOBAL_BTS_CLOSE_OM: + bts = signal_data; + if (bts->type == GSM_BTS_TYPE_RBS2000) + shutdown_om(signal_data); + break; + } + + return 0; +} + +/* Callback function to be called every time we receive a signal from INPUT */ +static int inp_sig_cb(unsigned int subsys, unsigned int signal, + void *handler_data, void *signal_data) +{ + struct input_signal_data *isd = signal_data; + + if (subsys != SS_INPUT) + return 0; + + switch (signal) { + case S_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_INP_LINE_INIT: + /* Right now Ericsson RBS are only supported on DAHDI */ + if (strcasecmp(isd->line->driver->name, "DAHDI")) + break; + start_sabm_in_line(isd->line, 1); + break; + case S_INP_LINE_ALARM: + if (strcasecmp(isd->line->driver->name, "DAHDI")) + break; + start_sabm_in_line(isd->line, 0); + break; + case S_INP_LINE_NOALARM: + if (strcasecmp(isd->line->driver->name, "DAHDI")) + break; + start_sabm_in_line(isd->line, 1); + break; + } + + return 0; +} + +static void config_write_bts(struct vty *vty, struct gsm_bts *bts) +{ + abis_om2k_config_write_bts(vty, bts); +} + +static struct gsm_bts_model model_rbs2k = { + .type = GSM_BTS_TYPE_RBS2000, + .name = "rbs2000", + .oml_rcvmsg = &abis_om2k_rcvmsg, + .config_write_bts = &config_write_bts, +}; + +int bts_model_rbs2k_init(void) +{ + model_rbs2k.features.data = &model_rbs2k._features_data[0]; + model_rbs2k.features.data_len = sizeof(model_rbs2k._features_data); + + gsm_btsmodel_set_feature(&model_rbs2k, BTS_FEAT_HOPPING); + gsm_btsmodel_set_feature(&model_rbs2k, BTS_FEAT_HSCSD); + + register_signal_handler(SS_INPUT, inp_sig_cb, NULL); + register_signal_handler(SS_GLOBAL, gbl_sig_cb, NULL); + + return gsm_bts_model_register(&model_rbs2k); +} diff --git a/openbsc/src/libbsc/bts_ipaccess_nanobts.c b/openbsc/src/libbsc/bts_ipaccess_nanobts.c new file mode 100644 index 000000000..25dc0c8a2 --- /dev/null +++ b/openbsc/src/libbsc/bts_ipaccess_nanobts.c @@ -0,0 +1,447 @@ +/* ip.access nanoBTS specific code */ + +/* (C) 2009-2010 by Harald Welte + * + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + * + */ + +#include +#include + +#include + +#include +#include +#include + +static struct gsm_bts_model model_nanobts = { + .type = GSM_BTS_TYPE_NANOBTS, + .name = "nanobts", + .oml_rcvmsg = &abis_nm_rcvmsg, + .nm_att_tlvdef = { + .def = { + /* ip.access specifics */ + [NM_ATT_IPACC_DST_IP] = { TLV_TYPE_FIXED, 4 }, + [NM_ATT_IPACC_DST_IP_PORT] = { TLV_TYPE_FIXED, 2 }, + [NM_ATT_IPACC_STREAM_ID] = { TLV_TYPE_TV, }, + [NM_ATT_IPACC_SEC_OML_CFG] = { TLV_TYPE_FIXED, 6 }, + [NM_ATT_IPACC_IP_IF_CFG] = { TLV_TYPE_FIXED, 8 }, + [NM_ATT_IPACC_IP_GW_CFG] = { TLV_TYPE_FIXED, 12 }, + [NM_ATT_IPACC_IN_SERV_TIME] = { TLV_TYPE_FIXED, 4 }, + [NM_ATT_IPACC_LOCATION] = { TLV_TYPE_TL16V }, + [NM_ATT_IPACC_PAGING_CFG] = { TLV_TYPE_FIXED, 2 }, + [NM_ATT_IPACC_UNIT_ID] = { TLV_TYPE_TL16V }, + [NM_ATT_IPACC_UNIT_NAME] = { TLV_TYPE_TL16V }, + [NM_ATT_IPACC_SNMP_CFG] = { TLV_TYPE_TL16V }, + [NM_ATT_IPACC_PRIM_OML_CFG_LIST] = { TLV_TYPE_TL16V }, + [NM_ATT_IPACC_NV_FLAGS] = { TLV_TYPE_TL16V }, + [NM_ATT_IPACC_FREQ_CTRL] = { TLV_TYPE_FIXED, 2 }, + [NM_ATT_IPACC_PRIM_OML_FB_TOUT] = { TLV_TYPE_TL16V }, + [NM_ATT_IPACC_CUR_SW_CFG] = { TLV_TYPE_TL16V }, + [NM_ATT_IPACC_TIMING_BUS] = { TLV_TYPE_TL16V }, + [NM_ATT_IPACC_CGI] = { TLV_TYPE_TL16V }, + [NM_ATT_IPACC_RAC] = { TLV_TYPE_TL16V }, + [NM_ATT_IPACC_OBJ_VERSION] = { TLV_TYPE_TL16V }, + [NM_ATT_IPACC_GPRS_PAGING_CFG]= { TLV_TYPE_TL16V }, + [NM_ATT_IPACC_NSEI] = { TLV_TYPE_TL16V }, + [NM_ATT_IPACC_BVCI] = { TLV_TYPE_TL16V }, + [NM_ATT_IPACC_NSVCI] = { TLV_TYPE_TL16V }, + [NM_ATT_IPACC_NS_CFG] = { TLV_TYPE_TL16V }, + [NM_ATT_IPACC_BSSGP_CFG] = { TLV_TYPE_TL16V }, + [NM_ATT_IPACC_NS_LINK_CFG] = { TLV_TYPE_TL16V }, + [NM_ATT_IPACC_RLC_CFG] = { TLV_TYPE_TL16V }, + [NM_ATT_IPACC_ALM_THRESH_LIST]= { TLV_TYPE_TL16V }, + [NM_ATT_IPACC_MONIT_VAL_LIST] = { TLV_TYPE_TL16V }, + [NM_ATT_IPACC_TIB_CONTROL] = { TLV_TYPE_TL16V }, + [NM_ATT_IPACC_SUPP_FEATURES] = { TLV_TYPE_TL16V }, + [NM_ATT_IPACC_CODING_SCHEMES] = { TLV_TYPE_TL16V }, + [NM_ATT_IPACC_RLC_CFG_2] = { TLV_TYPE_TL16V }, + [NM_ATT_IPACC_HEARTB_TOUT] = { TLV_TYPE_TL16V }, + [NM_ATT_IPACC_UPTIME] = { TLV_TYPE_TL16V }, + [NM_ATT_IPACC_RLC_CFG_3] = { TLV_TYPE_TL16V }, + [NM_ATT_IPACC_SSL_CFG] = { TLV_TYPE_TL16V }, + [NM_ATT_IPACC_SEC_POSSIBLE] = { TLV_TYPE_TL16V }, + [NM_ATT_IPACC_IML_SSL_STATE] = { TLV_TYPE_TL16V }, + [NM_ATT_IPACC_REVOC_DATE] = { TLV_TYPE_TL16V }, + }, + }, +}; + +static unsigned char nanobts_attr_bts[] = { + NM_ATT_INTERF_BOUND, 0x55, 0x5b, 0x61, 0x67, 0x6d, 0x73, + /* interference avg. period in numbers of SACCH multifr */ + NM_ATT_INTAVE_PARAM, 0x06, + /* conn fail based on SACCH error rate */ + NM_ATT_CONN_FAIL_CRIT, 0x00, 0x02, 0x01, 0x10, + NM_ATT_T200, 0x1e, 0x24, 0x24, 0xa8, 0x34, 0x21, 0xa8, + NM_ATT_MAX_TA, 0x3f, + NM_ATT_OVERL_PERIOD, 0x00, 0x01, 10, /* seconds */ + NM_ATT_CCCH_L_T, 10, /* percent */ + NM_ATT_CCCH_L_I_P, 1, /* seconds */ + NM_ATT_RACH_B_THRESH, 10, /* busy threshold in - dBm */ + NM_ATT_LDAVG_SLOTS, 0x03, 0xe8, /* rach load averaging 1000 slots */ + NM_ATT_BTS_AIR_TIMER, 128, /* miliseconds */ + NM_ATT_NY1, 10, /* 10 retransmissions of physical config */ + NM_ATT_BCCH_ARFCN, HARDCODED_ARFCN >> 8, HARDCODED_ARFCN & 0xff, + NM_ATT_BSIC, HARDCODED_BSIC, + NM_ATT_IPACC_CGI, 0, 7, 0x00, 0xf1, 0x10, 0x00, 0x01, 0x00, 0x00, +}; + +static unsigned char nanobts_attr_radio[] = { + NM_ATT_RF_MAXPOWR_R, 0x0c, /* number of -2dB reduction steps / Pn */ + NM_ATT_ARFCN_LIST, 0x00, 0x02, HARDCODED_ARFCN >> 8, HARDCODED_ARFCN & 0xff, +}; + +static unsigned char nanobts_attr_nse[] = { + NM_ATT_IPACC_NSEI, 0, 2, 0x03, 0x9d, /* NSEI 925 */ + NM_ATT_IPACC_NS_CFG, 0, 7, 3, /* (un)blocking timer (Tns-block) */ + 3, /* (un)blocking retries */ + 3, /* reset timer (Tns-reset) */ + 3, /* reset retries */ + 30, /* test timer (Tns-test) */ + 3, /* alive timer (Tns-alive) */ + 10, /* alive retrires */ + NM_ATT_IPACC_BSSGP_CFG, 0, 11, + 3, /* blockimg timer (T1) */ + 3, /* blocking retries */ + 3, /* unblocking retries */ + 3, /* reset timer */ + 3, /* reset retries */ + 10, /* suspend timer (T3) in 100ms */ + 3, /* suspend retries */ + 10, /* resume timer (T4) in 100ms */ + 3, /* resume retries */ + 10, /* capability update timer (T5) */ + 3, /* capability update retries */ +}; + +static unsigned char nanobts_attr_cell[] = { + NM_ATT_IPACC_RAC, 0, 1, 1, /* routing area code */ + NM_ATT_IPACC_GPRS_PAGING_CFG, 0, 2, + 5, /* repeat time (50ms) */ + 3, /* repeat count */ + NM_ATT_IPACC_BVCI, 0, 2, 0x03, 0x9d, /* BVCI 925 */ + NM_ATT_IPACC_RLC_CFG, 0, 9, + 20, /* T3142 */ + 5, /* T3169 */ + 5, /* T3191 */ + 200, /* T3193 */ + 5, /* T3195 */ + 10, /* N3101 */ + 4, /* N3103 */ + 8, /* N3105 */ + 15, /* RLC CV countdown */ + NM_ATT_IPACC_CODING_SCHEMES, 0, 2, 0x0f, 0x00, /* CS1..CS4 */ + NM_ATT_IPACC_RLC_CFG_2, 0, 5, + 0x00, 250, /* T downlink TBF extension (0..500) */ + 0x00, 250, /* T uplink TBF extension (0..500) */ + 2, /* CS2 */ +#if 0 + /* EDGE model only, breaks older models. + * Should inquire the BTS capabilities */ + NM_ATT_IPACC_RLC_CFG_3, 0, 1, + 2, /* MCS2 */ +#endif +}; + +static unsigned char nanobts_attr_nsvc0[] = { + NM_ATT_IPACC_NSVCI, 0, 2, 0x03, 0x9d, /* 925 */ + NM_ATT_IPACC_NS_LINK_CFG, 0, 8, + 0x59, 0xd8, /* remote udp port (23000) */ + 192, 168, 100, 11, /* remote ip address */ + 0x59, 0xd8, /* local udp port (23000) */ +}; + +static void patch_16(uint8_t *data, const uint16_t val) +{ + memcpy(data, &val, sizeof(val)); +} + +static void patch_32(uint8_t *data, const uint32_t val) +{ + memcpy(data, &val, sizeof(val)); +} + +/* + * Patch the various SYSTEM INFORMATION tables to update + * the LAI + */ +static void patch_nm_tables(struct gsm_bts *bts) +{ + u_int8_t arfcn_low = bts->c0->arfcn & 0xff; + u_int8_t arfcn_high = (bts->c0->arfcn >> 8) & 0x0f; + + /* patch ARFCN into BTS Attributes */ + nanobts_attr_bts[42] &= 0xf0; + nanobts_attr_bts[42] |= arfcn_high; + nanobts_attr_bts[43] = arfcn_low; + + /* patch the RACH attributes */ + if (bts->rach_b_thresh != -1) { + nanobts_attr_bts[33] = bts->rach_b_thresh & 0xff; + } + + if (bts->rach_ldavg_slots != -1) { + u_int8_t avg_high = bts->rach_ldavg_slots & 0xff; + u_int8_t avg_low = (bts->rach_ldavg_slots >> 8) & 0x0f; + + nanobts_attr_bts[35] = avg_high; + nanobts_attr_bts[36] = avg_low; + } + + /* patch BSIC */ + nanobts_attr_bts[sizeof(nanobts_attr_bts)-11] = bts->bsic; + + /* patch CGI */ + abis_nm_ipaccess_cgi(nanobts_attr_bts+sizeof(nanobts_attr_bts)-7, bts); + + /* patch the power reduction */ + nanobts_attr_radio[1] = bts->c0->max_power_red / 2; + + /* patch NSEI */ + nanobts_attr_nse[3] = bts->gprs.nse.nsei >> 8; + nanobts_attr_nse[4] = bts->gprs.nse.nsei & 0xff; + memcpy(nanobts_attr_nse+8, bts->gprs.nse.timer, + ARRAY_SIZE(bts->gprs.nse.timer)); + memcpy(nanobts_attr_nse+18, bts->gprs.cell.timer, + ARRAY_SIZE(bts->gprs.cell.timer)); + + /* patch NSVCI */ + nanobts_attr_nsvc0[3] = bts->gprs.nsvc[0].nsvci >> 8; + nanobts_attr_nsvc0[4] = bts->gprs.nsvc[0].nsvci & 0xff; + + /* patch IP address as SGSN IP */ + patch_16(nanobts_attr_nsvc0 + 8, + htons(bts->gprs.nsvc[0].remote_port)); + patch_32(nanobts_attr_nsvc0 + 10, + htonl(bts->gprs.nsvc[0].remote_ip)); + patch_16(nanobts_attr_nsvc0 + 14, + htons(bts->gprs.nsvc[0].local_port)); + + /* patch BVCI */ + nanobts_attr_cell[12] = bts->gprs.cell.bvci >> 8; + nanobts_attr_cell[13] = bts->gprs.cell.bvci & 0xff; + /* patch RAC */ + nanobts_attr_cell[3] = bts->gprs.rac; + + if (bts->gprs.mode == BTS_GPRS_EGPRS) { + /* patch EGPRS coding schemes MCS 1..9 */ + nanobts_attr_cell[29] = 0x8f; + nanobts_attr_cell[30] = 0xff; + } +} + + +/* 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) +{ + u_int8_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; + + /* 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) { + patch_nm_tables(bts); + abis_nm_set_bts_attr(bts, nanobts_attr_bts, + sizeof(nanobts_attr_bts)); + 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) { + patch_nm_tables(trx->bts); + enum abis_nm_chan_comb ccomb = + abis_nm_chcomb4pchan(ts->pchan); + abis_nm_set_channel_attr(ts, ccomb); + abis_nm_chg_adm_state(trx->bts, obj_class, + trx->bts->bts_nr, trx->nr, ts->nr, + NM_STATE_UNLOCKED); + abis_nm_opstart(trx->bts, obj_class, + trx->bts->bts_nr, trx->nr, ts->nr); + } + break; + case NM_OC_RADIO_CARRIER: + trx = obj; + if (new_state->operational == NM_OPSTATE_DISABLED && + new_state->availability == NM_AVSTATE_OK) + abis_nm_opstart(trx->bts, obj_class, trx->bts->bts_nr, + trx->nr, 0xff); + break; + case NM_OC_GPRS_NSE: + bts = container_of(obj, struct gsm_bts, gprs.nse); + if (bts->gprs.mode == BTS_GPRS_NONE) + break; + if (new_state->availability == NM_AVSTATE_DEPENDENCY) { + abis_nm_ipaccess_set_attr(bts, obj_class, bts->bts_nr, + 0xff, 0xff, nanobts_attr_nse, + sizeof(nanobts_attr_nse)); + 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) { + abis_nm_ipaccess_set_attr(bts, obj_class, bts->bts_nr, + 0, 0xff, nanobts_attr_cell, + sizeof(nanobts_attr_cell)); + 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) { + abis_nm_ipaccess_set_attr(bts, obj_class, bts->bts_nr, + nsvc->id, 0xff, + nanobts_attr_nsvc0, + sizeof(nanobts_attr_nsvc0)); + 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 gsm_bts *bts = mb->trx->bts; + struct gsm_bts_trx *trx = gsm_bts_trx_num(bts, foh->obj_inst.trx_nr); + + if (!trx) + return -EINVAL; + + if (trx->bts->type != GSM_BTS_TYPE_NANOBTS) + 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, 0, 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->nm_state.administrative; + /* Patch ARFCN into radio attribute */ + nanobts_attr_radio[5] &= 0xf0; + nanobts_attr_radio[5] |= trx->arfcn >> 8; + nanobts_attr_radio[6] = trx->arfcn & 0xff; + abis_nm_set_radio_attr(trx, nanobts_attr_radio, + sizeof(nanobts_attr_radio)); + 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 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; +} + +int bts_model_nanobts_init(void) +{ + model_nanobts.features.data = &model_nanobts._features_data[0]; + model_nanobts.features.data_len = sizeof(model_nanobts._features_data); + + gsm_btsmodel_set_feature(&model_nanobts, BTS_FEAT_GPRS); + gsm_btsmodel_set_feature(&model_nanobts, BTS_FEAT_EGPRS); + + register_signal_handler(SS_NM, nm_sig_cb, NULL); + + return gsm_bts_model_register(&model_nanobts); +} diff --git a/openbsc/src/libbsc/bts_siemens_bs11.c b/openbsc/src/libbsc/bts_siemens_bs11.c new file mode 100644 index 000000000..5a5f88306 --- /dev/null +++ b/openbsc/src/libbsc/bts_siemens_bs11.c @@ -0,0 +1,591 @@ +/* Siemens BS-11 specific code */ + +/* (C) 2009-2010 by Harald Welte + * + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + * + */ + +#include + +#include + +#include +#include +#include +#include +#include + +static struct gsm_bts_model model_bs11 = { + .type = GSM_BTS_TYPE_BS11, + .name = "bs11", + .oml_rcvmsg = &abis_nm_rcvmsg, + .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) +{ + u_int8_t arfcn_low = bts->c0->arfcn & 0xff; + u_int8_t arfcn_high = (bts->c0->arfcn >> 8) & 0x0f; + + /* 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) { + u_int8_t avg_high = bts->rach_ldavg_slots & 0xff; + u_int8_t avg_low = (bts->rach_ldavg_slots >> 8) & 0x0f; + + bs11_attr_bts[35] = avg_high; + bs11_attr_bts[36] = avg_low; + } + + /* patch BSIC */ + bs11_attr_bts[1] = bts->bsic; + + /* patch the power reduction */ + bs11_attr_radio[5] = bts->c0->max_power_red / 2; +} + + +static void nm_reconfig_ts(struct gsm_bts_trx_ts *ts) +{ + enum abis_nm_chan_comb ccomb = abis_nm_chcomb4pchan(ts->pchan); + struct gsm_e1_subslot *e1l = &ts->e1_link; + + abis_nm_set_channel_attr(ts, ccomb); + + if (is_ipaccess_bts(ts->trx->bts)) + return; + + switch (ts->pchan) { + case GSM_PCHAN_TCH_F: + case GSM_PCHAN_TCH_H: + abis_nm_conn_terr_traf(ts, e1l->e1_nr, e1l->e1_ts, + e1l->e1_ts_ss); + break; + default: + break; + } +} + +static void nm_reconfig_trx(struct gsm_bts_trx *trx) +{ + struct gsm_e1_subslot *e1l = &trx->rsl_e1_link; + int i; + + patch_nm_tables(trx->bts); + + switch (trx->bts->type) { + case GSM_BTS_TYPE_BS11: + /* FIXME: discover this by fetching an attribute */ +#if 0 + trx->nominal_power = 15; /* 15dBm == 30mW PA configuration */ +#else + trx->nominal_power = 24; /* 24dBm == 250mW PA configuration */ +#endif + abis_nm_conn_terr_sign(trx, e1l->e1_nr, e1l->e1_ts, + e1l->e1_ts_ss); + abis_nm_establish_tei(trx->bts, trx->nr, e1l->e1_nr, + e1l->e1_ts, e1l->e1_ts_ss, trx->rsl_tei); + + /* Set Radio Attributes */ + if (trx == trx->bts->c0) + abis_nm_set_radio_attr(trx, bs11_attr_radio, + sizeof(bs11_attr_radio)); + else { + u_int8_t trx1_attr_radio[sizeof(bs11_attr_radio)]; + u_int8_t arfcn_low = trx->arfcn & 0xff; + u_int8_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_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_INPUT) + return 0; + + switch (signal) { + case S_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; +} + +int bts_model_bs11_init(void) +{ + 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); + + register_signal_handler(SS_INPUT, inp_sig_cb, NULL); + register_signal_handler(SS_GLOBAL, gbl_sig_cb, NULL); + + return gsm_bts_model_register(&model_bs11); +} diff --git a/openbsc/src/libbsc/bts_unknown.c b/openbsc/src/libbsc/bts_unknown.c new file mode 100644 index 000000000..f95459959 --- /dev/null +++ b/openbsc/src/libbsc/bts_unknown.c @@ -0,0 +1,41 @@ +/* Generic BTS - VTY code tries to allocate this BTS before type is known */ + +/* (C) 2010 by Daniel Willmann + * + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + * + */ + +#include + +#include +#include +#include + +static struct gsm_bts_model model_unknown = { + .type = GSM_BTS_TYPE_UNKNOWN, + .name = "unknown", + .oml_rcvmsg = &abis_nm_rcvmsg, + .nm_att_tlvdef = { + .def = { + }, + }, +}; + +int bts_model_unknown_init(void) +{ + return gsm_bts_model_register(&model_unknown); +} diff --git a/openbsc/src/libbsc/chan_alloc.c b/openbsc/src/libbsc/chan_alloc.c new file mode 100644 index 000000000..167381b37 --- /dev/null +++ b/openbsc/src/libbsc/chan_alloc.c @@ -0,0 +1,507 @@ +/* GSM Channel allocation routines + * + * (C) 2008 by Harald Welte + * (C) 2008, 2009 by Holger Hans Peter Freyther + * + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + * + */ + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include + +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->nm_state)) + return 0; + } + + return 1; +} + +int trx_is_usable(struct gsm_bts_trx *trx) +{ + /* FIXME: How does this behave for BS-11 ? */ + if (is_ipaccess_bts(trx->bts)) { + if (!nm_is_running(&trx->nm_state) || + !nm_is_running(&trx->bb_transc.nm_state)) + return 0; + } + + return 1; +} + +struct gsm_bts_trx_ts *ts_c0_alloc(struct gsm_bts *bts, + enum gsm_phys_chan_config pchan) +{ + struct gsm_bts_trx *trx = bts->c0; + struct gsm_bts_trx_ts *ts = &trx->ts[0]; + + if (pchan != GSM_PCHAN_CCCH && + pchan != GSM_PCHAN_CCCH_SDCCH4) + return NULL; + + if (ts->pchan != GSM_PCHAN_NONE) + return NULL; + + ts->pchan = pchan; + + return ts; +} + +/* Allocate a physical channel (TS) */ +struct gsm_bts_trx_ts *ts_alloc(struct gsm_bts *bts, + enum gsm_phys_chan_config pchan) +{ + int j; + struct gsm_bts_trx *trx; + + llist_for_each_entry(trx, &bts->trx_list, list) { + int from, to; + + if (!trx_is_usable(trx)) + continue; + + /* the following constraints are pure policy, + * no requirement to put this restriction in place */ + if (trx == bts->c0) { + /* On the first TRX we run one CCCH and one SDCCH8 */ + switch (pchan) { + case GSM_PCHAN_CCCH: + case GSM_PCHAN_CCCH_SDCCH4: + from = 0; to = 0; + break; + case GSM_PCHAN_TCH_F: + case GSM_PCHAN_TCH_H: + from = 1; to = 7; + break; + case GSM_PCHAN_SDCCH8_SACCH8C: + default: + return NULL; + } + } else { + /* Every secondary TRX is configured for TCH/F + * and TCH/H only */ + switch (pchan) { + case GSM_PCHAN_SDCCH8_SACCH8C: + from = 1; to = 1; + case GSM_PCHAN_TCH_F: + case GSM_PCHAN_TCH_H: + from = 1; to = 7; + break; + default: + return NULL; + } + } + + for (j = from; j <= to; j++) { + struct gsm_bts_trx_ts *ts = &trx->ts[j]; + + if (!ts_is_usable(ts)) + continue; + + if (ts->pchan == GSM_PCHAN_NONE) { + ts->pchan = pchan; + /* set channel attribute on OML */ + abis_nm_set_channel_attr(ts, abis_nm_chcomb4pchan(pchan)); + return ts; + } + } + } + return NULL; +} + +/* Free a physical channel (TS) */ +void ts_free(struct gsm_bts_trx_ts *ts) +{ + ts->pchan = GSM_PCHAN_NONE; +} + +static const u_int8_t subslots_per_pchan[] = { + [GSM_PCHAN_NONE] = 0, + [GSM_PCHAN_CCCH] = 0, + [GSM_PCHAN_CCCH_SDCCH4] = 4, + [GSM_PCHAN_TCH_F] = 1, + [GSM_PCHAN_TCH_H] = 2, + [GSM_PCHAN_SDCCH8_SACCH8C] = 8, + /* FIXME: what about dynamic TCH_F_TCH_H ? */ + [GSM_PCHAN_TCH_F_PDCH] = 1, +}; + +static struct gsm_lchan * +_lc_find_trx(struct gsm_bts_trx *trx, enum gsm_phys_chan_config pchan) +{ + struct gsm_bts_trx_ts *ts; + int j, ss; + + if (!trx_is_usable(trx)) + return NULL; + + for (j = 0; j < 8; j++) { + ts = &trx->ts[j]; + if (!ts_is_usable(ts)) + continue; + /* ip.access dynamic TCH/F + PDCH combination */ + if (ts->pchan == GSM_PCHAN_TCH_F_PDCH && + pchan == GSM_PCHAN_TCH_F) { + /* we can only consider such a dynamic channel + * if the PDCH is currently inactive */ + if (ts->flags & TS_F_PDCH_MODE) + continue; + } else if (ts->pchan != pchan) + continue; + /* check if all sub-slots are allocated yet */ + for (ss = 0; ss < subslots_per_pchan[pchan]; ss++) { + struct gsm_lchan *lc = &ts->lchan[ss]; + if (lc->type == GSM_LCHAN_NONE && + lc->state == LCHAN_S_NONE) + return lc; + } + } + + return NULL; +} + +static struct gsm_lchan * +_lc_find_bts(struct gsm_bts *bts, enum gsm_phys_chan_config pchan) +{ + struct gsm_bts_trx *trx; + struct gsm_bts_trx_ts *ts; + struct gsm_lchan *lc; + + if (bts->chan_alloc_reverse) { + llist_for_each_entry_reverse(trx, &bts->trx_list, list) { + lc = _lc_find_trx(trx, pchan); + if (lc) + return lc; + } + } else { + llist_for_each_entry(trx, &bts->trx_list, list) { + lc = _lc_find_trx(trx, pchan); + if (lc) + return lc; + } + } + + /* we cannot allocate more of these */ + if (pchan == GSM_PCHAN_CCCH_SDCCH4) + return NULL; + + /* if we've reached here, we need to allocate a new physical + * channel for the logical channel type requested */ + ts = ts_alloc(bts, pchan); + if (!ts) { + /* no more radio resources */ + return NULL; + } + return &ts->lchan[0]; +} + +/* Allocate a logical channel */ +struct gsm_lchan *lchan_alloc(struct gsm_bts *bts, enum gsm_chan_t type, + int allow_bigger) +{ + struct gsm_lchan *lchan = NULL; + enum gsm_phys_chan_config first, second; + + switch (type) { + case GSM_LCHAN_SDCCH: + if (bts->chan_alloc_reverse) { + first = GSM_PCHAN_SDCCH8_SACCH8C; + second = GSM_PCHAN_CCCH_SDCCH4; + } else { + first = GSM_PCHAN_CCCH_SDCCH4; + second = GSM_PCHAN_SDCCH8_SACCH8C; + } + + lchan = _lc_find_bts(bts, first); + if (lchan == NULL) + lchan = _lc_find_bts(bts, second); + + /* allow to assign bigger channels */ + if (allow_bigger) { + if (lchan == NULL) { + lchan = _lc_find_bts(bts, GSM_PCHAN_TCH_H); + type = GSM_LCHAN_TCH_H; + } + + if (lchan == NULL) { + lchan = _lc_find_bts(bts, GSM_PCHAN_TCH_F); + type = GSM_LCHAN_TCH_F; + } + } + break; + case GSM_LCHAN_TCH_F: + lchan = _lc_find_bts(bts, GSM_PCHAN_TCH_F); + break; + case GSM_LCHAN_TCH_H: + lchan =_lc_find_bts(bts, GSM_PCHAN_TCH_H); + /* If we don't have TCH/H available, fall-back to TCH/F */ + if (!lchan) { + lchan = _lc_find_bts(bts, GSM_PCHAN_TCH_F); + type = GSM_LCHAN_TCH_F; + } + break; + default: + LOGP(DRLL, LOGL_ERROR, "Unknown gsm_chan_t %u\n", type); + } + + if (lchan) { + lchan->type = type; + + /* clear sapis */ + memset(lchan->sapis, 0, ARRAY_SIZE(lchan->sapis)); + + /* clear multi rate config */ + memset(&lchan->mr_conf, 0, sizeof(lchan->mr_conf)); + } else { + struct challoc_signal_data sig; + sig.bts = bts; + sig.type = type; + dispatch_signal(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; + dispatch_signal(SS_LCHAN, S_LCHAN_UNEXPECTED_RELEASE, &sig); + } + + + /* stop the timer */ + bsc_del_timer(&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; + dispatch_signal(SS_CHALLOC, S_CHALLOC_FREED, &sig); + + if (lchan->conn) { + LOGP(DRLL, LOGL_ERROR, "the subscriber connection should be gone.\n"); + lchan->conn = NULL; + } + + lchan->sach_deact = 0; + lchan->release_reason = 0; + + /* 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) +{ + bsc_del_timer(&lchan->T3101); + bsc_del_timer(&lchan->T3111); + bsc_del_timer(&lchan->error_timer); + + lchan->type = GSM_LCHAN_NONE; + lchan->state = LCHAN_S_NONE; +} + +/* release the next allocated SAPI or return 0 */ +static int _lchan_release_next_sapi(struct gsm_lchan *lchan) +{ + int sapi; + + for (sapi = 1; sapi < ARRAY_SIZE(lchan->sapis); ++sapi) { + u_int8_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, lchan->release_reason); + return 0; + } + + return 1; +} + +/* Drive the release process of the lchan */ +static void _lchan_handle_release(struct gsm_lchan *lchan) +{ + /* Ask for SAPI != 0 to be freed first and stop if we need to wait */ + if (_lchan_release_next_sapi(lchan) == 0) + return; + + if (lchan->sach_deact) { + gsm48_send_rr_release(lchan); + return; + } + + rsl_release_request(lchan, 0, lchan->release_reason); + rsl_lchan_set_state(lchan, LCHAN_S_REL_REQ); +} + +/* called from abis rsl */ +int rsl_lchan_rll_release(struct gsm_lchan *lchan, u_int8_t link_id) +{ + if (lchan->state != LCHAN_S_REL_REQ) + return -1; + + if ((link_id & 0x7) != 0) + _lchan_handle_release(lchan); + return 0; +} + +/* Consider releasing the channel now */ +int lchan_release(struct gsm_lchan *lchan, int sach_deact, int reason) +{ + DEBUGP(DRLL, "%s starting release sequence\n", gsm_lchan_name(lchan)); + rsl_lchan_set_state(lchan, LCHAN_S_REL_REQ); + + lchan->conn = NULL; + lchan->release_reason = reason; + lchan->sach_deact = sach_deact; + _lchan_handle_release(lchan); + return 1; +} + +static struct gsm_lchan* lchan_find(struct gsm_bts *bts, struct gsm_subscriber *subscr) { + struct gsm_bts_trx *trx; + int ts_no, lchan_no; + + llist_for_each_entry(trx, &bts->trx_list, list) { + for (ts_no = 0; ts_no < 8; ++ts_no) { + for (lchan_no = 0; lchan_no < TS_MAX_LCHAN; ++lchan_no) { + struct gsm_lchan *lchan = + &trx->ts[ts_no].lchan[lchan_no]; + if (lchan->conn && subscr == lchan->conn->subscr) + return lchan; + } + } + } + + return NULL; +} + +struct gsm_subscriber_connection *connection_for_subscr(struct gsm_subscriber *subscr) +{ + struct gsm_bts *bts; + struct gsm_network *net = subscr->net; + struct gsm_lchan *lchan; + + llist_for_each_entry(bts, &net->bts_list, list) { + lchan = lchan_find(bts, subscr); + if (lchan) + return lchan->conn; + } + + return NULL; +} + +void bts_chan_load(struct pchan_load *cl, const struct gsm_bts *bts) +{ + struct gsm_bts_trx *trx; + + llist_for_each_entry(trx, &bts->trx_list, list) { + int i; + + /* skip administratively deactivated tranxsceivers */ + if (!nm_is_running(&trx->nm_state) || + !nm_is_running(&trx->bb_transc.nm_state)) + continue; + + for (i = 0; i < ARRAY_SIZE(trx->ts); i++) { + struct gsm_bts_trx_ts *ts = &trx->ts[i]; + struct load_counter *pl = &cl->pchan[ts->pchan]; + int j; + + /* skip administratively deactivated timeslots */ + if (!nm_is_running(&ts->nm_state)) + continue; + + for (j = 0; j < subslots_per_pchan[ts->pchan]; j++) { + struct gsm_lchan *lchan = &ts->lchan[j]; + + pl->total++; + + switch (lchan->state) { + case LCHAN_S_NONE: + break; + default: + pl->used++; + break; + } + } + } + } +} + +void network_chan_load(struct pchan_load *pl, struct gsm_network *net) +{ + struct gsm_bts *bts; + + memset(pl, 0, sizeof(*pl)); + + llist_for_each_entry(bts, &net->bts_list, list) + bts_chan_load(pl, bts); +} + diff --git a/openbsc/src/libbsc/e1_config.c b/openbsc/src/libbsc/e1_config.c new file mode 100644 index 000000000..958839dcc --- /dev/null +++ b/openbsc/src/libbsc/e1_config.c @@ -0,0 +1,296 @@ +/* OpenBSC E1 Input code */ + +/* (C) 2008-2010 by Harald Welte + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + * + */ + +#include +#include + +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#define SAPI_L2ML 0 +#define SAPI_OML 62 +#define SAPI_RSL 0 /* 63 ? */ + +/* The e1_reconfig_*() functions below tale the configuration present in the + * bts/trx/ts data structures and ensure the E1 configuration reflects the + * timeslot/subslot/TEI configuration */ + +int e1_reconfig_ts(struct gsm_bts_trx_ts *ts) +{ + struct gsm_e1_subslot *e1_link = &ts->e1_link; + struct e1inp_line *line; + struct e1inp_ts *e1_ts; + + DEBUGP(DMI, "e1_reconfig_ts(%u,%u,%u)\n", ts->trx->bts->nr, ts->trx->nr, ts->nr); + + if (!e1_link->e1_ts) { + LOGP(DINP, LOGL_ERROR, "TS (%u/%u/%u) without E1 timeslot?\n", + ts->nr, ts->trx->nr, ts->trx->bts->nr); + return 0; + } + + line = e1inp_line_get(e1_link->e1_nr); + if (!line) { + LOGP(DINP, LOGL_ERROR, "TS (%u/%u/%u) referring to " + "non-existing E1 line %u\n", ts->nr, ts->trx->nr, + ts->trx->bts->nr, e1_link->e1_nr); + return -ENOMEM; + } + + switch (ts->pchan) { + case GSM_PCHAN_TCH_F: + case GSM_PCHAN_TCH_H: + e1_ts = &line->ts[e1_link->e1_ts-1]; + e1inp_ts_config(e1_ts, line, E1INP_TS_TYPE_TRAU); + subch_demux_activate(&e1_ts->trau.demux, e1_link->e1_ts_ss); + break; + default: + break; + } + + return 0; +} + +int e1_reconfig_trx(struct gsm_bts_trx *trx) +{ + struct gsm_e1_subslot *e1_link = &trx->rsl_e1_link; + struct e1inp_ts *sign_ts; + struct e1inp_line *line; + struct e1inp_sign_link *rsl_link; + int i; + + if (!e1_link->e1_ts) { + LOGP(DINP, LOGL_ERROR, "TRX (%u/%u) RSL link without " + "timeslot?\n", trx->bts->nr, trx->nr); + return -EINVAL; + } + + /* RSL Link */ + line = e1inp_line_get(e1_link->e1_nr); + if (!line) { + LOGP(DINP, 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_ts, line, E1INP_TS_TYPE_SIGN); + /* 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(DINP, 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(DINP, 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; +} + +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(DMI, "e1_reconfig_bts(%u)\n", bts->nr); + + if (!e1_link->e1_ts) { + LOGP(DINP, LOGL_ERROR, "BTS %u OML link without timeslot?\n", + bts->nr); + return -EINVAL; + } + + /* OML link */ + line = e1inp_line_get(e1_link->e1_nr); + if (!line) { + LOGP(DINP, LOGL_ERROR, "BTS %u OML link referring to " + "non-existing E1 line %u\n", bts->nr, e1_link->e1_nr); + return -ENOMEM; + } + sign_ts = &line->ts[e1_link->e1_ts-1]; + e1inp_ts_config(sign_ts, line, E1INP_TS_TYPE_SIGN); + oml_link = e1inp_sign_link_create(sign_ts, E1INP_SIGN_OML, + bts->c0, bts->oml_tei, SAPI_OML); + if (!oml_link) { + LOGP(DINP, 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 + +/* configure pseudo E1 line in ip.access style and connect to BTS */ +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; + + /* create E1 timeslots for signalling and TRAU frames */ + e1inp_ts_config(&line->ts[1-1], line, E1INP_TS_TYPE_SIGN); + e1inp_ts_config(&line->ts[2-1], line, E1INP_TS_TYPE_SIGN); + + /* 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); +} diff --git a/openbsc/src/libbsc/gsm_04_08_utils.c b/openbsc/src/libbsc/gsm_04_08_utils.c new file mode 100644 index 000000000..6d12cc08e --- /dev/null +++ b/openbsc/src/libbsc/gsm_04_08_utils.c @@ -0,0 +1,655 @@ +/* GSM Mobile Radio Interface Layer 3 messages on the A-bis interface + * 3GPP TS 04.08 version 7.21.0 Release 1998 / ETSI TS 100 940 V7.21.0 + * utility functions + */ + +/* (C) 2008-2009 by Harald Welte + * (C) 2008, 2009 by Holger Hans Peter Freyther + * + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + * + */ +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include +#include +#include + +/* 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->trx = msg->lchan->ts->trx; + + msg->l3h = msg->data; + return rsl_data_request(msg, 0); +} + +/* Section 9.1.8 / Table 9.9 */ +struct chreq { + u_int8_t val; + u_int8_t mask; + enum chreq_type type; +}; + +/* If SYSTEM INFORMATION TYPE 4 NECI bit == 1 */ +static const struct chreq chreq_type_neci1[] = { + { 0xa0, 0xe0, CHREQ_T_EMERG_CALL }, + { 0xc0, 0xe0, CHREQ_T_CALL_REEST_TCH_F }, + { 0x68, 0xfc, CHREQ_T_CALL_REEST_TCH_H }, + { 0x6c, 0xfc, CHREQ_T_CALL_REEST_TCH_H_DBL }, + { 0xe0, 0xe0, CHREQ_T_TCH_F }, + { 0x40, 0xf0, CHREQ_T_VOICE_CALL_TCH_H }, + { 0x50, 0xf0, CHREQ_T_DATA_CALL_TCH_H }, + { 0x00, 0xf0, CHREQ_T_LOCATION_UPD }, + { 0x10, 0xf0, CHREQ_T_SDCCH }, + { 0x80, 0xe0, CHREQ_T_PAG_R_ANY_NECI1 }, + { 0x20, 0xf0, CHREQ_T_PAG_R_TCH_F }, + { 0x30, 0xf0, CHREQ_T_PAG_R_TCH_FH }, + { 0x67, 0xff, CHREQ_T_LMU }, + { 0x60, 0xf9, CHREQ_T_RESERVED_SDCCH }, + { 0x61, 0xfb, CHREQ_T_RESERVED_SDCCH }, + { 0x63, 0xff, CHREQ_T_RESERVED_SDCCH }, + { 0x7f, 0xff, CHREQ_T_RESERVED_IGNORE }, +}; + +/* If SYSTEM INFORMATION TYPE 4 NECI bit == 0 */ +static const struct chreq chreq_type_neci0[] = { + { 0xa0, 0xe0, CHREQ_T_EMERG_CALL }, + { 0xc0, 0xe0, CHREQ_T_CALL_REEST_TCH_H }, + { 0xe0, 0xe0, CHREQ_T_TCH_F }, + { 0x50, 0xf0, CHREQ_T_DATA_CALL_TCH_H }, + { 0x00, 0xe0, CHREQ_T_LOCATION_UPD }, + { 0x80, 0xe0, CHREQ_T_PAG_R_ANY_NECI0 }, + { 0x20, 0xf0, CHREQ_T_PAG_R_TCH_F }, + { 0x30, 0xf0, CHREQ_T_PAG_R_TCH_FH }, + { 0x67, 0xff, CHREQ_T_LMU }, + { 0x60, 0xf9, CHREQ_T_RESERVED_SDCCH }, + { 0x61, 0xfb, CHREQ_T_RESERVED_SDCCH }, + { 0x63, 0xff, CHREQ_T_RESERVED_SDCCH }, + { 0x7f, 0xff, CHREQ_T_RESERVED_IGNORE }, +}; + +static const enum gsm_chan_t ctype_by_chreq[] = { + [CHREQ_T_EMERG_CALL] = GSM_LCHAN_TCH_F, + [CHREQ_T_CALL_REEST_TCH_F] = GSM_LCHAN_TCH_F, + [CHREQ_T_CALL_REEST_TCH_H] = GSM_LCHAN_TCH_H, + [CHREQ_T_CALL_REEST_TCH_H_DBL] = GSM_LCHAN_TCH_H, + [CHREQ_T_SDCCH] = GSM_LCHAN_SDCCH, + [CHREQ_T_TCH_F] = GSM_LCHAN_TCH_F, + [CHREQ_T_VOICE_CALL_TCH_H] = GSM_LCHAN_TCH_H, + [CHREQ_T_DATA_CALL_TCH_H] = GSM_LCHAN_TCH_H, + [CHREQ_T_LOCATION_UPD] = GSM_LCHAN_SDCCH, + [CHREQ_T_PAG_R_ANY_NECI1] = GSM_LCHAN_SDCCH, + [CHREQ_T_PAG_R_ANY_NECI0] = GSM_LCHAN_SDCCH, + [CHREQ_T_PAG_R_TCH_F] = GSM_LCHAN_TCH_F, + [CHREQ_T_PAG_R_TCH_FH] = GSM_LCHAN_TCH_F, + [CHREQ_T_LMU] = GSM_LCHAN_SDCCH, + [CHREQ_T_RESERVED_SDCCH] = GSM_LCHAN_SDCCH, + [CHREQ_T_RESERVED_IGNORE] = GSM_LCHAN_UNKNOWN, +}; + +static const enum gsm_chreq_reason_t reason_by_chreq[] = { + [CHREQ_T_EMERG_CALL] = GSM_CHREQ_REASON_EMERG, + [CHREQ_T_CALL_REEST_TCH_F] = GSM_CHREQ_REASON_CALL, + [CHREQ_T_CALL_REEST_TCH_H] = GSM_CHREQ_REASON_CALL, + [CHREQ_T_CALL_REEST_TCH_H_DBL] = GSM_CHREQ_REASON_CALL, + [CHREQ_T_SDCCH] = GSM_CHREQ_REASON_OTHER, + [CHREQ_T_TCH_F] = GSM_CHREQ_REASON_OTHER, + [CHREQ_T_VOICE_CALL_TCH_H] = GSM_CHREQ_REASON_CALL, + [CHREQ_T_DATA_CALL_TCH_H] = GSM_CHREQ_REASON_OTHER, + [CHREQ_T_LOCATION_UPD] = GSM_CHREQ_REASON_LOCATION_UPD, + [CHREQ_T_PAG_R_ANY_NECI1] = GSM_CHREQ_REASON_PAG, + [CHREQ_T_PAG_R_ANY_NECI0] = GSM_CHREQ_REASON_PAG, + [CHREQ_T_PAG_R_TCH_F] = GSM_CHREQ_REASON_PAG, + [CHREQ_T_PAG_R_TCH_FH] = GSM_CHREQ_REASON_PAG, + [CHREQ_T_LMU] = GSM_CHREQ_REASON_OTHER, + [CHREQ_T_RESERVED_SDCCH] = GSM_CHREQ_REASON_OTHER, + [CHREQ_T_RESERVED_IGNORE] = GSM_CHREQ_REASON_OTHER, +}; + +/* verify that the two tables match */ +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, u_int8_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; +} + +enum gsm_chreq_reason_t get_reason_by_chreq(u_int8_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; +} + +/* 7.1.7 and 9.1.7: RR CHANnel RELease */ +int gsm48_send_rr_release(struct gsm_lchan *lchan) +{ + struct msgb *msg = gsm48_msgb_alloc(); + struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh)); + u_int8_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 */ + gsm48_sendmsg(msg); + /* FIXME: Start Timer T3109 */ + + /* Deactivate the SACCH on the BTS side */ + return rsl_deact_sacch(lchan); +} + +int send_siemens_mrpci(struct gsm_lchan *lchan, + u_int8_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; + + u_int8_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, u_int8_t *mi_type) +{ + static const uint32_t classmark_offset = + offsetof(struct gsm48_pag_resp, classmark2); + u_int8_t *classmark2_lv = (uint8_t *) &resp->classmark2; + return gsm48_extract_mi(classmark2_lv, length - classmark_offset, + mi_string, mi_type); +} + +int gsm48_handle_paging_resp(struct gsm_subscriber_connection *conn, + struct msgb *msg, struct gsm_subscriber *subscr) +{ + struct gsm_bts *bts = msg->lchan->ts->trx->bts; + struct gsm48_hdr *gh = msgb_l3(msg); + u_int8_t *classmark2_lv = gh->data + 1; + + if (is_siemens_bts(bts)) + send_siemens_mrpci(msg->lchan, classmark2_lv); + + if (!conn->subscr) { + conn->subscr = subscr; + } else if (conn->subscr != subscr) { + LOGP(DRR, LOGL_ERROR, "<- Channel already owned by someone else?\n"); + subscr_put(subscr); + return -EINVAL; + } else { + DEBUGP(DRR, "<- Channel already owned by us\n"); + subscr_put(subscr); + subscr = conn->subscr; + } + + counter_inc(bts->network->stats.paging.completed); + + /* Stop paging on the bts we received the paging response */ + paging_request_stop(conn->bts, subscr, conn, msg); + return 0; +} + +/* Chapter 9.1.9: Ciphering Mode Command */ +int gsm48_send_rr_ciph_mode(struct gsm_lchan *lchan, int want_imeisv) +{ + struct msgb *msg = gsm48_msgb_alloc(); + struct gsm48_hdr *gh; + u_int8_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) +{ + u_int16_t arfcn = lchan->ts->trx->arfcn & 0x3ff; + + cd->chan_nr = lchan2chan_nr(lchan); + if (!lchan->ts->hopping.enabled) { + cd->h0.tsc = lchan->ts->trx->bts->tsc; + cd->h0.h = 0; + cd->h0.arfcn_high = arfcn >> 8; + cd->h0.arfcn_low = arfcn & 0xff; + } else { + cd->h1.tsc = lchan->ts->trx->bts->tsc; + 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; + } +} + +#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, + u_int8_t power_command, u_int8_t ho_ref) +{ + struct msgb *msg = gsm48_msgb_alloc(); + struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh)); + struct gsm48_ho_cmd *ho = + (struct gsm48_ho_cmd *) msgb_put(msg, sizeof(*ho)); + + msg->lchan = old_lchan; + gh->proto_discr = GSM48_PDISC_RR; + gh->msg_type = GSM48_MT_RR_HANDO_CMD; + + /* mandatory bits */ + gsm48_cell_desc(&ho->cell_desc, new_lchan->ts->trx->bts); + gsm48_lchan2chan_desc(&ho->chan_desc, new_lchan); + ho->ho_ref = ho_ref; + ho->power_command = power_command; + + if (new_lchan->ts->hopping.enabled) { + struct gsm_bts *bts = new_lchan->ts->trx->bts; + struct gsm48_system_information_type_1 *si1; + uint8_t *cur; + + si1 = GSM_BTS_SI(bts, SYSINFO_TYPE_1); + /* Copy the Cell Chan Desc (ARFCNS in this cell) */ + msgb_put_u8(msg, GSM48_IE_CELL_CH_DESC); + cur = msgb_put(msg, GSM48_HOCMD_CCHDESC_LEN); + memcpy(cur, si1->cell_channel_description, + GSM48_HOCMD_CCHDESC_LEN); + /* Copy the Mobile Allocation */ + msgb_tlv_put(msg, GSM48_IE_MA_BEFORE, + new_lchan->ts->hopping.ma_len, + new_lchan->ts->hopping.ma_data); + } + /* FIXME: optional bits for type of synchronization? */ + + return gsm48_sendmsg(msg); +} + +/* Chapter 9.1.2: Assignment Command */ +int gsm48_send_rr_ass_cmd(struct gsm_lchan *dest_lchan, struct gsm_lchan *lchan, u_int8_t power_command) +{ + struct msgb *msg = gsm48_msgb_alloc(); + struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh)); + struct gsm48_ass_cmd *ass = + (struct gsm48_ass_cmd *) msgb_put(msg, sizeof(*ass)); + + DEBUGP(DRR, "-> ASSIGNMENT COMMAND tch_mode=0x%02x\n", lchan->tch_mode); + + msg->lchan = dest_lchan; + gh->proto_discr = GSM48_PDISC_RR; + gh->msg_type = GSM48_MT_RR_ASS_CMD; + + /* + * fill the channel information element, this code + * should probably be shared with rsl_rx_chan_rqd(), + * gsm48_tx_chan_mode_modify. But beware that 10.5.2.5 + * 10.5.2.5.a have slightly different semantic for + * the chan_desc. But as long as multi-slot configurations + * are not used we seem to be fine. + */ + gsm48_lchan2chan_desc(&ass->chan_desc, lchan); + ass->power_command = power_command; + + /* optional: cell channel description */ + + msgb_tv_put(msg, GSM48_IE_CHANMODE_1, lchan->tch_mode); + + /* mobile allocation in case of hopping */ + if (lchan->ts->hopping.enabled) { + msgb_tlv_put(msg, GSM48_IE_MA_BEFORE, lchan->ts->hopping.ma_len, + lchan->ts->hopping.ma_data); + } + + /* in case of multi rate we need to attach a config */ + if (lchan->tch_mode == GSM48_CMODE_SPEECH_AMR) { + if (lchan->mr_conf.ver == 0) { + LOGP(DRR, LOGL_ERROR, "BUG: Using multirate codec " + "without multirate config.\n"); + } else { + u_int8_t *data = msgb_put(msg, 4); + data[0] = GSM48_IE_MUL_RATE_CFG; + data[1] = 0x2; + memcpy(&data[2], &lchan->mr_conf, 2); + } + } + + return gsm48_sendmsg(msg); +} + +/* 9.1.5 Channel mode modify: Modify the mode on the MS side */ +int gsm48_tx_chan_mode_modify(struct gsm_lchan *lchan, u_int8_t mode) +{ + struct msgb *msg = gsm48_msgb_alloc(); + struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh)); + struct gsm48_chan_mode_modify *cmm = + (struct gsm48_chan_mode_modify *) msgb_put(msg, sizeof(*cmm)); + + DEBUGP(DRR, "-> CHANNEL MODE MODIFY mode=0x%02x\n", mode); + + lchan->tch_mode = mode; + msg->lchan = lchan; + gh->proto_discr = GSM48_PDISC_RR; + gh->msg_type = GSM48_MT_RR_CHAN_MODE_MODIF; + + /* fill the channel information element, this code + * should probably be shared with rsl_rx_chan_rqd() */ + gsm48_lchan2chan_desc(&cmm->chan_desc, lchan); + cmm->mode = mode; + + /* in case of multi rate we need to attach a config */ + if (lchan->tch_mode == GSM48_CMODE_SPEECH_AMR) { + if (lchan->mr_conf.ver == 0) { + LOGP(DRR, LOGL_ERROR, "BUG: Using multirate codec " + "without multirate config.\n"); + } else { + u_int8_t *data = msgb_put(msg, 4); + data[0] = GSM48_IE_MUL_RATE_CFG; + data[1] = 0x2; + memcpy(&data[2], &lchan->mr_conf, 2); + } + } + + return gsm48_sendmsg(msg); +} + +int gsm48_lchan_modify(struct gsm_lchan *lchan, u_int8_t lchan_mode) +{ + int rc; + + rc = gsm48_tx_chan_mode_modify(lchan, lchan_mode); + if (rc < 0) + return rc; + + return rc; +} + +int gsm48_rx_rr_modif_ack(struct msgb *msg) +{ + int rc; + struct gsm48_hdr *gh = msgb_l3(msg); + struct gsm48_chan_mode_modify *mod = + (struct gsm48_chan_mode_modify *) gh->data; + + DEBUGP(DRR, "CHANNEL MODE MODIFY ACK\n"); + + if (mod->mode != msg->lchan->tch_mode) { + LOGP(DRR, LOGL_ERROR, "CHANNEL MODE change failed. Wanted: %d Got: %d\n", + msg->lchan->tch_mode, mod->mode); + return -1; + } + + /* update the channel type */ + switch (mod->mode) { + case GSM48_CMODE_SIGN: + msg->lchan->rsl_cmode = RSL_CMOD_SPD_SIGN; + break; + case GSM48_CMODE_SPEECH_V1: + case GSM48_CMODE_SPEECH_EFR: + case GSM48_CMODE_SPEECH_AMR: + msg->lchan->rsl_cmode = RSL_CMOD_SPD_SPEECH; + break; + case GSM48_CMODE_DATA_14k5: + case GSM48_CMODE_DATA_12k0: + case GSM48_CMODE_DATA_6k0: + case GSM48_CMODE_DATA_3k6: + msg->lchan->rsl_cmode = RSL_CMOD_SPD_DATA; + break; + } + + /* We've successfully modified the MS side of the channel, + * now go on to modify the BTS side of the channel */ + rc = rsl_chan_mode_modify_req(msg->lchan); + + /* FIXME: we not only need to do this after mode modify, but + * also after channel activation */ + if (is_ipaccess_bts(msg->lchan->ts->trx->bts) && mod->mode != GSM48_CMODE_SIGN) + rsl_ipacc_crcx(msg->lchan); + return rc; +} + +int gsm48_parse_meas_rep(struct gsm_meas_rep *rep, struct msgb *msg) +{ + struct gsm48_hdr *gh = msgb_l3(msg); + u_int8_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[3] >> 4) & 0x7; + rep->dl.sub.rx_qual = (data[3] >> 1) & 0x7; + + rep->num_cell = ((data[3] >> 6) & 0x3) | ((data[2] & 0x01) << 2); + if (rep->num_cell < 1 || rep->num_cell > 6) + return 0; + + /* an encoding nightmare in perfection */ + mrc = &rep->cell[0]; + mrc->rxlev = data[3] & 0x3f; + mrc->neigh_idx = data[4] >> 3; + mrc->arfcn = bitvec_get_nth_set_bit(nbv, mrc->neigh_idx + 1); + mrc->bsic = ((data[4] & 0x07) << 3) | (data[5] >> 5); + if (rep->num_cell < 2) + return 0; + + mrc = &rep->cell[1]; + mrc->rxlev = ((data[5] & 0x1f) << 1) | (data[6] >> 7); + mrc->neigh_idx = (data[6] >> 2) & 0x1f; + mrc->arfcn = bitvec_get_nth_set_bit(nbv, mrc->neigh_idx + 1); + mrc->bsic = ((data[6] & 0x03) << 4) | (data[7] >> 4); + if (rep->num_cell < 3) + return 0; + + mrc = &rep->cell[2]; + mrc->rxlev = ((data[7] & 0x0f) << 2) | (data[8] >> 6); + mrc->neigh_idx = (data[8] >> 1) & 0x1f; + mrc->arfcn = bitvec_get_nth_set_bit(nbv, mrc->neigh_idx + 1); + mrc->bsic = ((data[8] & 0x01) << 5) | (data[9] >> 3); + if (rep->num_cell < 4) + return 0; + + mrc = &rep->cell[3]; + mrc->rxlev = ((data[9] & 0x07) << 3) | (data[10] >> 5); + mrc->neigh_idx = data[10] & 0x1f; + mrc->arfcn = bitvec_get_nth_set_bit(nbv, mrc->neigh_idx + 1); + mrc->bsic = data[11] >> 2; + if (rep->num_cell < 5) + return 0; + + mrc = &rep->cell[4]; + mrc->rxlev = ((data[11] & 0x03) << 4) | (data[12] >> 4); + mrc->neigh_idx = ((data[12] & 0xf) << 1) | (data[13] >> 7); + mrc->arfcn = bitvec_get_nth_set_bit(nbv, mrc->neigh_idx + 1); + mrc->bsic = (data[13] >> 1) & 0x3f; + if (rep->num_cell < 6) + return 0; + + mrc = &rep->cell[5]; + mrc->rxlev = ((data[13] & 0x01) << 5) | (data[14] >> 3); + mrc->neigh_idx = ((data[14] & 0x07) << 2) | (data[15] >> 6); + mrc->arfcn = bitvec_get_nth_set_bit(nbv, mrc->neigh_idx + 1); + mrc->bsic = data[15] & 0x3f; + + return 0; +} + +struct msgb *gsm48_create_mm_serv_rej(enum gsm48_reject_value value) +{ + struct msgb *msg; + struct gsm48_hdr *gh; + + msg = gsm48_msgb_alloc(); + if (!msg) + return NULL; + + gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh) + 1); + gh->proto_discr = GSM48_PDISC_MM; + gh->msg_type = GSM48_MT_MM_CM_SERV_REJ; + gh->data[0] = value; + + return msg; +} + +struct msgb *gsm48_create_loc_upd_rej(uint8_t cause) +{ + struct gsm48_hdr *gh; + struct msgb *msg; + + msg = gsm48_msgb_alloc(); + if (!msg) + return NULL; + + gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh) + 1); + gh->proto_discr = GSM48_PDISC_MM; + gh->msg_type = GSM48_MT_MM_LOC_UPD_REJECT; + gh->data[0] = cause; + return msg; +} diff --git a/openbsc/src/libbsc/gsm_subscriber_base.c b/openbsc/src/libbsc/gsm_subscriber_base.c new file mode 100644 index 000000000..caf84e7bb --- /dev/null +++ b/openbsc/src/libbsc/gsm_subscriber_base.c @@ -0,0 +1,149 @@ +/* The concept of a subscriber as seen by the BSC */ + +/* (C) 2008 by Harald Welte + * (C) 2009-2010 by Holger Hans Peter Freyther + * (C) 2010 by On-Waves + * + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + * + */ + +#include +#include +#include +#include +#include + +#include +#include +#include + +LLIST_HEAD(active_subscribers); +void *tall_subscr_ctx; + +/* for the gsm_subscriber.c */ +struct llist_head *subscr_bsc_active_subscriber(void) +{ + return &active_subscribers; +} + + +char *subscr_name(struct gsm_subscriber *subscr) +{ + if (strlen(subscr->name)) + return subscr->name; + + return subscr->imsi; +} + +struct gsm_subscriber *subscr_alloc(void) +{ + struct gsm_subscriber *s; + + s = talloc_zero(tall_subscr_ctx, struct gsm_subscriber); + if (!s) + return NULL; + + llist_add_tail(&s->entry, &active_subscribers); + s->use_count = 1; + s->tmsi = GSM_RESERVED_TMSI; + + INIT_LLIST_HEAD(&s->requests); + + return s; +} + +static void subscr_free(struct gsm_subscriber *subscr) +{ + llist_del(&subscr->entry); + talloc_free(subscr); +} + +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->net->keep_subscr) + subscr_free(subscr); + return NULL; +} + +struct gsm_subscriber *subscr_get_or_create(struct gsm_network *net, + const char *imsi) +{ + struct gsm_subscriber *subscr; + + llist_for_each_entry(subscr, subscr_bsc_active_subscriber(), entry) { + if (strcmp(subscr->imsi, imsi) == 0 && subscr->net == net) + return subscr_get(subscr); + } + + subscr = subscr_alloc(); + if (!subscr) + return NULL; + + strcpy(subscr->imsi, imsi); + subscr->net = net; + return subscr; +} + +struct gsm_subscriber *subscr_active_by_tmsi(struct gsm_network *net, uint32_t tmsi) +{ + struct gsm_subscriber *subscr; + + llist_for_each_entry(subscr, subscr_bsc_active_subscriber(), entry) { + if (subscr->tmsi == tmsi && subscr->net == net) + return subscr_get(subscr); + } + + return NULL; +} + +struct gsm_subscriber *subscr_active_by_imsi(struct gsm_network *net, const char *imsi) +{ + struct gsm_subscriber *subscr; + + llist_for_each_entry(subscr, subscr_bsc_active_subscriber(), entry) { + if (strcmp(subscr->imsi, imsi) == 0 && subscr->net == net) + return subscr_get(subscr); + } + + return NULL; +} + +int subscr_purge_inactive(struct gsm_network *net) +{ + struct gsm_subscriber *subscr, *tmp; + int purged = 0; + + llist_for_each_entry_safe(subscr, tmp, subscr_bsc_active_subscriber(), entry) { + if (subscr->net == net && subscr->use_count <= 0) { + subscr_free(subscr); + purged += 1; + } + } + + return purged; +} diff --git a/openbsc/src/libbsc/handover_decision.c b/openbsc/src/libbsc/handover_decision.c new file mode 100644 index 000000000..d3f843afb --- /dev/null +++ b/openbsc/src/libbsc/handover_decision.c @@ -0,0 +1,297 @@ +/* Handover Decision making for Inter-BTS (Intra-BSC) Handover. This + * only implements the handover algorithm/decision, but not execution + * of it */ + +/* (C) 2009 by Harald Welte + * + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + * + */ + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +/* issue handover to a cell identified by ARFCN and BSIC */ +static int handover_to_arfcn_bsic(struct gsm_lchan *lchan, + u_int16_t arfcn, u_int8_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, + u_int16_t arfcn, u_int8_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; + + /* 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 (avg < worst) { + worst = avg; + nmp_worst = nmp; + } + } + + return nmp_worst; +} + +/* process neighbor cell measurement reports */ +static void process_meas_neigh(struct gsm_meas_rep *mr) +{ + int i, j, idx; + + /* for each reported cell, try to update global state */ + for (j = 0; j < ARRAY_SIZE(mr->lchan->neigh_meas); j++) { + struct neigh_meas_proc *nmp = &mr->lchan->neigh_meas[j]; + unsigned int idx; + int rxlev; + + /* skip unused entries */ + if (!nmp->arfcn) + continue; + + rxlev = rxlev_for_cell_in_rep(mr, nmp->arfcn, nmp->bsic); + idx = nmp->rxlev_cnt % ARRAY_SIZE(nmp->rxlev); + if (rxlev >= 0) { + nmp->rxlev[idx] = rxlev; + nmp->last_seen_nr = mr->nr; + } else + nmp->rxlev[idx] = 0; + nmp->rxlev_cnt++; + } + + /* iterate over list of reported cells, check if we did not + * process all of them */ + for (i = 0; i < mr->num_cell; i++) { + struct gsm_meas_rep_cell *mrc = &mr->cell[i]; + struct neigh_meas_proc *nmp; + + if (mrc->flags & MRC_F_PROCESSED) + continue; + + nmp = find_evict_neigh(mr->lchan); + + nmp->arfcn = mrc->arfcn; + nmp->bsic = mrc->bsic; + + idx = nmp->rxlev_cnt % ARRAY_SIZE(nmp->rxlev); + nmp->rxlev[idx] = mrc->rxlev; + nmp->rxlev_cnt++; + nmp->last_seen_nr = mr->nr; + + mrc->flags |= MRC_F_PROCESSED; + } +} + +/* attempt to do a handover */ +static int attempt_handover(struct gsm_meas_rep *mr) +{ + struct gsm_network *net = mr->lchan->ts->trx->bts->network; + struct neigh_meas_proc *best_cell = NULL; + unsigned int best_better_db = 0; + int i, rc; + + /* find the best cell in this report that is at least RXLEV_HYST + * better than the current serving cell */ + + for (i = 0; i < ARRAY_SIZE(mr->lchan->neigh_meas); i++) { + struct neigh_meas_proc *nmp = &mr->lchan->neigh_meas[i]; + int avg, better; + + /* skip empty slots */ + if (nmp->arfcn == 0) + continue; + + /* caculate average rxlev for this cell over the window */ + avg = neigh_meas_avg(nmp, net->handover.win_rxlev_avg_neigh); + + /* check if hysteresis is fulfilled */ + if (avg < mr->dl.full.rx_lev + net->handover.pwr_hysteresis) + continue; + + better = avg - mr->dl.full.rx_lev; + if (better > best_better_db) { + best_cell = nmp; + best_better_db = better; + } + } + + if (!best_cell) + return 0; + + LOGP(DHO, LOGL_INFO, "%s: Cell on ARFCN %u is better: ", + gsm_ts_name(mr->lchan->ts), best_cell->arfcn); + if (!net->handover.active) { + LOGPC(DHO, LOGL_INFO, "Skipping, Handover disabled\n"); + return 0; + } + + rc = handover_to_arfcn_bsic(mr->lchan, best_cell->arfcn, best_cell->bsic); + switch (rc) { + case 0: + LOGPC(DHO, LOGL_INFO, "Starting handover\n"); + break; + case -ENOSPC: + LOGPC(DHO, LOGL_INFO, "No channel available\n"); + break; + case -EBUSY: + LOGPC(DHO, LOGL_INFO, "Handover already active\n"); + break; + default: + LOGPC(DHO, LOGL_ERROR, "Unknown error\n"); + } + return rc; +} + +/* process an already parsed measurement report and decide if we want to + * attempt a handover */ +static int process_meas_rep(struct gsm_meas_rep *mr) +{ + struct gsm_network *net = mr->lchan->ts->trx->bts->network; + int av_rxlev; + + /* we currently only do handover for TCH channels */ + switch (mr->lchan->type) { + case GSM_LCHAN_TCH_F: + case GSM_LCHAN_TCH_H: + break; + default: + return 0; + } + + /* parse actual neighbor cell info */ + if (mr->num_cell > 0 && mr->num_cell < 7) + process_meas_neigh(mr); + + av_rxlev = get_meas_rep_avg(mr->lchan, MEAS_REP_DL_RXLEV_FULL, + net->handover.win_rxlev_avg); + + /* Interference HO */ + if (rxlev2dbm(av_rxlev) > -85 && + meas_rep_n_out_of_m_be(mr->lchan, MEAS_REP_DL_RXQUAL_FULL, + 3, 4, 5)) + return attempt_handover(mr); + + /* Bad Quality */ + if (meas_rep_n_out_of_m_be(mr->lchan, MEAS_REP_DL_RXQUAL_FULL, + 3, 4, 5)) + return attempt_handover(mr); + + /* Low Level */ + if (rxlev2dbm(av_rxlev) <= -110) + return attempt_handover(mr); + + /* Distance */ + if (mr->ms_l1.ta > net->handover.max_distance) + return attempt_handover(mr); + + /* Power Budget AKA Better Cell */ + if ((mr->nr % net->handover.pwr_interval) == 0) + return attempt_handover(mr); + + return 0; + +} + +static int ho_dec_sig_cb(unsigned int subsys, unsigned int signal, + void *handler_data, void *signal_data) +{ + struct lchan_signal_data *lchan_data; + + if (subsys != SS_LCHAN) + return 0; + + lchan_data = signal_data; + switch (signal) { + case S_LCHAN_MEAS_REP: + process_meas_rep(lchan_data->mr); + break; + } + + return 0; +} + +void on_dso_load_ho_dec(void) +{ + register_signal_handler(SS_LCHAN, ho_dec_sig_cb, NULL); +} diff --git a/openbsc/src/libbsc/handover_logic.c b/openbsc/src/libbsc/handover_logic.c new file mode 100644 index 000000000..c2e3f8c72 --- /dev/null +++ b/openbsc/src/libbsc/handover_logic.c @@ -0,0 +1,393 @@ +/* Handover Logic for Inter-BTS (Intra-BSC) Handover. This does not + * actually implement the handover algorithm/decision, but executes a + * handover decision */ + +/* (C) 2009 by Harald Welte + * + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + * + */ + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +struct bsc_handover { + struct llist_head list; + + struct gsm_lchan *old_lchan; + struct gsm_lchan *new_lchan; + + struct timer_list T3103; + + u_int8_t ho_ref; +}; + +static LLIST_HEAD(bsc_handovers); + +static struct bsc_handover *bsc_ho_by_new_lchan(struct gsm_lchan *new_lchan) +{ + struct bsc_handover *ho; + + llist_for_each_entry(ho, &bsc_handovers, list) { + if (ho->new_lchan == new_lchan) + return ho; + } + + return NULL; +} + +static struct bsc_handover *bsc_ho_by_old_lchan(struct gsm_lchan *old_lchan) +{ + struct bsc_handover *ho; + + llist_for_each_entry(ho, &bsc_handovers, list) { + if (ho->old_lchan == old_lchan) + return ho; + } + + return NULL; +} + +/* Hand over the specified logical channel to the specified new BTS. + * This is the main entry point for the actual handover algorithm, + * after it has decided it wants to initiate HO to a specific BTS */ +int bsc_handover_start(struct gsm_lchan *old_lchan, struct gsm_bts *bts) +{ + struct gsm_lchan *new_lchan; + struct bsc_handover *ho; + static u_int8_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); + + counter_inc(bts->network->stats.handover.attempted); + + if (!old_lchan->conn) { + LOGP(DHO, LOGL_ERROR, "Old lchan lacks connection data.\n"); + return -ENOSPC; + } + + new_lchan = lchan_alloc(bts, old_lchan->type, 0); + if (!new_lchan) { + LOGP(DHO, LOGL_NOTICE, "No free channel\n"); + counter_inc(bts->network->stats.handover.no_channel); + return -ENOSPC; + } + + ho = talloc_zero(tall_bsc_ctx, struct bsc_handover); + if (!ho) { + LOGP(DHO, LOGL_FATAL, "Out of Memory\n"); + lchan_free(new_lchan); + return -ENOMEM; + } + ho->old_lchan = old_lchan; + ho->new_lchan = new_lchan; + ho->ho_ref = ho_ref++; + + /* copy some parameters from old lchan */ + memcpy(&new_lchan->encr, &old_lchan->encr, sizeof(new_lchan->encr)); + new_lchan->ms_power = old_lchan->ms_power; + new_lchan->bs_power = old_lchan->bs_power; + new_lchan->rsl_cmode = old_lchan->rsl_cmode; + new_lchan->tch_mode = old_lchan->tch_mode; + + 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, 0, + 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, 1); + + bsc_del_timer(&ho->T3103); + llist_del(&ho->list); + talloc_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"); + counter_inc(net->stats.handover.timeout); + + ho->new_lchan->conn->ho_lchan = NULL; + ho->new_lchan->conn = NULL; + lchan_release(ho->new_lchan, 0, 1); + llist_del(&ho->list); + talloc_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; + int rc; + + /* 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 */ + + rc = gsm48_send_ho_cmd(ho->old_lchan, new_lchan, 0, ho->ho_ref); + + /* start T3103. We can continue either with T3103 expiration, + * 04.08 HANDOVER COMPLETE or 04.08 HANDOVER FAIL */ + ho->T3103.cb = ho_T3103_cb; + ho->T3103.data = ho; + bsc_schedule_timer(&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_ERROR, "unable to find HO record\n"); + return -ENODEV; + } + + new_lchan->conn->ho_lchan = NULL; + new_lchan->conn = NULL; + llist_del(&ho->list); + talloc_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); + + counter_inc(net->stats.handover.completed); + + bsc_del_timer(&ho->T3103); + + /* Replace the ho lchan with the primary one */ + if (ho->old_lchan != new_lchan->conn->lchan) + LOGP(DHO, LOGL_ERROR, "Primary lchan changed during handover.\n"); + + if (new_lchan != new_lchan->conn->ho_lchan) + LOGP(DHO, LOGL_ERROR, "Handover channel changed during this handover.\n"); + + new_lchan->conn->ho_lchan = NULL; + new_lchan->conn->lchan = new_lchan; + ho->old_lchan->conn = NULL; + + rsl_lchan_set_state(ho->old_lchan, LCHAN_S_INACTIVE); + lchan_release(ho->old_lchan, 0, 1); + + /* do something to re-route the actual speech frames ! */ + + llist_del(&ho->list); + talloc_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; + + ho = bsc_ho_by_old_lchan(old_lchan); + if (!ho) { + LOGP(DHO, LOGL_ERROR, "unable to find HO record\n"); + return -ENODEV; + } + + counter_inc(net->stats.handover.failed); + + bsc_del_timer(&ho->T3103); + llist_del(&ho->list); + + /* release the channel and forget about it */ + ho->new_lchan->conn->ho_lchan = NULL; + ho->new_lchan->conn = NULL; + lchan_release(ho->new_lchan, 0, 1); + + talloc_free(ho); + + 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_ipac_crcx_ack(struct gsm_lchan *new_lchan) +{ + struct bsc_handover *ho; + struct ho_signal_data sig_ho; + + ho = bsc_ho_by_new_lchan(new_lchan); + if (!ho) { + /* it is perfectly normal, we have CRCX even in non-HO cases */ + return 0; + } + + sig_ho.old_lchan = ho->old_lchan; + sig_ho.new_lchan = new_lchan; + dispatch_signal(SS_HO, S_HANDOVER_ACK, &sig_ho); + 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; + case SS_ABISIP: + lchan = signal_data; + switch (signal) { + case S_ABISIP_CRCX_ACK: + return ho_ipac_crcx_ack(lchan); + break; + } + break; + default: + break; + } + + return 0; +} + +static __attribute__((constructor)) void on_dso_load_ho_logic(void) +{ + register_signal_handler(SS_LCHAN, ho_logic_sig_cb, NULL); + register_signal_handler(SS_ABISIP, ho_logic_sig_cb, NULL); +} diff --git a/openbsc/src/libbsc/meas_proc.c b/openbsc/src/libbsc/meas_proc.c new file mode 100644 index 000000000..ade1f2e0c --- /dev/null +++ b/openbsc/src/libbsc/meas_proc.c @@ -0,0 +1,84 @@ +/* Measurement Processing */ + +/* (C) 2009 by Harald Welte + * + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + * + */ + +#include +#include + +#include +#include +#include +#include +#include +#include + +/* process an already parsed measurement report */ +static int process_meas_rep(struct gsm_meas_rep *mr) +{ + struct gsm_meas_rep_cell *mr_cell = NULL; + unsigned int best_better_db; + int i; + + /* FIXME: implement actual averaging over multiple measurement + * reports */ + + /* find the best cell in this report that is at least RXLEV_HYST + * better than the current serving cell */ + for (i = 0; i < mr->num_cell; i++) { + unsigned int better; + if (mr->cell[i].rxlev < mr->dl.full.rx_lev + RXLEV_HYST) + continue; + + better = mr->cell[i].rxlev - mr->dl.full.rx_lev; + if (better > best_better_db) { + mr_cell = &mr->cell[i]; + best_better_db = better; + } + } + + if (mr_cell) + return handover_to_arfcn_bsic(mr->lchan, mr_cell->arfcn, + mr_cell->bsic); + return 0; +} + +static int meas_proc_sig_cb(unsigned int subsys, unsigned int signal, + void *handler_data, void *signal_data) +{ + struct gsm_lchan *lchan; + struct gsm_meas_rep *mr; + + if (subsys != SS_LCHAN) + return 0; + + switch (signal) { + case S_LCHAN_MEAS_REP: + mr = signal_data; + process_meas_rep(mr); + break; + } + + return 0; +} + +static __attribute__((constructor)) void on_dso_load_meas(void) +{ + register_signal_handler(SS_LCHAN, meas_proc_sig_cb, NULL); +} diff --git a/openbsc/src/libbsc/meas_rep.c b/openbsc/src/libbsc/meas_rep.c new file mode 100644 index 000000000..788a9baed --- /dev/null +++ b/openbsc/src/libbsc/meas_rep.c @@ -0,0 +1,116 @@ +/* Measurement Report Processing */ + +/* (C) 2009 by Harald Welte + * + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + * + */ + +#include + +#include +#include + +static int get_field(const struct gsm_meas_rep *rep, + enum meas_rep_field field) +{ + switch (field) { + case MEAS_REP_DL_RXLEV_FULL: + return rep->dl.full.rx_lev; + case MEAS_REP_DL_RXLEV_SUB: + return rep->dl.sub.rx_lev; + case MEAS_REP_DL_RXQUAL_FULL: + return rep->dl.full.rx_qual; + case MEAS_REP_DL_RXQUAL_SUB: + return rep->dl.sub.rx_qual; + case MEAS_REP_UL_RXLEV_FULL: + return rep->ul.full.rx_lev; + case MEAS_REP_UL_RXLEV_SUB: + return rep->ul.sub.rx_lev; + case MEAS_REP_UL_RXQUAL_FULL: + return rep->ul.full.rx_qual; + case MEAS_REP_UL_RXQUAL_SUB: + return rep->ul.sub.rx_qual; + } + + return 0; +} + + +unsigned int calc_initial_idx(unsigned int array_size, + unsigned int meas_rep_idx, + unsigned int num_values) +{ + int offs, idx; + + /* from which element do we need to start if we're interested + * in an average of 'num' elements */ + offs = meas_rep_idx - num_values; + + if (offs < 0) + idx = array_size + offs; + else + idx = offs; + + return idx; +} + +/* obtain an average over the last 'num' fields in the meas reps */ +int get_meas_rep_avg(const struct gsm_lchan *lchan, + enum meas_rep_field field, unsigned int num) +{ + unsigned int i, idx; + int avg = 0; + + if (num < 1) + return 0; + + idx = calc_initial_idx(ARRAY_SIZE(lchan->meas_rep), + lchan->meas_rep_idx, num); + + for (i = 0; i < num; i++) { + int j = (idx+i) % ARRAY_SIZE(lchan->meas_rep); + + avg += get_field(&lchan->meas_rep[j], field); + } + + return avg / num; +} + +/* Check if N out of M last values for FIELD are >= bd */ +int meas_rep_n_out_of_m_be(const struct gsm_lchan *lchan, + enum meas_rep_field field, + unsigned int n, unsigned int m, int be) +{ + unsigned int i, idx; + int count = 0; + + idx = calc_initial_idx(ARRAY_SIZE(lchan->meas_rep), + lchan->meas_rep_idx, m); + + for (i = 0; i < m; i++) { + int j = (idx + i) % ARRAY_SIZE(lchan->meas_rep); + int val = get_field(&lchan->meas_rep[j], field); + + if (val >= be) + count++; + + if (count >= n) + return 1; + } + + return 0; +} diff --git a/openbsc/src/libbsc/paging.c b/openbsc/src/libbsc/paging.c new file mode 100644 index 000000000..650254575 --- /dev/null +++ b/openbsc/src/libbsc/paging.c @@ -0,0 +1,395 @@ +/* Paging helper and manager.... */ +/* (C) 2009 by Holger Hans Peter Freyther + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + * + */ + +/* + * Relevant specs: + * 12.21: + * - 9.4.12 for CCCH Local Threshold + * + * 05.58: + * - 8.5.2 CCCH Load indication + * - 9.3.15 Paging Load + * + * Approach: + * - Send paging command to subscriber + * - On Channel Request we will remember the reason + * - After the ACK we will request the identity + * - Then we will send assign the gsm_subscriber and + * - and call a callback + */ + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +void *tall_paging_ctx; + +#define PAGING_TIMER 0, 500000 + +static unsigned int calculate_group(struct gsm_bts *bts, struct gsm_subscriber *subscr) +{ + int ccch_conf; + int bs_cc_chans; + int blocks; + unsigned int group; + + ccch_conf = bts->si_common.chan_desc.ccch_conf; + bs_cc_chans = rsl_ccch_conf_to_bs_cc_chans(ccch_conf); + /* code word + 2, as 2 channels equals 0x0 */ + blocks = rsl_number_of_paging_subchannels(bts); + group = get_paging_group(str_to_imsi(subscr->imsi), + bs_cc_chans, blocks); + return group; +} + +/* + * 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) +{ + bsc_del_timer(&to_be_deleted->T3113); + llist_del(&to_be_deleted->entry); + subscr_put(to_be_deleted->subscr); + talloc_free(to_be_deleted); +} + +static void page_ms(struct gsm_paging_request *request) +{ + u_int8_t mi[128]; + unsigned int mi_len; + unsigned int page_group; + + LOGP(DPAG, LOGL_INFO, "Going to send paging commands: imsi: '%s' tmsi: '0x%x'\n", + request->subscr->imsi, request->subscr->tmsi); + + if (request->subscr->tmsi == GSM_RESERVED_TMSI) + mi_len = gsm48_generate_mid_from_imsi(mi, request->subscr->imsi); + else + mi_len = gsm48_generate_mid_from_tmsi(mi, request->subscr->tmsi); + + page_group = calculate_group(request->bts, request->subscr); + gsm0808_page(request->bts, page_group, mi_len, mi, request->chan_type); +} + +static void paging_schedule_if_needed(struct gsm_bts_paging_state *paging_bts) +{ + if (llist_empty(&paging_bts->pending_requests)) + return; + + if (!bsc_timer_pending(&paging_bts->work_timer)) + bsc_schedule_timer(&paging_bts->work_timer, PAGING_TIMER); +} + + +static void paging_handle_pending_requests(struct gsm_bts_paging_state *paging_bts); +static void paging_give_credit(void *data) +{ + struct gsm_bts_paging_state *paging_bts = data; + + LOGP(DPAG, LOGL_NOTICE, "No slots available on bts nr %d\n", paging_bts->bts->nr); + paging_bts->available_slots = 20; + paging_handle_pending_requests(paging_bts); +} + +static int can_send_pag_req(struct gsm_bts *bts, int rsl_type) +{ + struct pchan_load pl; + int count; + + memset(&pl, 0, sizeof(pl)); + bts_chan_load(&pl, bts); + + switch (rsl_type) { + case RSL_CHANNEED_TCH_F: + case RSL_CHANNEED_TCH_ForH: + goto count_tch; + break; + case RSL_CHANNEED_SDCCH: + goto count_sdcch; + break; + case RSL_CHANNEED_ANY: + default: + if (bts->network->pag_any_tch) + goto count_tch; + else + goto count_sdcch; + break; + } + + return 0; + + /* could available SDCCH */ +count_sdcch: + count = 0; + count += pl.pchan[GSM_PCHAN_SDCCH8_SACCH8C].total + - pl.pchan[GSM_PCHAN_SDCCH8_SACCH8C].used; + count += pl.pchan[GSM_PCHAN_CCCH_SDCCH4].total + - pl.pchan[GSM_PCHAN_CCCH_SDCCH4].used; + return bts->paging.free_chans_need > count; + +count_tch: + count = 0; + count += pl.pchan[GSM_PCHAN_TCH_F].total + - pl.pchan[GSM_PCHAN_TCH_F].used; + if (bts->network->neci) + count += pl.pchan[GSM_PCHAN_TCH_H].total + - pl.pchan[GSM_PCHAN_TCH_H].used; + return bts->paging.free_chans_need > count; +} + +/* + * This is kicked by the periodic PAGING LOAD Indicator + * coming from abis_rsl.c + * + * We attempt to iterate once over the list of items but + * only upto available_slots. + */ +static void paging_handle_pending_requests(struct gsm_bts_paging_state *paging_bts) +{ + struct gsm_paging_request *request = NULL; + + /* + * Determine if the pending_requests list is empty and + * return then. + */ + if (llist_empty(&paging_bts->pending_requests)) { + /* since the list is empty, no need to reschedule the timer */ + return; + } + + /* + * In case the BTS does not provide us with load indication and we + * ran out of slots, call an autofill routine. It might be that the + * BTS did not like our paging messages and then we have counted down + * to zero and we do not get any messages. + */ + if (paging_bts->available_slots == 0) { + paging_bts->credit_timer.cb = paging_give_credit; + paging_bts->credit_timer.data = paging_bts; + bsc_schedule_timer(&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: + bsc_schedule_timer(&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); +} + +void paging_init(struct gsm_bts *bts) +{ + bts->paging.bts = bts; + INIT_LLIST_HEAD(&bts->paging.pending_requests); + bts->paging.work_timer.cb = paging_worker; + bts->paging.work_timer.data = &bts->paging; + + /* Large number, until we get a proper message */ + bts->paging.available_slots = 20; +} + +static int paging_pending_request(struct gsm_bts_paging_state *bts, + struct gsm_subscriber *subscr) { + struct gsm_paging_request *req; + + llist_for_each_entry(req, &bts->pending_requests, entry) { + if (subscr == req->subscr) + return 1; + } + + return 0; +} + +static void paging_T3113_expired(void *data) +{ + struct gsm_paging_request *req = (struct gsm_paging_request *)data; + void *cbfn_param; + gsm_cbfn *cbfn; + int msg; + + LOGP(DPAG, LOGL_INFO, "T3113 expired for request %p (%s)\n", + req, req->subscr->imsi); + + /* must be destroyed before calling cbfn, to prevent double free */ + counter_inc(req->bts->network->stats.paging.expired); + cbfn_param = req->cbfn_param; + cbfn = req->cbfn; + + /* did we ever manage to page the subscriber */ + msg = req->attempts > 0 ? GSM_PAGING_EXPIRED : GSM_PAGING_BUSY; + + /* destroy it now. Do not access req afterwards */ + paging_remove_request(&req->bts->paging, req); + + if (cbfn) + cbfn(GSM_HOOK_RR_PAGING, msg, NULL, NULL, + cbfn_param); +} + +static int _paging_request(struct gsm_bts *bts, struct gsm_subscriber *subscr, + int type, gsm_cbfn *cbfn, void *data) +{ + struct gsm_bts_paging_state *bts_entry = &bts->paging; + struct gsm_paging_request *req; + + if (paging_pending_request(bts_entry, subscr)) { + LOGP(DPAG, LOGL_INFO, "Paging request already pending for %s\n", subscr->imsi); + return -EEXIST; + } + + LOGP(DPAG, LOGL_DEBUG, "Start paging of subscriber %llu on bts %d.\n", + subscr->id, bts->nr); + req = talloc_zero(tall_paging_ctx, struct gsm_paging_request); + req->subscr = subscr_get(subscr); + req->bts = bts; + req->chan_type = type; + req->cbfn = cbfn; + req->cbfn_param = data; + req->T3113.cb = paging_T3113_expired; + req->T3113.data = req; + bsc_schedule_timer(&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(struct gsm_network *network, struct gsm_subscriber *subscr, + int type, gsm_cbfn *cbfn, void *data) +{ + struct gsm_bts *bts = NULL; + int num_pages = 0; + + counter_inc(network->stats.paging.attempted); + + /* start paging subscriber on all BTS within Location Area */ + do { + int rc; + + bts = gsm_bts_by_lac(network, subscr->lac, bts); + if (!bts) + break; + + /* skip all currently inactive TRX */ + if (!trx_is_usable(bts->c0)) + continue; + + num_pages++; + + /* Trigger paging, pass any error to caller */ + rc = _paging_request(bts, subscr, type, cbfn, data); + if (rc < 0) + return rc; + } while (1); + + if (num_pages == 0) + counter_inc(network->stats.paging.detached); + + return num_pages; +} + + +/* we consciously ignore the type of the request here */ +static void _paging_request_stop(struct gsm_bts *bts, struct gsm_subscriber *subscr, + struct gsm_subscriber_connection *conn, + struct msgb *msg) +{ + struct gsm_bts_paging_state *bts_entry = &bts->paging; + struct gsm_paging_request *req, *req2; + + llist_for_each_entry_safe(req, req2, &bts_entry->pending_requests, + entry) { + if (req->subscr == subscr) { + if (conn && req->cbfn) { + LOGP(DPAG, LOGL_DEBUG, "Stop paging on bts %d, calling cbfn.\n", bts->nr); + req->cbfn(GSM_HOOK_RR_PAGING, GSM_PAGING_SUCCEEDED, + msg, conn, req->cbfn_param); + } else + LOGP(DPAG, LOGL_DEBUG, "Stop paging on bts %d silently.\n", bts->nr); + paging_remove_request(&bts->paging, req); + break; + } + } +} + +/* Stop paging on all other bts' */ +void paging_request_stop(struct gsm_bts *_bts, struct gsm_subscriber *subscr, + struct gsm_subscriber_connection *conn, + struct msgb *msg) +{ + struct gsm_bts *bts = NULL; + + if (_bts) + _paging_request_stop(_bts, subscr, conn, msg); + + do { + /* + * FIXME: Don't use the lac of the subscriber... + * as it might have magically changed the lac.. use the + * location area of the _bts as reconfiguration of the + * network is probably happening less often. + */ + bts = gsm_bts_by_lac(subscr->net, subscr->lac, bts); + if (!bts) + break; + + /* Stop paging */ + if (bts != _bts) + _paging_request_stop(bts, subscr, NULL, NULL); + } while (1); +} + +void paging_update_buffer_space(struct gsm_bts *bts, u_int16_t free_slots) +{ + bsc_del_timer(&bts->paging.credit_timer); + bts->paging.available_slots = free_slots; + paging_schedule_if_needed(&bts->paging); +} diff --git a/openbsc/src/libbsc/rest_octets.c b/openbsc/src/libbsc/rest_octets.c new file mode 100644 index 000000000..084f14498 --- /dev/null +++ b/openbsc/src/libbsc/rest_octets.c @@ -0,0 +1,432 @@ +/* GSM Mobile Radio Interface Layer 3 messages on the A-bis interface, + * rest octet handling according to + * 3GPP TS 04.08 version 7.21.0 Release 1998 / ETSI TS 100 940 V7.21.0 */ + +/* (C) 2009 by Harald Welte + * + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + * + */ + +#include +#include +#include + +#include +#include +#include + +/* generate SI1 rest octets */ +int rest_octets_si1(u_int8_t *data, u_int8_t *nch_pos) +{ + 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); + + bitvec_spare_padding(&bv, 7); + 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(u_int8_t *data, const struct gsm48_si_ro_info *si3) +{ + struct bitvec bv; + + memset(&bv, 0, sizeof(bv)); + bv.data = data; + bv.data_len = 4; + + /* Optional Selection Parameters */ + append_selection_params(&bv, &si3->selection_params); + + /* Optional Power Offset */ + append_power_offset(&bv, &si3->power_offset); + + /* Do we have a SI2ter on the BCCH? */ + if (si3->si2ter_indicator) + bitvec_set_bit(&bv, H); + else + bitvec_set_bit(&bv, L); + + /* Early Classmark Sending Control */ + if (si3->early_cm_ctrl) + bitvec_set_bit(&bv, H); + else + bitvec_set_bit(&bv, L); + + /* Do we have a SI Type 9 on the BCCH? */ + if (si3->scheduling.present) { + bitvec_set_bit(&bv, H); + bitvec_set_uint(&bv, si3->scheduling.where, 3); + } else + bitvec_set_bit(&bv, L); + + /* GPRS Indicator */ + append_gprs_ind(&bv, &si3->gprs_ind); + + bitvec_spare_padding(&bv, (bv.data_len*8)-1); + return bv.data_len; +} + +static int append_lsa_params(struct bitvec *bv, + const struct gsm48_lsa_params *lsa_params) +{ + /* FIXME */ + return -1; +} + +/* Generate SI4 Rest Octets (Chapter 10.5.2.35) */ +int rest_octets_si4(u_int8_t *data, const struct gsm48_si_ro_info *si4) +{ + struct bitvec bv; + + memset(&bv, 0, sizeof(bv)); + bv.data = data; + bv.data_len = 10; /* FIXME: up to ? */ + + /* SI4 Rest Octets O */ + append_selection_params(&bv, &si4->selection_params); + append_power_offset(&bv, &si4->power_offset); + append_gprs_ind(&bv, &si4->gprs_ind); + + if (0 /* FIXME */) { + /* H and SI4 Rest Octets S */ + bitvec_set_bit(&bv, H); + + /* LSA Parameters */ + if (si4->lsa_params.present) { + bitvec_set_bit(&bv, H); + append_lsa_params(&bv, &si4->lsa_params); + } else + bitvec_set_bit(&bv, L); + + /* Cell Identity */ + if (1) { + bitvec_set_bit(&bv, H); + bitvec_set_uint(&bv, si4->cell_id, 16); + } else + bitvec_set_bit(&bv, L); + + /* LSA ID Information */ + if (0) { + bitvec_set_bit(&bv, H); + /* FIXME */ + } else + bitvec_set_bit(&bv, L); + } else { + /* L and break indicator */ + bitvec_set_bit(&bv, L); + bitvec_set_bit(&bv, si4->break_ind ? H : L); + } + + return bv.data_len; +} + +/* GPRS Mobile Allocation as per TS 04.60 Chapter 12.10a: + < GPRS Mobile Allocation IE > ::= + < HSN : bit (6) > + { 0 | 1 < RFL number list : < RFL number list struct > > } + { 0 < MA_LENGTH : bit (6) > + < MA_BITMAP: bit (val(MA_LENGTH) + 1) > + | 1 { 0 | 1 > } } ; + + < RFL number list struct > :: = + < RFL_NUMBER : bit (4) > + { 0 | 1 < RFL number list struct > } ; + < ARFCN index list struct > ::= + < ARFCN_INDEX : bit(6) > + { 0 | 1 < ARFCN index list struct > } ; + */ +static int append_gprs_mobile_alloc(struct bitvec *bv) +{ + /* Hopping Sequence Number */ + bitvec_set_uint(bv, 0, 6); + + if (0) { + /* We want to use a RFL number list */ + bitvec_set_bit(bv, 1); + /* FIXME: RFL number list */ + } else + bitvec_set_bit(bv, 0); + + if (0) { + /* We want to use a MA_BITMAP */ + bitvec_set_bit(bv, 0); + /* FIXME: MA_LENGTH, MA_BITMAP, ... */ + } else { + bitvec_set_bit(bv, 1); + if (0) { + /* We want to provide an ARFCN index list */ + bitvec_set_bit(bv, 1); + /* FIXME */ + } else + bitvec_set_bit(bv, 0); + } + return 0; +} + +static int encode_t3192(unsigned int t3192) +{ + if (t3192 == 0) + return 3; + else if (t3192 <= 80) + return 4; + else if (t3192 <= 120) + return 5; + else if (t3192 <= 160) + return 6; + else if (t3192 <= 200) + return 7; + else if (t3192 <= 500) + return 0; + else if (t3192 <= 1000) + return 1; + else if (t3192 <= 1500) + return 2; + else + return -EINVAL; +} + +static int encode_drx_timer(unsigned int drx) +{ + if (drx == 0) + return 0; + else if (drx == 1) + return 1; + else if (drx == 2) + return 2; + else if (drx <= 4) + return 3; + else if (drx <= 8) + return 4; + else if (drx <= 16) + return 5; + else if (drx <= 32) + return 6; + else if (drx <= 64) + return 7; + else + return -EINVAL; +} + +/* GPRS Cell Options as per TS 04.60 Chapter 12.24 + < GPRS Cell Options IE > ::= + < NMO : bit(2) > + < T3168 : bit(3) > + < T3192 : bit(3) > + < DRX_TIMER_MAX: bit(3) > + < ACCESS_BURST_TYPE: bit > + < CONTROL_ACK_TYPE : bit > + < BS_CV_MAX: bit(4) > + { 0 | 1 < PAN_DEC : bit(3) > + < PAN_INC : bit(3) > + < PAN_MAX : bit(3) > + { 0 | 1 < Extension Length : bit(6) > + < bit (val(Extension Length) + 1 + & { < Extension Information > ! { bit ** = } } ; + < Extension Information > ::= + { 0 | 1 < EGPRS_PACKET_CHANNEL_REQUEST : bit > + < BEP_PERIOD : bit(4) > } + < PFC_FEATURE_MODE : bit > + < DTM_SUPPORT : bit > + + ** ; + */ +static int append_gprs_cell_opt(struct bitvec *bv, + const struct gprs_cell_options *gco) +{ + int t3192, drx_timer_max; + + t3192 = encode_t3192(gco->t3192); + if (t3192 < 0) + return t3192; + + drx_timer_max = encode_drx_timer(gco->drx_timer_max); + if (drx_timer_max < 0) + return drx_timer_max; + + bitvec_set_uint(bv, gco->nmo, 2); + bitvec_set_uint(bv, gco->t3168 / 500, 3); + bitvec_set_uint(bv, t3192, 3); + bitvec_set_uint(bv, drx_timer_max, 3); + /* ACCESS_BURST_TYPE: Hard-code 8bit */ + bitvec_set_bit(bv, 0); + /* CONTROL_ACK_TYPE: Hard-code to RLC/MAC control block */ + bitvec_set_bit(bv, 1); + bitvec_set_uint(bv, gco->bs_cv_max, 4); + + if (0) { + /* hard-code no PAN_{DEC,INC,MAX} */ + bitvec_set_bit(bv, 0); + } else { + /* copied from ip.access BSC protocol trace */ + bitvec_set_bit(bv, 1); + bitvec_set_uint(bv, 1, 3); /* DEC */ + bitvec_set_uint(bv, 1, 3); /* INC */ + bitvec_set_uint(bv, 15, 3); /* MAX */ + } + + if (!gco->ext_info_present) { + /* no extension information */ + bitvec_set_bit(bv, 0); + } else { + /* extension information */ + bitvec_set_bit(bv, 1); + if (!gco->ext_info.egprs_supported) { + /* 6bit length of extension */ + bitvec_set_uint(bv, (1 + 3)-1, 6); + /* EGPRS supported in the cell */ + bitvec_set_bit(bv, 0); + } else { + /* 6bit length of extension */ + bitvec_set_uint(bv, (1 + 5 + 3)-1, 6); + /* EGPRS supported in the cell */ + bitvec_set_bit(bv, 1); + /* 1bit EGPRS PACKET CHANNEL REQUEST */ + bitvec_set_bit(bv, gco->ext_info.use_egprs_p_ch_req); + /* 4bit BEP PERIOD */ + bitvec_set_uint(bv, gco->ext_info.bep_period, 4); + } + bitvec_set_bit(bv, gco->ext_info.pfc_supported); + bitvec_set_bit(bv, gco->ext_info.dtm_supported); + bitvec_set_bit(bv, gco->ext_info.bss_paging_coordination); + } + + return 0; +} + +static void append_gprs_pwr_ctrl_pars(struct bitvec *bv, + const struct gprs_power_ctrl_pars *pcp) +{ + bitvec_set_uint(bv, pcp->alpha, 4); + bitvec_set_uint(bv, pcp->t_avg_w, 5); + bitvec_set_uint(bv, pcp->t_avg_t, 5); + bitvec_set_uint(bv, pcp->pc_meas_chan, 1); + bitvec_set_uint(bv, pcp->n_avg_i, 4); +} + +/* Generate SI13 Rest Octests (04.08 Chapter 10.5.2.37b) */ +int rest_octets_si13(u_int8_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 new file mode 100644 index 000000000..8a99c565e --- /dev/null +++ b/openbsc/src/libbsc/system_information.c @@ -0,0 +1,606 @@ +/* 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 + * + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + * + */ + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#define GSM48_CELL_CHAN_DESC_SIZE 16 +#define GSM_MACBLOCK_PADDING 0x2b + +/* verify the sizes of the system information type structs */ + +/* rest octets are not part of the struct */ +static_assert(sizeof(struct gsm48_system_information_type_header) == 3, _si_header_size); +static_assert(sizeof(struct gsm48_rach_control) == 3, _si_rach_control); +static_assert(sizeof(struct gsm48_system_information_type_1) == 22, _si1_size); +static_assert(sizeof(struct gsm48_system_information_type_2) == 23, _si2_size); +static_assert(sizeof(struct gsm48_system_information_type_3) == 19, _si3_size); +static_assert(sizeof(struct gsm48_system_information_type_4) == 13, _si4_size); + +/* bs11 forgot the l2 len, 0-6 rest octets */ +static_assert(sizeof(struct gsm48_system_information_type_5) == 18, _si5_size); +static_assert(sizeof(struct gsm48_system_information_type_6) == 11, _si6_size); + +static_assert(sizeof(struct gsm48_system_information_type_13) == 3, _si13_size); + +/* 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(u_int8_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(u_int8_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) { + LOGP(DRR, LOGL_ERROR, "arfcn(%u) < min(%u)\n", arfcn, min_arfcn); + return -EINVAL; + } + if (arfcn > min_arfcn + 111) { + LOGP(DRR, LOGL_ERROR, "arfcn(%u) > min(%u) + 111\n", arfcn, min_arfcn); + return -EINVAL; + } + + bitno = (arfcn - min_arfcn); + byte = bitno / 8; + bit = bitno % 8; + + chan_list[2 + byte] |= 1 << (7 - bit); + + return 0; +} + +/* generate a cell channel list as per Section 10.5.2.1b of 04.08 */ +static int bitvec2freq_list(u_int8_t *chan_list, struct bitvec *bv, + const struct gsm_bts *bts) +{ + int i, rc, min = 1024, max = -1; + + memset(chan_list, 0, 16); + + /* GSM900-only handsets only support 'bit map 0 format' */ + if (bts->band == GSM_BAND_900) { + chan_list[0] = 0; + + for (i = 0; i < bv->data_len*8; i++) { + if (bitvec_get_bit_pos(bv, i)) { + rc = freq_list_bm0_set_arfcn(chan_list, i); + if (rc < 0) + return rc; + } + } + return 0; + } + + /* We currently only support the 'Variable bitmap format' */ + chan_list[0] = 0x8e; + + for (i = 0; i < bv->data_len*8; i++) { + if (bitvec_get_bit_pos(bv, i)) { + if (i < min) + min = i; + if (i > max) + max = i; + } + } + + if (max == -1) { + /* Empty set, use 'bit map 0 format' */ + chan_list[0] = 0; + return 0; + } + + if ((max - min) > 111) { + LOGP(DRR, LOGL_ERROR, "min_arfcn=%u, max_arfcn=%u, " + "distance > 111\n", min, max); + return -EINVAL; + } + + 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++) { + if (bitvec_get_bit_pos(bv, i)) { + rc = freq_list_bmrel_set_arfcn(chan_list, i); + if (rc < 0) + return rc; + } + } + + return 0; +} + +/* generate a cell channel list as per Section 10.5.2.1b of 04.08 */ +static int generate_cell_chan_list(u_int8_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); +} + +/* generate a cell channel list as per Section 10.5.2.1b of 04.08 */ +static int generate_bcch_chan_list(u_int8_t *chan_list, struct gsm_bts *bts, int si5) +{ + 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); +} + +static int generate_si1(u_int8_t *output, struct gsm_bts *bts) +{ + int rc; + struct gsm48_system_information_type_1 *si1 = + (struct gsm48_system_information_type_1 *) output; + + memset(si1, GSM_MACBLOCK_PADDING, GSM_MACBLOCK_LEN); + + si1->header.l2_plen = (21 << 2) | 1; + si1->header.rr_protocol_discriminator = GSM48_PDISC_RR; + si1->header.skip_indicator = 0; + si1->header.system_information = GSM48_MT_RR_SYSINFO_1; + + rc = generate_cell_chan_list(si1->cell_channel_description, bts); + if (rc < 0) + return rc; + + si1->rach_control = bts->si_common.rach_control; + + /* SI1 Rest Octets (10.5.2.32), contains NCH position */ + rc = rest_octets_si1(si1->rest_octets, NULL); + + return sizeof(*si1) + rc; +} + +static int generate_si2(u_int8_t *output, struct gsm_bts *bts) +{ + int rc; + struct gsm48_system_information_type_2 *si2 = + (struct gsm48_system_information_type_2 *) output; + + memset(si2, GSM_MACBLOCK_PADDING, GSM_MACBLOCK_LEN); + + si2->header.l2_plen = (22 << 2) | 1; + si2->header.rr_protocol_discriminator = GSM48_PDISC_RR; + si2->header.skip_indicator = 0; + si2->header.system_information = GSM48_MT_RR_SYSINFO_2; + + rc = generate_bcch_chan_list(si2->bcch_frequency_list, bts, 0); + if (rc < 0) + return rc; + + si2->ncc_permitted = bts->si_common.ncc_permitted; + si2->rach_control = bts->si_common.rach_control; + + return sizeof(*si2); +} + +static struct gsm48_si_ro_info si_info = { + .selection_params = { + .present = 0, + }, + .power_offset = { + .present = 0, + }, + .si2ter_indicator = 0, + .early_cm_ctrl = 1, + .scheduling = { + .present = 0, + }, + .gprs_ind = { + .si13_position = 0, + .ra_colour = 0, + .present = 1, + }, + .lsa_params = { + .present = 0, + }, + .cell_id = 0, /* FIXME: doesn't the bts have this? */ + .break_ind = 0, +}; + +static int generate_si3(u_int8_t *output, struct gsm_bts *bts) +{ + int rc; + struct gsm48_system_information_type_3 *si3 = + (struct gsm48_system_information_type_3 *) output; + + memset(si3, GSM_MACBLOCK_PADDING, GSM_MACBLOCK_LEN); + + si3->header.l2_plen = (18 << 2) | 1; + si3->header.rr_protocol_discriminator = GSM48_PDISC_RR; + si3->header.skip_indicator = 0; + si3->header.system_information = GSM48_MT_RR_SYSINFO_3; + + si3->cell_identity = htons(bts->cell_identity); + gsm48_generate_lai(&si3->lai, bts->network->country_code, + bts->network->network_code, + bts->location_area_code); + si3->control_channel_desc = bts->si_common.chan_desc; + si3->cell_options = bts->si_common.cell_options; + si3->cell_sel_par = bts->si_common.cell_sel_par; + si3->rach_control = bts->si_common.rach_control; + + /* 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(u_int8_t *output, struct gsm_bts *bts) +{ + int rc; + struct gsm48_system_information_type_4 *si4 = + (struct gsm48_system_information_type_4 *) output; + + /* 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 */ + + si4->header.l2_plen = (l2_plen << 2) | 1; + + /* SI4 Rest Octets (10.5.2.35), containing + Optional Power offset, GPRS Indicator, + Cell Identity, LSA ID, Selection Parameter */ + rc = rest_octets_si4(si4->data, &si_info); + + return sizeof(*si4) + rc; +} + +static int generate_si5(u_int8_t *output, struct gsm_bts *bts) +{ + struct gsm48_system_information_type_5 *si5; + int rc, l2_plen = 18; + + memset(output, GSM_MACBLOCK_PADDING, GSM_MACBLOCK_LEN); + + /* ip.access nanoBTS needs l2_plen!! */ + if (is_ipaccess_bts(bts)) { + *output++ = (l2_plen << 2) | 1; + l2_plen++; + } + + si5 = (struct gsm48_system_information_type_5 *) output; + + /* l2 pseudo length, not part of msg: 18 */ + si5->rr_protocol_discriminator = GSM48_PDISC_RR; + si5->skip_indicator = 0; + si5->system_information = GSM48_MT_RR_SYSINFO_5; + rc = generate_bcch_chan_list(si5->bcch_frequency_list, bts, 1); + if (rc < 0) + return rc; + + /* 04.08 9.1.37: L2 Pseudo Length of 18 */ + return l2_plen; +} + +static int generate_si6(u_int8_t *output, struct gsm_bts *bts) +{ + struct gsm48_system_information_type_6 *si6; + int l2_plen = 11; + + memset(output, GSM_MACBLOCK_PADDING, GSM_MACBLOCK_LEN); + + /* ip.access nanoBTS needs l2_plen!! */ + if (is_ipaccess_bts(bts)) { + *output++ = (l2_plen << 2) | 1; + l2_plen++; + } + + si6 = (struct gsm48_system_information_type_6 *) output; + + /* l2 pseudo length, not part of msg: 11 */ + si6->rr_protocol_discriminator = GSM48_PDISC_RR; + si6->skip_indicator = 0; + si6->system_information = GSM48_MT_RR_SYSINFO_6; + si6->cell_identity = htons(bts->cell_identity); + gsm48_generate_lai(&si6->lai, bts->network->country_code, + bts->network->network_code, + bts->location_area_code); + si6->cell_options = bts->si_common.cell_options; + si6->ncc_permitted = bts->si_common.ncc_permitted; + + /* SI6 Rest Octets: 10.5.2.35a: PCH / NCH info, VBS/VGCS options */ + + return l2_plen; +} + +static struct gsm48_si13_info si13_default = { + .cell_opts = { + .nmo = GPRS_NMO_II, + .t3168 = 2000, + .t3192 = 200, + .drx_timer_max = 3, + .bs_cv_max = 15, + .ext_info_present = 1, + .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 = 10, /* a = 1.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(u_int8_t *output, struct gsm_bts *bts) +{ + struct gsm48_system_information_type_13 *si13 = + (struct gsm48_system_information_type_13 *) output; + int ret; + + memset(si13, GSM_MACBLOCK_PADDING, GSM_MACBLOCK_LEN); + + si13->header.rr_protocol_discriminator = GSM48_PDISC_RR; + si13->header.skip_indicator = 0; + si13->header.system_information = GSM48_MT_RR_SYSINFO_13; + + si13_default.no_pbcch.rac = bts->gprs.rac; + + 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; +} + +static const uint8_t sitype2rsl[_MAX_SYSINFO_TYPE] = { + [SYSINFO_TYPE_1] = RSL_SYSTEM_INFO_1, + [SYSINFO_TYPE_2] = RSL_SYSTEM_INFO_2, + [SYSINFO_TYPE_3] = RSL_SYSTEM_INFO_3, + [SYSINFO_TYPE_4] = RSL_SYSTEM_INFO_4, + [SYSINFO_TYPE_5] = RSL_SYSTEM_INFO_5, + [SYSINFO_TYPE_6] = RSL_SYSTEM_INFO_6, + [SYSINFO_TYPE_7] = RSL_SYSTEM_INFO_7, + [SYSINFO_TYPE_8] = RSL_SYSTEM_INFO_8, + [SYSINFO_TYPE_9] = RSL_SYSTEM_INFO_9, + [SYSINFO_TYPE_10] = RSL_SYSTEM_INFO_10, + [SYSINFO_TYPE_13] = RSL_SYSTEM_INFO_13, + [SYSINFO_TYPE_16] = RSL_SYSTEM_INFO_16, + [SYSINFO_TYPE_17] = RSL_SYSTEM_INFO_17, + [SYSINFO_TYPE_18] = RSL_SYSTEM_INFO_18, + [SYSINFO_TYPE_19] = RSL_SYSTEM_INFO_19, + [SYSINFO_TYPE_20] = RSL_SYSTEM_INFO_20, + [SYSINFO_TYPE_2bis] = RSL_SYSTEM_INFO_2bis, + [SYSINFO_TYPE_2ter] = RSL_SYSTEM_INFO_2ter, + [SYSINFO_TYPE_2quater] = RSL_SYSTEM_INFO_2quater, + [SYSINFO_TYPE_5bis] = RSL_SYSTEM_INFO_5bis, + [SYSINFO_TYPE_5ter] = RSL_SYSTEM_INFO_5ter, +}; + +static const uint8_t rsl2sitype[0xff] = { + [RSL_SYSTEM_INFO_1] = SYSINFO_TYPE_1, + [RSL_SYSTEM_INFO_2] = SYSINFO_TYPE_2, + [RSL_SYSTEM_INFO_3] = SYSINFO_TYPE_3, + [RSL_SYSTEM_INFO_4] = SYSINFO_TYPE_4, + [RSL_SYSTEM_INFO_5] = SYSINFO_TYPE_5, + [RSL_SYSTEM_INFO_6] = SYSINFO_TYPE_6, + [RSL_SYSTEM_INFO_7] = SYSINFO_TYPE_7, + [RSL_SYSTEM_INFO_8] = SYSINFO_TYPE_8, + [RSL_SYSTEM_INFO_9] = SYSINFO_TYPE_9, + [RSL_SYSTEM_INFO_10] = SYSINFO_TYPE_10, + [RSL_SYSTEM_INFO_13] = SYSINFO_TYPE_13, + [RSL_SYSTEM_INFO_16] = SYSINFO_TYPE_16, + [RSL_SYSTEM_INFO_17] = SYSINFO_TYPE_17, + [RSL_SYSTEM_INFO_18] = SYSINFO_TYPE_18, + [RSL_SYSTEM_INFO_19] = SYSINFO_TYPE_19, + [RSL_SYSTEM_INFO_20] = SYSINFO_TYPE_20, + [RSL_SYSTEM_INFO_2bis] = SYSINFO_TYPE_2bis, + [RSL_SYSTEM_INFO_2ter] = SYSINFO_TYPE_2ter, + [RSL_SYSTEM_INFO_2quater] = SYSINFO_TYPE_2quater, + [RSL_SYSTEM_INFO_5bis] = SYSINFO_TYPE_5bis, + [RSL_SYSTEM_INFO_5ter] = SYSINFO_TYPE_5ter, +}; + +typedef int (*gen_si_fn_t)(uint8_t *output, struct gsm_bts *bts); + +static const gen_si_fn_t gen_si_fn[_MAX_SYSINFO_TYPE] = { + [SYSINFO_TYPE_1] = &generate_si1, + [SYSINFO_TYPE_2] = &generate_si2, + [SYSINFO_TYPE_3] = &generate_si3, + [SYSINFO_TYPE_4] = &generate_si4, + [SYSINFO_TYPE_5] = &generate_si5, + [SYSINFO_TYPE_6] = &generate_si6, + [SYSINFO_TYPE_13] = &generate_si13, +}; + +const struct value_string osmo_sitype_strs[_MAX_SYSINFO_TYPE] = { + { SYSINFO_TYPE_1, "1" }, + { SYSINFO_TYPE_2, "2" }, + { SYSINFO_TYPE_3, "3" }, + { SYSINFO_TYPE_4, "4" }, + { SYSINFO_TYPE_5, "5" }, + { SYSINFO_TYPE_6, "6" }, + { SYSINFO_TYPE_7, "7" }, + { SYSINFO_TYPE_8, "8" }, + { SYSINFO_TYPE_9, "9" }, + { SYSINFO_TYPE_10, "10" }, + { SYSINFO_TYPE_13, "13" }, + { SYSINFO_TYPE_16, "16" }, + { SYSINFO_TYPE_17, "17" }, + { SYSINFO_TYPE_18, "18" }, + { SYSINFO_TYPE_19, "19" }, + { SYSINFO_TYPE_20, "20" }, + { SYSINFO_TYPE_2bis, "2bis" }, + { SYSINFO_TYPE_2ter, "2ter" }, + { SYSINFO_TYPE_2quater, "2quater" }, + { SYSINFO_TYPE_5bis, "5bis" }, + { SYSINFO_TYPE_5ter, "5ter" }, + { 0, NULL } +}; + +uint8_t gsm_sitype2rsl(enum osmo_sysinfo_type si_type) +{ + return sitype2rsl[si_type]; +} + +const char *gsm_sitype_name(enum osmo_sysinfo_type si_type) +{ + return get_value_string(osmo_sitype_strs, si_type); +} + +int gsm_generate_si(struct gsm_bts *bts, enum osmo_sysinfo_type si_type) +{ + gen_si_fn_t gen_si; + + switch (bts->gprs.mode) { + case BTS_GPRS_EGPRS: + si13_default.cell_opts.ext_info_present = 1; + si13_default.cell_opts.ext_info.egprs_supported = 1; + /* fallthrough */ + case BTS_GPRS_GPRS: + si_info.gprs_ind.present = 1; + break; + case BTS_GPRS_NONE: + si_info.gprs_ind.present = 0; + break; + } + + memcpy(&si_info.selection_params, + &bts->si_common.cell_ro_sel_par, + sizeof(struct gsm48_si_selection_params)); + + gen_si = gen_si_fn[si_type]; + if (!gen_si) + return -EINVAL; + + return gen_si(bts->si_buf[si_type], bts); +} diff --git a/openbsc/src/libbsc/transaction.c b/openbsc/src/libbsc/transaction.c new file mode 100644 index 000000000..9b4af1aac --- /dev/null +++ b/openbsc/src/libbsc/transaction.c @@ -0,0 +1,152 @@ +/* GSM 04.07 Transaction handling */ + +/* (C) 2009 by Harald Welte + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +void *tall_trans_ctx; + +void _gsm48_cc_trans_free(struct gsm_trans *trans); + +struct gsm_trans *trans_find_by_id(struct gsm_subscriber *subscr, + u_int8_t proto, u_int8_t trans_id) +{ + struct gsm_trans *trans; + struct gsm_network *net = subscr->net; + + 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, + u_int32_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_subscriber *subscr, + u_int8_t protocol, u_int8_t trans_id, + u_int32_t callref) +{ + struct gsm_trans *trans; + + DEBUGP(DCC, "subscr=%p, subscr->net=%p\n", subscr, 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; + + llist_add_tail(&trans->entry, &subscr->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; + } + + /* FIXME: implement a sane way to stop this. */ + if (!trans->conn && trans->paging_request) { + LOGP(DNM, LOGL_ERROR, + "Transaction freed while paging for sub: %llu\n", + trans->subscr->id); + trans->paging_request = NULL; + } + + if (trans->subscr) + subscr_put(trans->subscr); + + llist_del(&trans->entry); + + if (trans->conn) + msc_release_connection(trans->conn); + + + 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_subscriber *subscr, + u_int8_t protocol, u_int8_t ti_flag) +{ + struct gsm_network *net = subscr->net; + 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; +} + diff --git a/openbsc/src/libcommon/Makefile.am b/openbsc/src/libcommon/Makefile.am new file mode 100644 index 000000000..2895452ea --- /dev/null +++ b/openbsc/src/libcommon/Makefile.am @@ -0,0 +1,7 @@ +INCLUDES = $(all_includes) -I$(top_srcdir)/include -I$(top_builddir) +AM_CFLAGS=-Wall $(LIBOSMOCORE_CFLAGS) $(LIBOSMOVTY_CFLAGS) $(COVERAGE_CFLAGS) +AM_LDFLAGS = $(LIBOSMOCORE_LIBS) $(COVERAGE_LDFLAGS) + +noinst_LIBRARIES = libcommon.a + +libcommon_a_SOURCES = bsc_version.c common_vty.c debug.c gsm_data.c socket.c talloc_ctx.c diff --git a/openbsc/src/libcommon/bsc_version.c b/openbsc/src/libcommon/bsc_version.c new file mode 100644 index 000000000..cc1e76449 --- /dev/null +++ b/openbsc/src/libcommon/bsc_version.c @@ -0,0 +1,30 @@ +/* Hold the copyright and version string */ +/* (C) 2010 by Harald Welte + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + * + */ + +#include "bscconfig.h" + +const char *openbsc_copyright = + "Copyright (C) 2008-2010 Harald Welte, Holger Freyther\r\n" + "Contributions by Daniel Willmann, Jan Lübbe, Stefan Schmidt\r\n" + "Dieter Spaar, Andreas Eversberg, Sylvain Munaut\r\n\r\n" + "License AGPLv3+: GNU AGPL version 3 or later \r\n" + "This is free software: you are free to change and redistribute it.\r\n" + "There is NO WARRANTY, to the extent permitted by law.\r\n"; + + diff --git a/openbsc/src/libcommon/common_vty.c b/openbsc/src/libcommon/common_vty.c new file mode 100644 index 000000000..84375a22d --- /dev/null +++ b/openbsc/src/libcommon/common_vty.c @@ -0,0 +1,225 @@ +/* OpenBSC VTY common helpers */ +/* (C) 2009-2010 by Harald Welte + * (C) 2009-2010 by Holger Hans Peter Freyther + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + * + */ + +#include +#include + +#include + +#include +#include +#include +#include +#include + +#include +#include +#include +#include + + +enum node_type 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; + } + break; + case TRX_NODE: + vty->node = BTS_NODE; + { + /* set vty->index correctly ! */ + struct gsm_bts_trx *trx = vty->index; + vty->index = trx->bts; + } + break; + case TS_NODE: + vty->node = TRX_NODE; + { + /* set vty->index correctly ! */ + struct gsm_bts_trx_ts *ts = vty->index; + vty->index = ts->trx; + } + break; + case OML_NODE: + case OM2K_NODE: + vty->node = ENABLE_NODE; + talloc_free(vty->index); + vty->index = NULL; + break; + case NAT_NODE: + vty->node = CONFIG_NODE; + vty->index = NULL; + break; + case NAT_BSC_NODE: + vty->node = NAT_NODE; + { + struct bsc_config *bsc_config = vty->index; + vty->index = bsc_config->nat; + } + break; + case MSC_NODE: + vty->node = GSMNET_NODE; + break; + case TRUNK_NODE: + vty->node = MGCP_NODE; + break; + default: + vty->node = CONFIG_NODE; + } + + return vty->node; +} + +/* Down vty node level. */ +gDEFUN(ournode_exit, + ournode_exit_cmd, "exit", "Exit current mode and down to previous mode\n") +{ + 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 NAT_BSC_NODE: + vty->node = NAT_NODE; + { + struct bsc_config *bsc_config = vty->index; + vty->index = bsc_config->nat; + } + break; + case MGCP_NODE: + case GBPROXY_NODE: + case SGSN_NODE: + case NS_NODE: + case BSSGP_NODE: + case NAT_NODE: + vty->node = CONFIG_NODE; + vty->index = NULL; + break; + case OML_NODE: + case OM2K_NODE: + vty->node = ENABLE_NODE; + talloc_free(vty->index); + vty->index = NULL; + break; + case MSC_NODE: + vty->node = GSMNET_NODE; + break; + case TRUNK_NODE: + vty->node = MGCP_NODE; + vty->index = NULL; + break; + default: + break; + } + return CMD_SUCCESS; +} + +/* End of configuration. */ +gDEFUN(ournode_end, + ournode_end_cmd, "end", "End current mode and change to enable mode.") +{ + switch (vty->node) { + case VIEW_NODE: + case ENABLE_NODE: + /* Nothing to do. */ + break; + case CONFIG_NODE: + case GSMNET_NODE: + case BTS_NODE: + case TRX_NODE: + case TS_NODE: + case MGCP_NODE: + case TRUNK_NODE: + case GBPROXY_NODE: + case SGSN_NODE: + case NS_NODE: + case VTY_NODE: + case NAT_NODE: + case NAT_BSC_NODE: + case MSC_NODE: + vty_config_unlock(vty); + vty->node = ENABLE_NODE; + vty->index = NULL; + vty->index_sub = NULL; + break; + default: + break; + } + return CMD_SUCCESS; +} + +int bsc_vty_is_config_node(struct vty *vty, int node) +{ + switch (node) { + /* add items that are not config */ + case OML_NODE: + case OM2K_NODE: + case SUBSCR_NODE: + case CONFIG_NODE: + return 0; + + default: + return 1; + } +} + +/* a talloc string replace routine */ +void bsc_replace_string(void *ctx, char **dst, const char *newstr) +{ + if (*dst) + talloc_free(*dst); + *dst = talloc_strdup(ctx, newstr); +} diff --git a/openbsc/src/libcommon/debug.c b/openbsc/src/libcommon/debug.c new file mode 100644 index 000000000..ea5db49e5 --- /dev/null +++ b/openbsc/src/libcommon/debug.c @@ -0,0 +1,249 @@ +/* OpenBSC Debugging/Logging support code */ + +/* (C) 2008-2010 by Harald Welte + * (C) 2008 by Holger Hans Peter Freyther + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + * + */ + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +/* 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, + }, + [DSMS] = { + .name = "DSMS", + .description = "Layer3 Short Message Service (SMS)", + .color = "\033[1;37m", + .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, + }, + [DMI] = { + .name = "DMI", + .description = "A-bis Input Driver for Signalling", + .enabled = 0, .loglevel = LOGL_NOTICE, + }, + [DMIB] = { + .name = "DMIB", + .description = "A-bis Input Driver for B-Channels (voice)", + .enabled = 0, .loglevel = LOGL_NOTICE, + }, + [DMUX] = { + .name = "DMUX", + .description = "A-bis B-Subchannel TRAU Frame Multiplex", + .enabled = 1, .loglevel = LOGL_NOTICE, + }, + [DINP] = { + .name = "DINP", + .description = "A-bis Intput Subsystem", + .enabled = 1, .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/Multipkexer", + .enabled = 1, .loglevel = LOGL_NOTICE, + }, +}; + +enum log_filter { + _FLT_ALL = LOG_FILTER_ALL, /* libosmocore */ + FLT_IMSI = 1, + FLT_NSVC = 2, + FLT_BVC = 3, +}; + +static int filter_fn(const struct log_context *ctx, + struct log_target *tar) +{ + struct gsm_subscriber *subscr = ctx->ctx[BSC_CTX_SUBSCR]; + const struct gprs_nsvc *nsvc = ctx->ctx[BSC_CTX_NSVC]; + const struct gprs_nsvc *bvc = ctx->ctx[BSC_CTX_BVC]; + + if ((tar->filter_map & (1 << FLT_IMSI)) != 0 + && subscr && strcmp(subscr->imsi, tar->filter_data[FLT_IMSI]) == 0) + return 1; + + /* Filter on the NS Virtual Connection */ + if ((tar->filter_map & (1 << FLT_NSVC)) != 0 + && nsvc && (nsvc == tar->filter_data[FLT_NSVC])) + return 1; + + /* Filter on the NS Virtual Connection */ + if ((tar->filter_map & (1 << FLT_BVC)) != 0 + && bvc && (bvc == tar->filter_data[FLT_BVC])) + return 1; + + return 0; +} + +const struct log_info log_info = { + .filter_fn = filter_fn, + .cat = default_categories, + .num_cat = ARRAY_SIZE(default_categories), +}; + +void log_set_imsi_filter(struct log_target *target, const char *imsi) +{ + if (imsi) { + target->filter_map |= (1 << FLT_IMSI); + target->filter_data[FLT_IMSI] = talloc_strdup(target, imsi); + } else if (target->filter_data[FLT_IMSI]) { + target->filter_map &= ~(1 << FLT_IMSI); + talloc_free(target->filter_data[FLT_IMSI]); + target->filter_data[FLT_IMSI] = NULL; + } +} + +void log_set_nsvc_filter(struct log_target *target, struct gprs_nsvc *nsvc) +{ + if (nsvc) { + target->filter_map |= (1 << FLT_NSVC); + target->filter_data[FLT_NSVC] = nsvc; + } else if (target->filter_data[FLT_NSVC]) { + target->filter_map = ~(1 << FLT_NSVC); + target->filter_data[FLT_NSVC] = NULL; + } +} + +void log_set_bvc_filter(struct log_target *target, struct bssgp_bvc_ctx *bctx) +{ + if (bctx) { + target->filter_map |= (1 << FLT_BVC); + target->filter_data[FLT_BVC] = bctx; + } else if (target->filter_data[FLT_NSVC]) { + target->filter_map = ~(1 << FLT_BVC); + target->filter_data[FLT_BVC] = NULL; + } +} diff --git a/openbsc/src/libcommon/gsm_data.c b/openbsc/src/libcommon/gsm_data.c new file mode 100644 index 000000000..f181fd162 --- /dev/null +++ b/openbsc/src/libcommon/gsm_data.c @@ -0,0 +1,589 @@ +/* (C) 2008-2010 by Harald Welte + * + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + * + */ + + +#include +#include +#include +#include +#include + +#include + +#include +#include +#include +#include + +#include +#include +#include + +void *tall_bsc_ctx; + +static LLIST_HEAD(bts_models); + +void set_ts_e1link(struct gsm_bts_trx_ts *ts, u_int8_t e1_nr, + u_int8_t e1_ts, u_int8_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 const struct value_string pchan_names[] = { + { 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" }, + { 0, NULL } +}; + +const char *gsm_pchan_name(enum gsm_phys_chan_config c) +{ + return get_value_string(pchan_names, c); +} + +enum gsm_phys_chan_config gsm_pchan_parse(const char *name) +{ + return get_string_value(pchan_names, name); +} + +static const struct value_string lchant_names[] = { + { GSM_LCHAN_NONE, "NONE" }, + { GSM_LCHAN_SDCCH, "SDCCH" }, + { GSM_LCHAN_TCH_F, "TCH/F" }, + { GSM_LCHAN_TCH_H, "TCH/H" }, + { GSM_LCHAN_UNKNOWN, "UNKNOWN" }, + { 0, NULL } +}; + +const char *gsm_lchant_name(enum gsm_chan_t c) +{ + return get_value_string(lchant_names, c); +} + +static const struct value_string lchan_s_names[] = { + { LCHAN_S_NONE, "NONE" }, + { LCHAN_S_ACT_REQ, "ACTIVATION REQUESTED" }, + { LCHAN_S_ACTIVE, "ACTIVE" }, + { LCHAN_S_INACTIVE, "INACTIVE" }, + { LCHAN_S_REL_REQ, "RELEASE REQUESTED" }, + { LCHAN_S_REL_ERR, "RELEASE DUE ERROR" }, + { 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); +} + +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, &nm_att_tlvdef); + llist_add_tail(&model->list, &bts_models); + return 0; +} + + +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->nm_state.administrative = NM_STATE_UNLOCKED; + + 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->hopping.arfcns.data_len = sizeof(ts->hopping.arfcns_data); + ts->hopping.arfcns.data = ts->hopping.arfcns_data; + ts->hopping.ma.data_len = sizeof(ts->hopping.ma_data); + ts->hopping.ma.data = ts->hopping.ma_data; + + for (l = 0; l < TS_MAX_LCHAN; l++) { + struct gsm_lchan *lchan; + lchan = &ts->lchan[l]; + + lchan->ts = ts; + lchan->nr = l; + lchan->type = GSM_LCHAN_NONE; + } + } + + 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 }; + +struct gsm_bts *gsm_bts_alloc(struct gsm_network *net, enum gsm_bts_type type, + u_int8_t tsc, u_int8_t bsic) +{ + struct gsm_bts *bts = talloc_zero(net, struct gsm_bts); + struct gsm_bts_model *model = bts_model_find(type); + int i; + + if (!bts) + return NULL; + + if (!model && type != GSM_BTS_TYPE_UNKNOWN) { + talloc_free(bts); + return NULL; + } + + bts->network = net; + bts->nr = net->num_bts++; + bts->type = type; + bts->model = model; + bts->tsc = tsc; + bts->bsic = bsic; + bts->num_trx = 0; + INIT_LLIST_HEAD(&bts->trx_list); + bts->ms_max_power = 15; /* dBm */ + + bts->neigh_list_manual_mode = 0; + bts->si_common.cell_sel_par.cell_resel_hyst = 2; /* 4 dB */ + bts->si_common.cell_sel_par.rxlev_acc_min = 0; + bts->si_common.neigh_list.data = bts->si_common.data.neigh_list; + bts->si_common.neigh_list.data_len = + sizeof(bts->si_common.data.neigh_list); + bts->si_common.si5_neigh_list.data = bts->si_common.data.si5_neigh_list; + bts->si_common.si5_neigh_list.data_len = + sizeof(bts->si_common.data.si5_neigh_list); + bts->si_common.cell_alloc.data = bts->si_common.data.cell_alloc; + bts->si_common.cell_alloc.data_len = + sizeof(bts->si_common.data.cell_alloc); + bts->si_common.rach_control.re = 1; /* no re-establishment */ + bts->si_common.rach_control.tx_integer = 9; /* 12 slots spread - 217/115 slots delay */ + bts->si_common.rach_control.max_trans = 3; /* 7 retransmissions */ + bts->si_common.rach_control.t2 = 4; /* no emergency calls */ + + for (i = 0; i < ARRAY_SIZE(bts->gprs.nsvc); i++) { + bts->gprs.nsvc[i].bts = bts; + bts->gprs.nsvc[i].id = i; + } + memcpy(&bts->gprs.nse.timer, bts_nse_timer_default, + sizeof(bts->gprs.nse.timer)); + memcpy(&bts->gprs.cell.timer, bts_cell_timer_default, + sizeof(bts->gprs.cell.timer)); + + /* 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; + INIT_LLIST_HEAD(&bts->abis_queue); + + llist_add_tail(&bts->list, &net->bts_list); + + return bts; +} + +struct gsm_network *gsm_network_init(u_int16_t country_code, u_int16_t network_code, + int (*mncc_recv)(struct gsm_network *, struct msgb *)) +{ + struct gsm_network *net; + + net = talloc_zero(tall_bsc_ctx, struct gsm_network); + if (!net) + return NULL; + + net->msc_data = talloc_zero(net, struct osmo_msc_data); + if (!net->msc_data) { + talloc_free(net); + return NULL; + } + + net->country_code = country_code; + net->network_code = network_code; + net->num_bts = 0; + net->reject_cause = GSM48_REJECT_ROAMING_NOT_ALLOWED; + net->T3101 = GSM_T3101_DEFAULT; + net->T3113 = GSM_T3113_DEFAULT; + /* FIXME: initialize all other timers! */ + + /* default set of handover parameters */ + net->handover.win_rxlev_avg = 10; + net->handover.win_rxqual_avg = 1; + net->handover.win_rxlev_avg_neigh = 10; + net->handover.pwr_interval = 6; + net->handover.pwr_hysteresis = 3; + net->handover.max_distance = 9999; + + INIT_LLIST_HEAD(&net->trans_list); + INIT_LLIST_HEAD(&net->upqueue); + INIT_LLIST_HEAD(&net->bts_list); + + net->stats.chreq.total = counter_alloc("net.chreq.total"); + net->stats.chreq.no_channel = counter_alloc("net.chreq.no_channel"); + net->stats.handover.attempted = counter_alloc("net.handover.attempted"); + net->stats.handover.no_channel = counter_alloc("net.handover.no_channel"); + net->stats.handover.timeout = counter_alloc("net.handover.timeout"); + net->stats.handover.completed = counter_alloc("net.handover.completed"); + net->stats.handover.failed = counter_alloc("net.handover.failed"); + net->stats.loc_upd_type.attach = counter_alloc("net.loc_upd_type.attach"); + net->stats.loc_upd_type.normal = counter_alloc("net.loc_upd_type.normal"); + net->stats.loc_upd_type.periodic = counter_alloc("net.loc_upd_type.periodic"); + net->stats.loc_upd_type.detach = counter_alloc("net.imsi_detach.count"); + net->stats.loc_upd_resp.reject = counter_alloc("net.loc_upd_resp.reject"); + net->stats.loc_upd_resp.accept = counter_alloc("net.loc_upd_resp.accept"); + net->stats.paging.attempted = counter_alloc("net.paging.attempted"); + net->stats.paging.detached = counter_alloc("net.paging.detached"); + net->stats.paging.completed = counter_alloc("net.paging.completed"); + net->stats.paging.expired = counter_alloc("net.paging.expired"); + net->stats.sms.submitted = counter_alloc("net.sms.submitted"); + net->stats.sms.no_receiver = counter_alloc("net.sms.no_receiver"); + net->stats.sms.delivered = counter_alloc("net.sms.delivered"); + net->stats.sms.rp_err_mem = counter_alloc("net.sms.rp_err_mem"); + net->stats.sms.rp_err_other = counter_alloc("net.sms.rp_err_other"); + net->stats.call.mo_setup = counter_alloc("net.call.mo_setup"); + net->stats.call.mo_connect_ack = counter_alloc("net.call.mo_connect_ack"); + net->stats.call.mt_setup = counter_alloc("net.call.mt_setup"); + net->stats.call.mt_connect = counter_alloc("net.call.mt_connect"); + net->stats.chan.rf_fail = counter_alloc("net.chan.rf_fail"); + net->stats.chan.rll_err = counter_alloc("net.chan.rll_err"); + net->stats.bts.oml_fail = counter_alloc("net.bts.oml_fail"); + net->stats.bts.rsl_fail = counter_alloc("net.bts.rsl_fail"); + + net->mncc_recv = mncc_recv; + + net->msc_data->msc_ip = talloc_strdup(net, "127.0.0.1"); + net->msc_data->msc_port = 5000; + net->msc_data->ping_timeout = 20; + net->msc_data->pong_timeout = 5; + net->msc_data->core_ncc = -1; + net->msc_data->core_mcc = -1; + net->msc_data->rtp_base = 4000; + + gsm_net_update_ctype(net); + + return net; +} + +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; +} + +/* Get reference to a neighbor cell on a given BCCH ARFCN */ +struct gsm_bts *gsm_bts_neighbor(const struct gsm_bts *bts, + u_int16_t arfcn, u_int8_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; +} + +struct gsm_bts_trx *gsm_bts_trx_num(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(struct gsm_bts_trx *trx) +{ + snprintf(ts2str, sizeof(ts2str), "(bts=%d,trx=%d)", + trx->bts->nr, trx->nr); + + return ts2str; +} + + +char *gsm_ts_name(struct gsm_bts_trx_ts *ts) +{ + snprintf(ts2str, sizeof(ts2str), "(bts=%d,trx=%d,ts=%d)", + ts->trx->bts->nr, ts->trx->nr, ts->nr); + + return ts2str; +} + +char *gsm_lchan_name(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; +} + +static const struct value_string bts_types[] = { + { GSM_BTS_TYPE_UNKNOWN, "unknown" }, + { GSM_BTS_TYPE_BS11, "bs11" }, + { GSM_BTS_TYPE_NANOBTS, "nanobts" }, + { GSM_BTS_TYPE_RBS2000, "rbs2000" }, + { 0, NULL } +}; + +enum gsm_bts_type parse_btstype(const char *arg) +{ + return get_string_value(bts_types, arg); +} + +const char *btstype2str(enum gsm_bts_type type) +{ + return get_value_string(bts_types, type); +} + +struct gsm_bts_trx *gsm_bts_trx_by_nr(struct gsm_bts *bts, int nr) +{ + struct gsm_bts_trx *trx; + + llist_for_each_entry(trx, &bts->trx_list, list) { + if (trx->nr == nr) + return trx; + } + return NULL; +} + +/* Search for a BTS in the given Location Area; optionally start searching + * with start_bts (for continuing to search after the first result) */ +struct gsm_bts *gsm_bts_by_lac(struct gsm_network *net, unsigned int lac, + struct gsm_bts *start_bts) +{ + int i; + struct gsm_bts *bts; + int skip = 0; + + if (start_bts) + skip = 1; + + for (i = 0; i < net->num_bts; i++) { + bts = gsm_bts_num(net, i); + + if (skip) { + if (start_bts == bts) + skip = 0; + continue; + } + + if (lac == GSM_LAC_RESERVED_ALL_BTS || bts->location_area_code == lac) + return bts; + } + return NULL; +} + +static const struct value_string auth_policy_names[] = { + { GSM_AUTH_POLICY_CLOSED, "closed" }, + { GSM_AUTH_POLICY_ACCEPT_ALL, "accept-all" }, + { GSM_AUTH_POLICY_TOKEN, "token" }, + { 0, NULL } +}; + +enum gsm_auth_policy gsm_auth_policy_parse(const char *arg) +{ + return get_string_value(auth_policy_names, arg); +} + +const char *gsm_auth_policy_name(enum gsm_auth_policy policy) +{ + return get_value_string(auth_policy_names, policy); +} + +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(u_int8_t *buf, struct gsm_bts *bts) +{ + struct gprs_ra_id raid; + + gprs_ra_id_by_bts(&raid, bts); + + return gsm48_construct_ra(buf, &raid); +} + +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) +{ + return get_string_value(bts_gprs_mode_names, arg); +} + +const char *bts_gprs_mode_name(enum bts_gprs_mode mode) +{ + return get_value_string(bts_gprs_mode_names, mode); +} + +struct gsm_meas_rep *lchan_next_meas_rep(struct gsm_lchan *lchan) +{ + struct gsm_meas_rep *meas_rep; + + meas_rep = &lchan->meas_rep[lchan->meas_rep_idx]; + memset(meas_rep, 0, sizeof(*meas_rep)); + meas_rep->lchan = lchan; + lchan->meas_rep_idx = (lchan->meas_rep_idx + 1) + % ARRAY_SIZE(lchan->meas_rep); + + return meas_rep; +} + +int gsm_btsmodel_set_feature(struct gsm_bts_model *bts, enum gsm_bts_features feat) +{ + return bitvec_set_bit_pos(&bts->features, feat, 1); +} + +int gsm_bts_has_feature(struct gsm_bts *bts, enum gsm_bts_features feat) +{ + return bitvec_get_bit_pos(&bts->model->features, feat); +} + +int gsm_set_bts_type(struct gsm_bts *bts, enum gsm_bts_type type) +{ + struct gsm_bts_model *model; + + model = bts_model_find(type); + if (!model) + return -EINVAL; + + bts->type = type; + bts->model = model; + + switch (bts->type) { + case GSM_BTS_TYPE_NANOBTS: + /* Set the default OML Stream ID to 0xff */ + bts->oml_tei = 0xff; + bts->c0->nominal_power = 23; + break; + case GSM_BTS_TYPE_BS11: + case GSM_BTS_TYPE_UNKNOWN: + break; + case GSM_BTS_TYPE_RBS2000: + INIT_LLIST_HEAD(&bts->rbs2000.is.conn_groups); + INIT_LLIST_HEAD(&bts->rbs2000.con.conn_groups); + break; + } + + return 0; +} diff --git a/openbsc/src/libcommon/socket.c b/openbsc/src/libcommon/socket.c new file mode 100644 index 000000000..47778e746 --- /dev/null +++ b/openbsc/src/libcommon/socket.c @@ -0,0 +1,108 @@ +/* OpenBSC sokcet code, taken from Abis input driver for ip.access */ + +/* (C) 2009 by Harald Welte + * (C) 2010 by Holger Hans Peter Freyther + * (C) 2010 by On-Waves + * + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +int make_sock(struct bsc_fd *bfd, int proto, u_int32_t ip, u_int16_t port, + int (*cb)(struct bsc_fd *fd, unsigned int what)) +{ + 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; + case IPPROTO_GRE: + type = SOCK_RAW; + break; + default: + return -EINVAL; + } + + bfd->fd = socket(AF_INET, type, proto); + bfd->cb = cb; + bfd->when = BSC_FD_READ; + //bfd->data = line; + + if (bfd->fd < 0) { + LOGP(DINP, 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) + 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(DINP, 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 = bsc_register_fd(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 new file mode 100644 index 000000000..8e7ec230f --- /dev/null +++ b/openbsc/src/libcommon/talloc_ctx.c @@ -0,0 +1,38 @@ +#include +#include + +extern void *tall_msgb_ctx; +extern void *tall_fle_ctx; +extern void *tall_locop_ctx; +extern void *tall_authciphop_ctx; +extern void *tall_gsms_ctx; +extern void *tall_subscr_ctx; +extern void *tall_sub_req_ctx; +extern void *tall_call_ctx; +extern void *tall_paging_ctx; +extern void *tall_sigh_ctx; +extern void *tall_tqe_ctx; +extern void *tall_trans_ctx; +extern void *tall_map_ctx; +extern void *tall_upq_ctx; +extern void *tall_ctr_ctx; + +void talloc_ctx_init(void) +{ + tall_msgb_ctx = talloc_named_const(tall_bsc_ctx, 0, "msgb"); + tall_fle_ctx = talloc_named_const(tall_bsc_ctx, 0, + "bs11_file_list_entry"); + tall_locop_ctx = talloc_named_const(tall_bsc_ctx, 0, "loc_updating_oper"); + tall_authciphop_ctx = talloc_named_const(tall_bsc_ctx, 0, "auth_ciph_oper"); + tall_gsms_ctx = talloc_named_const(tall_bsc_ctx, 0, "sms"); + tall_subscr_ctx = talloc_named_const(tall_bsc_ctx, 0, "subscriber"); + tall_sub_req_ctx = talloc_named_const(tall_bsc_ctx, 0, "subscr_request"); + tall_call_ctx = talloc_named_const(tall_bsc_ctx, 0, "gsm_call"); + tall_paging_ctx = talloc_named_const(tall_bsc_ctx, 0, "paging_request"); + tall_sigh_ctx = talloc_named_const(tall_bsc_ctx, 0, "signal_handler"); + tall_tqe_ctx = talloc_named_const(tall_bsc_ctx, 0, "subch_txq_entry"); + tall_trans_ctx = talloc_named_const(tall_bsc_ctx, 0, "transaction"); + tall_map_ctx = talloc_named_const(tall_bsc_ctx, 0, "trau_map_entry"); + tall_upq_ctx = talloc_named_const(tall_bsc_ctx, 0, "trau_upq_entry"); + tall_ctr_ctx = talloc_named_const(tall_bsc_ctx, 0, "counter"); +} diff --git a/openbsc/src/libgb/Makefile.am b/openbsc/src/libgb/Makefile.am new file mode 100644 index 000000000..b48b17791 --- /dev/null +++ b/openbsc/src/libgb/Makefile.am @@ -0,0 +1,9 @@ +INCLUDES = $(all_includes) -I$(top_srcdir)/include -I$(top_builddir) +AM_CFLAGS=-Wall -fno-strict-aliasing $(LIBOSMOCORE_CFLAGS) $(LIBOSMOVTY_CFLAGS) $(COVERAGE_CFLAGS) +AM_LDFLAGS = $(LIBOSMOCORE_LIBS) $(LIBOSMOVTY_LIBS) $(COVERAGE_LDFLAGS) + +noinst_LIBRARIES = libgb.a + +libgb_a_SOURCES = gprs_ns.c gprs_ns_frgre.c gprs_ns_vty.c \ + gprs_bssgp.c gprs_bssgp_util.c gprs_bssgp_vty.c +#gprs_llc.c gprs_llc_vty.c crc24.c diff --git a/openbsc/src/libgb/gprs_bssgp.c b/openbsc/src/libgb/gprs_bssgp.c new file mode 100644 index 000000000..eca34b989 --- /dev/null +++ b/openbsc/src/libgb/gprs_bssgp.c @@ -0,0 +1,856 @@ +/* GPRS BSSGP protocol implementation as per 3GPP TS 08.18 */ + +/* (C) 2009-2010 by Harald Welte + * + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + * + * TODO: + * o properly count incoming BVC-RESET packets in counter group + * o set log context as early as possible for outgoing packets + */ + +#include +#include + +#include + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +void *bssgp_tall_ctx = NULL; + +#define BVC_F_BLOCKED 0x0001 + +enum bssgp_ctr { + BSSGP_CTR_PKTS_IN, + BSSGP_CTR_PKTS_OUT, + BSSGP_CTR_BYTES_IN, + BSSGP_CTR_BYTES_OUT, + BSSGP_CTR_BLOCKED, + BSSGP_CTR_DISCARDED, +}; + +static const struct rate_ctr_desc bssgp_ctr_description[] = { + { "packets.in", "Packets at BSSGP Level ( In)" }, + { "packets.out","Packets at BSSGP Level (Out)" }, + { "bytes.in", "Bytes at BSSGP Level ( In)" }, + { "bytes.out", "Bytes at BSSGP Level (Out)" }, + { "blocked", "BVC Blocking count" }, + { "discarded", "BVC LLC Discarded count" }, +}; + +static const struct rate_ctr_group_desc bssgp_ctrg_desc = { + .group_name_prefix = "bssgp.bss_ctx", + .group_description = "BSSGP Peer Statistics", + .num_ctr = ARRAY_SIZE(bssgp_ctr_description), + .ctr_desc = bssgp_ctr_description, +}; + +LLIST_HEAD(bssgp_bvc_ctxts); + +/* Find a BTS Context based on parsed RA ID and Cell ID */ +struct bssgp_bvc_ctx *btsctx_by_raid_cid(const struct gprs_ra_id *raid, uint16_t cid) +{ + struct bssgp_bvc_ctx *bctx; + + llist_for_each_entry(bctx, &bssgp_bvc_ctxts, list) { + if (!memcmp(&bctx->ra_id, raid, sizeof(bctx->ra_id)) && + bctx->cell_id == cid) + return bctx; + } + return NULL; +} + +/* Find a BTS context based on BVCI+NSEI tuple */ +struct bssgp_bvc_ctx *btsctx_by_bvci_nsei(uint16_t bvci, uint16_t nsei) +{ + struct bssgp_bvc_ctx *bctx; + + llist_for_each_entry(bctx, &bssgp_bvc_ctxts, list) { + if (bctx->nsei == nsei && bctx->bvci == bvci) + return bctx; + } + return NULL; +} + +struct bssgp_bvc_ctx *btsctx_alloc(uint16_t bvci, uint16_t nsei) +{ + struct bssgp_bvc_ctx *ctx; + + ctx = talloc_zero(bssgp_tall_ctx, struct bssgp_bvc_ctx); + if (!ctx) + return NULL; + ctx->bvci = bvci; + ctx->nsei = nsei; + /* FIXME: BVCI is not unique, only BVCI+NSEI ?!? */ + ctx->ctrg = rate_ctr_group_alloc(ctx, &bssgp_ctrg_desc, bvci); + + llist_add(&ctx->list, &bssgp_bvc_ctxts); + + return ctx; +} + +/* Chapter 10.4.5: Flow Control BVC ACK */ +static int bssgp_tx_fc_bvc_ack(uint16_t nsei, uint8_t tag, uint16_t ns_bvci) +{ + struct msgb *msg = bssgp_msgb_alloc(); + struct bssgp_normal_hdr *bgph = + (struct bssgp_normal_hdr *) msgb_put(msg, sizeof(*bgph)); + + msgb_nsei(msg) = nsei; + msgb_bvci(msg) = ns_bvci; + + bgph->pdu_type = BSSGP_PDUT_FLOW_CONTROL_BVC_ACK; + msgb_tvlv_put(msg, BSSGP_IE_TAG, 1, &tag); + + return gprs_ns_sendmsg(bssgp_nsi, msg); +} + +/* 10.3.7 SUSPEND-ACK PDU */ +int bssgp_tx_suspend_ack(uint16_t nsei, uint32_t tlli, + const struct gprs_ra_id *ra_id, uint8_t suspend_ref) +{ + struct msgb *msg = bssgp_msgb_alloc(); + struct bssgp_normal_hdr *bgph = + (struct bssgp_normal_hdr *) msgb_put(msg, sizeof(*bgph)); + uint32_t _tlli; + uint8_t ra[6]; + + msgb_nsei(msg) = nsei; + msgb_bvci(msg) = 0; /* Signalling */ + bgph->pdu_type = BSSGP_PDUT_SUSPEND_ACK; + + _tlli = htonl(tlli); + msgb_tvlv_put(msg, BSSGP_IE_TLLI, 4, (uint8_t *) &_tlli); + gsm48_construct_ra(ra, ra_id); + msgb_tvlv_put(msg, BSSGP_IE_ROUTEING_AREA, 6, ra); + msgb_tvlv_put(msg, BSSGP_IE_SUSPEND_REF_NR, 1, &suspend_ref); + + return gprs_ns_sendmsg(bssgp_nsi, msg); +} + +/* 10.3.8 SUSPEND-NACK PDU */ +int bssgp_tx_suspend_nack(uint16_t nsei, uint32_t tlli, + const struct gprs_ra_id *ra_id, + uint8_t *cause) +{ + struct msgb *msg = bssgp_msgb_alloc(); + struct bssgp_normal_hdr *bgph = + (struct bssgp_normal_hdr *) msgb_put(msg, sizeof(*bgph)); + uint32_t _tlli; + uint8_t ra[6]; + + msgb_nsei(msg) = nsei; + msgb_bvci(msg) = 0; /* Signalling */ + bgph->pdu_type = BSSGP_PDUT_SUSPEND_NACK; + + _tlli = htonl(tlli); + msgb_tvlv_put(msg, BSSGP_IE_TLLI, 4, (uint8_t *) &_tlli); + gsm48_construct_ra(ra, ra_id); + msgb_tvlv_put(msg, BSSGP_IE_ROUTEING_AREA, 6, ra); + if (cause) + msgb_tvlv_put(msg, BSSGP_IE_CAUSE, 1, cause); + + return gprs_ns_sendmsg(bssgp_nsi, msg); +} + +/* 10.3.10 RESUME-ACK PDU */ +int bssgp_tx_resume_ack(uint16_t nsei, uint32_t tlli, + const struct gprs_ra_id *ra_id) +{ + struct msgb *msg = bssgp_msgb_alloc(); + struct bssgp_normal_hdr *bgph = + (struct bssgp_normal_hdr *) msgb_put(msg, sizeof(*bgph)); + uint32_t _tlli; + uint8_t ra[6]; + + msgb_nsei(msg) = nsei; + msgb_bvci(msg) = 0; /* Signalling */ + bgph->pdu_type = BSSGP_PDUT_RESUME_ACK; + + _tlli = htonl(tlli); + msgb_tvlv_put(msg, BSSGP_IE_TLLI, 4, (uint8_t *) &_tlli); + gsm48_construct_ra(ra, ra_id); + msgb_tvlv_put(msg, BSSGP_IE_ROUTEING_AREA, 6, ra); + + return gprs_ns_sendmsg(bssgp_nsi, msg); +} + +/* 10.3.11 RESUME-NACK PDU */ +int bssgp_tx_resume_nack(uint16_t nsei, uint32_t tlli, + const struct gprs_ra_id *ra_id, uint8_t *cause) +{ + struct msgb *msg = bssgp_msgb_alloc(); + struct bssgp_normal_hdr *bgph = + (struct bssgp_normal_hdr *) msgb_put(msg, sizeof(*bgph)); + uint32_t _tlli; + uint8_t ra[6]; + + msgb_nsei(msg) = nsei; + msgb_bvci(msg) = 0; /* Signalling */ + bgph->pdu_type = BSSGP_PDUT_SUSPEND_NACK; + + _tlli = htonl(tlli); + msgb_tvlv_put(msg, BSSGP_IE_TLLI, 4, (uint8_t *) &_tlli); + gsm48_construct_ra(ra, ra_id); + msgb_tvlv_put(msg, BSSGP_IE_ROUTEING_AREA, 6, ra); + if (cause) + msgb_tvlv_put(msg, BSSGP_IE_CAUSE, 1, cause); + + return gprs_ns_sendmsg(bssgp_nsi, msg); +} + +uint16_t bssgp_parse_cell_id(struct gprs_ra_id *raid, const uint8_t *buf) +{ + /* 6 octets RAC */ + gsm48_parse_ra(raid, buf); + /* 2 octets CID */ + return ntohs(*(uint16_t *) (buf+6)); +} + +/* Chapter 8.4 BVC-Reset Procedure */ +static int bssgp_rx_bvc_reset(struct msgb *msg, struct tlv_parsed *tp, + uint16_t ns_bvci) +{ + struct bssgp_bvc_ctx *bctx; + uint16_t nsei = msgb_nsei(msg); + uint16_t bvci; + int rc; + + bvci = ntohs(*(uint16_t *)TLVP_VAL(tp, BSSGP_IE_BVCI)); + DEBUGP(DBSSGP, "BSSGP BVCI=%u Rx RESET cause=%s\n", bvci, + bssgp_cause_str(*TLVP_VAL(tp, BSSGP_IE_CAUSE))); + + /* look-up or create the BTS context for this BVC */ + bctx = btsctx_by_bvci_nsei(bvci, nsei); + if (!bctx) + bctx = btsctx_alloc(bvci, nsei); + + /* As opposed to NS-VCs, BVCs are NOT blocked after RESET */ + bctx->state &= ~BVC_S_BLOCKED; + + /* When we receive a BVC-RESET PDU (at least of a PTP BVCI), the BSS + * informs us about its RAC + Cell ID, so we can create a mapping */ + if (bvci != 0 && bvci != 1) { + if (!TLVP_PRESENT(tp, BSSGP_IE_CELL_ID)) { + LOGP(DBSSGP, LOGL_ERROR, "BSSGP BVCI=%u RESET " + "missing mandatory IE\n", bvci); + return -EINVAL; + } + /* actually extract RAC / CID */ + bctx->cell_id = bssgp_parse_cell_id(&bctx->ra_id, + TLVP_VAL(tp, BSSGP_IE_CELL_ID)); + LOGP(DBSSGP, LOGL_NOTICE, "Cell %u-%u-%u-%u CI %u on BVCI %u\n", + bctx->ra_id.mcc, bctx->ra_id.mnc, bctx->ra_id.lac, + bctx->ra_id.rac, bctx->cell_id, bvci); + } + + /* Acknowledge the RESET to the BTS */ + rc = bssgp_tx_simple_bvci(BSSGP_PDUT_BVC_RESET_ACK, + nsei, bvci, ns_bvci); + return 0; +} + +static int bssgp_rx_bvc_block(struct msgb *msg, struct tlv_parsed *tp) +{ + uint16_t bvci; + struct bssgp_bvc_ctx *ptp_ctx; + + bvci = ntohs(*(uint16_t *)TLVP_VAL(tp, BSSGP_IE_BVCI)); + if (bvci == BVCI_SIGNALLING) { + /* 8.3.2: Signalling BVC shall never be blocked */ + LOGP(DBSSGP, LOGL_ERROR, "NSEI=%u/BVCI=%u " + "received block for signalling BVC!?!\n", + msgb_nsei(msg), msgb_bvci(msg)); + return 0; + } + + LOGP(DBSSGP, LOGL_INFO, "BSSGP BVCI=%u BVC-BLOCK\n", bvci); + + ptp_ctx = btsctx_by_bvci_nsei(bvci, msgb_nsei(msg)); + if (!ptp_ctx) + return bssgp_tx_status(BSSGP_CAUSE_UNKNOWN_BVCI, &bvci, msg); + + ptp_ctx->state |= BVC_S_BLOCKED; + rate_ctr_inc(&ptp_ctx->ctrg->ctr[BSSGP_CTR_BLOCKED]); + + /* FIXME: Send NM_BVC_BLOCK.ind to NM */ + + /* We always acknowledge the BLOCKing */ + return bssgp_tx_simple_bvci(BSSGP_PDUT_BVC_BLOCK_ACK, msgb_nsei(msg), + bvci, msgb_bvci(msg)); +}; + +static int bssgp_rx_bvc_unblock(struct msgb *msg, struct tlv_parsed *tp) +{ + uint16_t bvci; + struct bssgp_bvc_ctx *ptp_ctx; + + bvci = ntohs(*(uint16_t *)TLVP_VAL(tp, BSSGP_IE_BVCI)); + if (bvci == BVCI_SIGNALLING) { + /* 8.3.2: Signalling BVC shall never be blocked */ + LOGP(DBSSGP, LOGL_ERROR, "NSEI=%u/BVCI=%u " + "received unblock for signalling BVC!?!\n", + msgb_nsei(msg), msgb_bvci(msg)); + return 0; + } + + DEBUGP(DBSSGP, "BSSGP BVCI=%u Rx BVC-UNBLOCK\n", bvci); + + ptp_ctx = btsctx_by_bvci_nsei(bvci, msgb_nsei(msg)); + if (!ptp_ctx) + return bssgp_tx_status(BSSGP_CAUSE_UNKNOWN_BVCI, &bvci, msg); + + ptp_ctx->state &= ~BVC_S_BLOCKED; + + /* FIXME: Send NM_BVC_UNBLOCK.ind to NM */ + + /* We always acknowledge the unBLOCKing */ + return bssgp_tx_simple_bvci(BSSGP_PDUT_BVC_UNBLOCK_ACK, msgb_nsei(msg), + bvci, msgb_bvci(msg)); +}; + +/* Uplink unit-data */ +static int bssgp_rx_ul_ud(struct msgb *msg, struct tlv_parsed *tp, + struct bssgp_bvc_ctx *ctx) +{ + struct bssgp_ud_hdr *budh = (struct bssgp_ud_hdr *) msgb_bssgph(msg); + + /* extract TLLI and parse TLV IEs */ + msgb_tlli(msg) = ntohl(budh->tlli); + + DEBUGP(DBSSGP, "BSSGP TLLI=0x%08x UPLINK-UNITDATA\n", msgb_tlli(msg)); + + /* Cell ID and LLC_PDU are the only mandatory IE */ + if (!TLVP_PRESENT(tp, BSSGP_IE_CELL_ID) || + !TLVP_PRESENT(tp, BSSGP_IE_LLC_PDU)) { + LOGP(DBSSGP, LOGL_ERROR, "BSSGP TLLI=0x%08x Rx UL-UD " + "missing mandatory IE\n", msgb_tlli(msg)); + return bssgp_tx_status(BSSGP_CAUSE_MISSING_MAND_IE, NULL, msg); + } + + /* store pointer to LLC header and CELL ID in msgb->cb */ + msgb_llch(msg) = (uint8_t *) TLVP_VAL(tp, BSSGP_IE_LLC_PDU); + msgb_bcid(msg) = (uint8_t *) TLVP_VAL(tp, BSSGP_IE_CELL_ID); + + return gprs_llc_rcvmsg(msg, tp); +} + +static int bssgp_rx_suspend(struct msgb *msg, struct tlv_parsed *tp, + struct bssgp_bvc_ctx *ctx) +{ + struct bssgp_normal_hdr *bgph = + (struct bssgp_normal_hdr *) msgb_bssgph(msg); + struct gprs_ra_id raid; + uint32_t tlli; + int rc; + + if (!TLVP_PRESENT(tp, BSSGP_IE_TLLI) || + !TLVP_PRESENT(tp, BSSGP_IE_ROUTEING_AREA)) { + LOGP(DBSSGP, LOGL_ERROR, "BSSGP BVCI=%u Rx SUSPEND " + "missing mandatory IE\n", ctx->bvci); + return bssgp_tx_status(BSSGP_CAUSE_MISSING_MAND_IE, NULL, msg); + } + + tlli = ntohl(*(uint32_t *)TLVP_VAL(tp, BSSGP_IE_TLLI)); + + DEBUGP(DBSSGP, "BSSGP BVCI=%u TLLI=0x%08x Rx SUSPEND\n", + ctx->bvci, tlli); + + gsm48_parse_ra(&raid, TLVP_VAL(tp, BSSGP_IE_ROUTEING_AREA)); + + /* Inform GMM about the SUSPEND request */ + rc = gprs_gmm_rx_suspend(&raid, tlli); + if (rc < 0) + return bssgp_tx_suspend_nack(msgb_nsei(msg), tlli, &raid, NULL); + + bssgp_tx_suspend_ack(msgb_nsei(msg), tlli, &raid, 0); + + return 0; +} + +static int bssgp_rx_resume(struct msgb *msg, struct tlv_parsed *tp, + struct bssgp_bvc_ctx *ctx) +{ + struct bssgp_normal_hdr *bgph = + (struct bssgp_normal_hdr *) msgb_bssgph(msg); + struct gprs_ra_id raid; + uint32_t tlli; + uint8_t suspend_ref; + int rc; + + if (!TLVP_PRESENT(tp, BSSGP_IE_TLLI) || + !TLVP_PRESENT(tp, BSSGP_IE_ROUTEING_AREA) || + !TLVP_PRESENT(tp, BSSGP_IE_SUSPEND_REF_NR)) { + LOGP(DBSSGP, LOGL_ERROR, "BSSGP BVCI=%u Rx RESUME " + "missing mandatory IE\n", ctx->bvci); + return bssgp_tx_status(BSSGP_CAUSE_MISSING_MAND_IE, NULL, msg); + } + + tlli = ntohl(*(uint32_t *)TLVP_VAL(tp, BSSGP_IE_TLLI)); + suspend_ref = *TLVP_VAL(tp, BSSGP_IE_SUSPEND_REF_NR); + + DEBUGP(DBSSGP, "BSSGP BVCI=%u TLLI=0x%08x RESUME\n", ctx->bvci, tlli); + + gsm48_parse_ra(&raid, TLVP_VAL(tp, BSSGP_IE_ROUTEING_AREA)); + + /* Inform GMM about the RESUME request */ + rc = gprs_gmm_rx_resume(&raid, tlli, suspend_ref); + if (rc < 0) + return bssgp_tx_resume_nack(msgb_nsei(msg), tlli, &raid, + NULL); + + bssgp_tx_resume_ack(msgb_nsei(msg), tlli, &raid); + return 0; +} + + +static int bssgp_rx_llc_disc(struct msgb *msg, struct tlv_parsed *tp, + struct bssgp_bvc_ctx *ctx) +{ + uint32_t tlli = 0; + + if (!TLVP_PRESENT(tp, BSSGP_IE_TLLI) || + !TLVP_PRESENT(tp, BSSGP_IE_LLC_FRAMES_DISCARDED) || + !TLVP_PRESENT(tp, BSSGP_IE_BVCI) || + !TLVP_PRESENT(tp, BSSGP_IE_NUM_OCT_AFF)) { + LOGP(DBSSGP, LOGL_ERROR, "BSSGP BVCI=%u Rx LLC DISCARDED " + "missing mandatory IE\n", ctx->bvci); + } + + if (TLVP_PRESENT(tp, BSSGP_IE_TLLI)) + tlli = ntohl(*(uint32_t *)TLVP_VAL(tp, BSSGP_IE_TLLI)); + + DEBUGP(DBSSGP, "BSSGP BVCI=%u TLLI=%08x LLC DISCARDED\n", + ctx->bvci, tlli); + + rate_ctr_inc(&ctx->ctrg->ctr[BSSGP_CTR_DISCARDED]); + + /* FIXME: send NM_LLC_DISCARDED to NM */ + return 0; +} + +static int bssgp_rx_fc_bvc(struct msgb *msg, struct tlv_parsed *tp, + struct bssgp_bvc_ctx *bctx) +{ + + DEBUGP(DBSSGP, "BSSGP BVCI=%u Rx Flow Control BVC\n", + bctx->bvci); + + if (!TLVP_PRESENT(tp, BSSGP_IE_TAG) || + !TLVP_PRESENT(tp, BSSGP_IE_BVC_BUCKET_SIZE) || + !TLVP_PRESENT(tp, BSSGP_IE_BUCKET_LEAK_RATE) || + !TLVP_PRESENT(tp, BSSGP_IE_BMAX_DEFAULT_MS) || + !TLVP_PRESENT(tp, BSSGP_IE_R_DEFAULT_MS)) { + LOGP(DBSSGP, LOGL_ERROR, "BSSGP BVCI=%u Rx FC BVC " + "missing mandatory IE\n", bctx->bvci); + return bssgp_tx_status(BSSGP_CAUSE_MISSING_MAND_IE, NULL, msg); + } + + /* FIXME: actually implement flow control */ + + /* Send FLOW_CONTROL_BVC_ACK */ + return bssgp_tx_fc_bvc_ack(msgb_nsei(msg), *TLVP_VAL(tp, BSSGP_IE_TAG), + msgb_bvci(msg)); +} + +/* Receive a BSSGP PDU from a BSS on a PTP BVCI */ +static int gprs_bssgp_rx_ptp(struct msgb *msg, struct tlv_parsed *tp, + struct bssgp_bvc_ctx *bctx) +{ + struct bssgp_normal_hdr *bgph = + (struct bssgp_normal_hdr *) msgb_bssgph(msg); + uint8_t pdu_type = bgph->pdu_type; + int rc = 0; + + /* If traffic is received on a BVC that is marked as blocked, the + * received PDU shall not be accepted and a STATUS PDU (Cause value: + * BVC Blocked) shall be sent to the peer entity on the signalling BVC */ + if (bctx->state & BVC_S_BLOCKED && pdu_type != BSSGP_PDUT_STATUS) { + uint16_t bvci = msgb_bvci(msg); + return bssgp_tx_status(BSSGP_CAUSE_BVCI_BLOCKED, &bvci, msg); + } + + switch (pdu_type) { + case BSSGP_PDUT_UL_UNITDATA: + /* some LLC data from the MS */ + rc = bssgp_rx_ul_ud(msg, tp, bctx); + break; + case BSSGP_PDUT_RA_CAPABILITY: + /* BSS requests RA capability or IMSI */ + DEBUGP(DBSSGP, "BSSGP BVCI=%u Rx RA CAPABILITY UPDATE\n", + bctx->bvci); + /* FIXME: send GMM_RA_CAPABILITY_UPDATE.ind to GMM */ + /* FIXME: send RA_CAPA_UPDATE_ACK */ + break; + case BSSGP_PDUT_RADIO_STATUS: + DEBUGP(DBSSGP, "BSSGP BVCI=%u Rx RADIO STATUS\n", bctx->bvci); + /* BSS informs us of some exception */ + /* FIXME: send GMM_RADIO_STATUS.ind to GMM */ + break; + case BSSGP_PDUT_FLOW_CONTROL_BVC: + /* BSS informs us of available bandwidth in Gb interface */ + rc = bssgp_rx_fc_bvc(msg, tp, bctx); + break; + case BSSGP_PDUT_FLOW_CONTROL_MS: + /* BSS informs us of available bandwidth to one MS */ + DEBUGP(DBSSGP, "BSSGP BVCI=%u Rx Flow Control MS\n", + bctx->bvci); + /* FIXME: actually implement flow control */ + /* FIXME: Send FLOW_CONTROL_MS_ACK */ + break; + case BSSGP_PDUT_STATUS: + /* Some exception has occurred */ + /* FIXME: send NM_STATUS.ind to NM */ + case BSSGP_PDUT_DOWNLOAD_BSS_PFC: + case BSSGP_PDUT_CREATE_BSS_PFC_ACK: + case BSSGP_PDUT_CREATE_BSS_PFC_NACK: + case BSSGP_PDUT_MODIFY_BSS_PFC: + case BSSGP_PDUT_DELETE_BSS_PFC_ACK: + DEBUGP(DBSSGP, "BSSGP BVCI=%u Rx PDU type 0x%02x not [yet] " + "implemented\n", bctx->bvci, pdu_type); + rc = bssgp_tx_status(BSSGP_CAUSE_PDU_INCOMP_FEAT, NULL, msg); + break; + /* those only exist in the SGSN -> BSS direction */ + case BSSGP_PDUT_DL_UNITDATA: + case BSSGP_PDUT_PAGING_PS: + case BSSGP_PDUT_PAGING_CS: + case BSSGP_PDUT_RA_CAPA_UPDATE_ACK: + case BSSGP_PDUT_FLOW_CONTROL_BVC_ACK: + case BSSGP_PDUT_FLOW_CONTROL_MS_ACK: + DEBUGP(DBSSGP, "BSSGP BVCI=%u PDU type 0x%02x only exists " + "in DL\n", bctx->bvci, pdu_type); + bssgp_tx_status(BSSGP_CAUSE_PROTO_ERR_UNSPEC, NULL, msg); + rc = -EINVAL; + break; + default: + DEBUGP(DBSSGP, "BSSGP BVCI=%u PDU type 0x%02x unknown\n", + bctx->bvci, pdu_type); + rc = bssgp_tx_status(BSSGP_CAUSE_PROTO_ERR_UNSPEC, NULL, msg); + break; + } + + return rc; +} + +/* Receive a BSSGP PDU from a BSS on a SIGNALLING BVCI */ +static int gprs_bssgp_rx_sign(struct msgb *msg, struct tlv_parsed *tp, + struct bssgp_bvc_ctx *bctx) +{ + struct bssgp_normal_hdr *bgph = + (struct bssgp_normal_hdr *) msgb_bssgph(msg); + uint8_t pdu_type = bgph->pdu_type; + int rc = 0; + uint16_t ns_bvci = msgb_bvci(msg); + uint16_t bvci; + + switch (bgph->pdu_type) { + case BSSGP_PDUT_SUSPEND: + /* MS wants to suspend */ + rc = bssgp_rx_suspend(msg, tp, bctx); + break; + case BSSGP_PDUT_RESUME: + /* MS wants to resume */ + rc = bssgp_rx_resume(msg, tp, bctx); + break; + case BSSGP_PDUT_FLUSH_LL_ACK: + /* BSS informs us it has performed LL FLUSH */ + DEBUGP(DBSSGP, "BSSGP BVCI=%u Rx FLUSH LL ACK\n", bctx->bvci); + /* FIXME: send NM_FLUSH_LL.res to NM */ + break; + case BSSGP_PDUT_LLC_DISCARD: + /* BSS informs that some LLC PDU's have been discarded */ + rc = bssgp_rx_llc_disc(msg, tp, bctx); + break; + case BSSGP_PDUT_BVC_BLOCK: + /* BSS tells us that BVC shall be blocked */ + if (!TLVP_PRESENT(tp, BSSGP_IE_BVCI) || + !TLVP_PRESENT(tp, BSSGP_IE_CAUSE)) { + LOGP(DBSSGP, LOGL_ERROR, "BSSGP Rx BVC-BLOCK " + "missing mandatory IE\n"); + goto err_mand_ie; + } + rc = bssgp_rx_bvc_block(msg, tp); + break; + case BSSGP_PDUT_BVC_UNBLOCK: + /* BSS tells us that BVC shall be unblocked */ + if (!TLVP_PRESENT(tp, BSSGP_IE_BVCI)) { + LOGP(DBSSGP, LOGL_ERROR, "BSSGP Rx BVC-UNBLOCK " + "missing mandatory IE\n"); + goto err_mand_ie; + } + rc = bssgp_rx_bvc_unblock(msg, tp); + break; + case BSSGP_PDUT_BVC_RESET: + /* BSS tells us that BVC init is required */ + if (!TLVP_PRESENT(tp, BSSGP_IE_BVCI) || + !TLVP_PRESENT(tp, BSSGP_IE_CAUSE)) { + LOGP(DBSSGP, LOGL_ERROR, "BSSGP Rx BVC-RESET " + "missing mandatory IE\n"); + goto err_mand_ie; + } + rc = bssgp_rx_bvc_reset(msg, tp, ns_bvci); + break; + case BSSGP_PDUT_STATUS: + /* Some exception has occurred */ + DEBUGP(DBSSGP, "BSSGP BVCI=%u Rx BVC STATUS\n", bctx->bvci); + /* FIXME: send NM_STATUS.ind to NM */ + break; + /* those only exist in the SGSN -> BSS direction */ + case BSSGP_PDUT_PAGING_PS: + case BSSGP_PDUT_PAGING_CS: + case BSSGP_PDUT_SUSPEND_ACK: + case BSSGP_PDUT_SUSPEND_NACK: + case BSSGP_PDUT_RESUME_ACK: + case BSSGP_PDUT_RESUME_NACK: + case BSSGP_PDUT_FLUSH_LL: + case BSSGP_PDUT_BVC_BLOCK_ACK: + case BSSGP_PDUT_BVC_UNBLOCK_ACK: + case BSSGP_PDUT_SGSN_INVOKE_TRACE: + DEBUGP(DBSSGP, "BSSGP BVCI=%u Rx PDU type 0x%02x only exists " + "in DL\n", bctx->bvci, pdu_type); + bssgp_tx_status(BSSGP_CAUSE_PROTO_ERR_UNSPEC, NULL, msg); + rc = -EINVAL; + break; + default: + DEBUGP(DBSSGP, "BSSGP BVCI=%u Rx PDU type 0x%02x unknown\n", + bctx->bvci, pdu_type); + rc = bssgp_tx_status(BSSGP_CAUSE_PROTO_ERR_UNSPEC, NULL, msg); + break; + } + + return rc; +err_mand_ie: + return bssgp_tx_status(BSSGP_CAUSE_MISSING_MAND_IE, NULL, msg); +} + +/* We expect msgb_bssgph() to point to the BSSGP header */ +int gprs_bssgp_rcvmsg(struct msgb *msg) +{ + struct bssgp_normal_hdr *bgph = + (struct bssgp_normal_hdr *) msgb_bssgph(msg); + struct bssgp_ud_hdr *budh = (struct bssgp_ud_hdr *) msgb_bssgph(msg); + struct tlv_parsed tp; + struct bssgp_bvc_ctx *bctx; + uint8_t pdu_type = bgph->pdu_type; + uint16_t ns_bvci = msgb_bvci(msg); + int data_len; + int rc = 0; + + /* Identifiers from DOWN: NSEI, BVCI (both in msg->cb) */ + + /* UNITDATA BSSGP headers have TLLI in front */ + if (pdu_type != BSSGP_PDUT_UL_UNITDATA && + pdu_type != BSSGP_PDUT_DL_UNITDATA) { + data_len = msgb_bssgp_len(msg) - sizeof(*bgph); + rc = bssgp_tlv_parse(&tp, bgph->data, data_len); + } else { + data_len = msgb_bssgp_len(msg) - sizeof(*budh); + rc = bssgp_tlv_parse(&tp, budh->data, data_len); + } + + /* look-up or create the BTS context for this BVC */ + bctx = btsctx_by_bvci_nsei(ns_bvci, msgb_nsei(msg)); + /* Only a RESET PDU can create a new BVC context */ + if (!bctx && pdu_type != BSSGP_PDUT_BVC_RESET) { + LOGP(DBSSGP, LOGL_NOTICE, "NSEI=%u/BVCI=%u Rejecting PDU " + "type %u for unknown BVCI\n", msgb_nsei(msg), ns_bvci, + pdu_type); + return bssgp_tx_status(BSSGP_CAUSE_UNKNOWN_BVCI, NULL, msg); + } + + if (bctx) { + log_set_context(BSC_CTX_BVC, bctx); + rate_ctr_inc(&bctx->ctrg->ctr[BSSGP_CTR_PKTS_IN]); + rate_ctr_add(&bctx->ctrg->ctr[BSSGP_CTR_BYTES_IN], + msgb_bssgp_len(msg)); + } + + if (ns_bvci == BVCI_SIGNALLING) + rc = gprs_bssgp_rx_sign(msg, &tp, bctx); + else if (ns_bvci == BVCI_PTM) + rc = bssgp_tx_status(BSSGP_CAUSE_PDU_INCOMP_FEAT, NULL, msg); + else + rc = gprs_bssgp_rx_ptp(msg, &tp, bctx); + + return rc; +} + +/* 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 */ +int gprs_bssgp_tx_dl_ud(struct msgb *msg, struct sgsn_mm_ctx *mmctx) +{ + struct bssgp_bvc_ctx *bctx; + struct bssgp_ud_hdr *budh; + uint8_t llc_pdu_tlv_hdr_len = 2; + uint8_t *llc_pdu_tlv, *qos_profile; + uint16_t pdu_lifetime = 1000; /* centi-seconds */ + uint8_t qos_profile_default[3] = { 0x00, 0x00, 0x20 }; + uint16_t msg_len = msg->len; + uint16_t bvci = msgb_bvci(msg); + uint16_t nsei = msgb_nsei(msg); + uint16_t drx_params; + + /* Identifiers from UP: TLLI, BVCI, NSEI (all in msgb->cb) */ + if (bvci <= BVCI_PTM ) { + LOGP(DBSSGP, LOGL_ERROR, "Cannot send DL-UD to BVCI %u\n", + bvci); + return -EINVAL; + } + + bctx = btsctx_by_bvci_nsei(bvci, nsei); + if (!bctx) { + /* FIXME: don't simply create missing context, but reject message */ + bctx = btsctx_alloc(bvci, nsei); + } + + if (msg->len > TVLV_MAX_ONEBYTE) + llc_pdu_tlv_hdr_len += 1; + + /* prepend the tag and length of the LLC-PDU TLV */ + llc_pdu_tlv = msgb_push(msg, llc_pdu_tlv_hdr_len); + llc_pdu_tlv[0] = BSSGP_IE_LLC_PDU; + if (llc_pdu_tlv_hdr_len > 2) { + llc_pdu_tlv[1] = msg_len >> 8; + llc_pdu_tlv[2] = msg_len & 0xff; + } else { + llc_pdu_tlv[1] = msg_len & 0x7f; + llc_pdu_tlv[1] |= 0x80; + } + + /* FIXME: optional elements: Alignment, UTRAN CCO, LSA, PFI */ + + if (mmctx) { + /* Old TLLI to help BSS map from old->new */ +#if 0 + if (mmctx->tlli_old) + msgb_tvlv_push(msg, BSSGP_IE_TLLI, 4, htonl(*tlli_old)); +#endif + + /* IMSI */ + if (strlen(mmctx->imsi)) { + uint8_t mi[10]; + int imsi_len = gsm48_generate_mid_from_imsi(mi, mmctx->imsi); + if (imsi_len > 2) + msgb_tvlv_push(msg, BSSGP_IE_IMSI, + imsi_len-2, mi+2); + } + + /* DRX parameters */ + drx_params = htons(mmctx->drx_parms); + msgb_tvlv_push(msg, BSSGP_IE_DRX_PARAMS, 2, + (uint8_t *) &drx_params); + + /* FIXME: Priority */ + + /* MS Radio Access Capability */ + if (mmctx->ms_radio_access_capa.len) + msgb_tvlv_push(msg, BSSGP_IE_MS_RADIO_ACCESS_CAP, + mmctx->ms_radio_access_capa.len, + mmctx->ms_radio_access_capa.buf); + } + + /* prepend the pdu lifetime */ + pdu_lifetime = htons(pdu_lifetime); + msgb_tvlv_push(msg, BSSGP_IE_PDU_LIFETIME, 2, (uint8_t *)&pdu_lifetime); + + /* prepend the QoS profile, TLLI and pdu type */ + budh = (struct bssgp_ud_hdr *) msgb_push(msg, sizeof(*budh)); + memcpy(budh->qos_profile, qos_profile_default, sizeof(qos_profile_default)); + budh->tlli = htonl(msgb_tlli(msg)); + budh->pdu_type = BSSGP_PDUT_DL_UNITDATA; + + rate_ctr_inc(&bctx->ctrg->ctr[BSSGP_CTR_PKTS_OUT]); + rate_ctr_add(&bctx->ctrg->ctr[BSSGP_CTR_BYTES_OUT], msg->len); + + /* Identifiers down: BVCI, NSEI (in msgb->cb) */ + + return gprs_ns_sendmsg(bssgp_nsi, msg); +} + +/* Send a single GMM-PAGING.req to a given NSEI/NS-BVCI */ +int gprs_bssgp_tx_paging(uint16_t nsei, uint16_t ns_bvci, + struct bssgp_paging_info *pinfo) +{ + struct msgb *msg = bssgp_msgb_alloc(); + struct bssgp_normal_hdr *bgph = + (struct bssgp_normal_hdr *) msgb_put(msg, sizeof(*bgph)); + uint16_t drx_params = htons(pinfo->drx_params); + uint8_t mi[10]; + int imsi_len = gsm48_generate_mid_from_imsi(mi, pinfo->imsi); + uint8_t ra[6]; + + if (imsi_len < 2) + return -EINVAL; + + msgb_nsei(msg) = nsei; + msgb_bvci(msg) = ns_bvci; + + if (pinfo->mode == BSSGP_PAGING_PS) + bgph->pdu_type = BSSGP_PDUT_PAGING_PS; + else + bgph->pdu_type = BSSGP_PDUT_PAGING_CS; + /* IMSI */ + msgb_tvlv_put(msg, BSSGP_IE_IMSI, imsi_len-2, mi+2); + /* DRX Parameters */ + msgb_tvlv_put(msg, BSSGP_IE_DRX_PARAMS, 2, + (uint8_t *) &drx_params); + /* Scope */ + switch (pinfo->scope) { + case BSSGP_PAGING_BSS_AREA: + { + uint8_t null = 0; + msgb_tvlv_put(msg, BSSGP_IE_BSS_AREA_ID, 1, &null); + } + break; + case BSSGP_PAGING_LOCATION_AREA: + gsm48_construct_ra(ra, &pinfo->raid); + msgb_tvlv_put(msg, BSSGP_IE_LOCATION_AREA, 4, ra); + break; + case BSSGP_PAGING_ROUTEING_AREA: + gsm48_construct_ra(ra, &pinfo->raid); + msgb_tvlv_put(msg, BSSGP_IE_ROUTEING_AREA, 6, ra); + break; + case BSSGP_PAGING_BVCI: + { + uint16_t bvci = htons(pinfo->bvci); + msgb_tvlv_put(msg, BSSGP_IE_BVCI, 2, (uint8_t *)&bvci); + } + break; + } + /* QoS profile mandatory for PS */ + if (pinfo->mode == BSSGP_PAGING_PS) + msgb_tvlv_put(msg, BSSGP_IE_QOS_PROFILE, 3, pinfo->qos); + + /* Optional (P-)TMSI */ + if (pinfo->ptmsi) { + uint32_t ptmsi = htonl(*pinfo->ptmsi); + msgb_tvlv_put(msg, BSSGP_IE_TMSI, 4, (uint8_t *) &ptmsi); + } + + return gprs_ns_sendmsg(bssgp_nsi, msg); +} diff --git a/openbsc/src/libgb/gprs_bssgp_util.c b/openbsc/src/libgb/gprs_bssgp_util.c new file mode 100644 index 000000000..f8e3b5699 --- /dev/null +++ b/openbsc/src/libgb/gprs_bssgp_util.c @@ -0,0 +1,119 @@ +/* GPRS BSSGP protocol implementation as per 3GPP TS 08.18 */ + +/* (C) 2009-2010 by Harald Welte + * + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + * + */ + +#include +#include + +#include + +#include +#include +#include + +#include +#include +#include +#include + +struct gprs_ns_inst *bssgp_nsi; + +/* BSSGP Protocol specific, not implementation specific */ +/* FIXME: This needs to go into libosmocore after finished */ + +/* Chapter 11.3.9 / Table 11.10: Cause coding */ +static const struct value_string bssgp_cause_strings[] = { + { BSSGP_CAUSE_PROC_OVERLOAD, "Processor overload" }, + { BSSGP_CAUSE_EQUIP_FAIL, "Equipment Failure" }, + { BSSGP_CAUSE_TRASIT_NET_FAIL, "Transit netowkr service failure" }, + { BSSGP_CAUSE_CAPA_GREATER_0KPBS,"Transmission capacity modified" }, + { BSSGP_CAUSE_UNKNOWN_MS, "Unknown MS" }, + { BSSGP_CAUSE_UNKNOWN_BVCI, "Unknown BVCI" }, + { BSSGP_CAUSE_CELL_TRAF_CONG, "Cell traffic congestion" }, + { BSSGP_CAUSE_SGSN_CONG, "SGSN congestion" }, + { BSSGP_CAUSE_OML_INTERV, "O&M intervention" }, + { BSSGP_CAUSE_BVCI_BLOCKED, "BVCI blocked" }, + { BSSGP_CAUSE_PFC_CREATE_FAIL, "PFC create failure" }, + { BSSGP_CAUSE_SEM_INCORR_PDU, "Semantically incorrect PDU" }, + { BSSGP_CAUSE_INV_MAND_INF, "Invalid mandatory information" }, + { BSSGP_CAUSE_MISSING_MAND_IE, "Missing mandatory IE" }, + { BSSGP_CAUSE_MISSING_COND_IE, "Missing conditional IE" }, + { BSSGP_CAUSE_UNEXP_COND_IE, "Unexpected conditional IE" }, + { BSSGP_CAUSE_COND_IE_ERR, "Conditional IE error" }, + { BSSGP_CAUSE_PDU_INCOMP_STATE, "PDU incompatible with protocol state" }, + { BSSGP_CAUSE_PROTO_ERR_UNSPEC, "Protocol error - unspecified" }, + { BSSGP_CAUSE_PDU_INCOMP_FEAT, "PDU not compatible with feature set" }, + { 0, NULL }, +}; + +const char *bssgp_cause_str(enum gprs_bssgp_cause cause) +{ + return get_value_string(bssgp_cause_strings, cause); +} + + +struct msgb *bssgp_msgb_alloc(void) +{ + return msgb_alloc_headroom(4096, 128, "BSSGP"); +} + +/* Transmit a simple response such as BLOCK/UNBLOCK/RESET ACK/NACK */ +int bssgp_tx_simple_bvci(uint8_t pdu_type, uint16_t nsei, + uint16_t bvci, uint16_t ns_bvci) +{ + struct msgb *msg = bssgp_msgb_alloc(); + struct bssgp_normal_hdr *bgph = + (struct bssgp_normal_hdr *) msgb_put(msg, sizeof(*bgph)); + uint16_t _bvci; + + msgb_nsei(msg) = nsei; + msgb_bvci(msg) = ns_bvci; + + bgph->pdu_type = pdu_type; + _bvci = htons(bvci); + msgb_tvlv_put(msg, BSSGP_IE_BVCI, 2, (uint8_t *) &_bvci); + + return gprs_ns_sendmsg(bssgp_nsi, msg); +} + +/* Chapter 10.4.14: Status */ +int bssgp_tx_status(uint8_t cause, uint16_t *bvci, struct msgb *orig_msg) +{ + struct msgb *msg = bssgp_msgb_alloc(); + struct bssgp_normal_hdr *bgph = + (struct bssgp_normal_hdr *) msgb_put(msg, sizeof(*bgph)); + + LOGP(DBSSGP, LOGL_NOTICE, "BSSGP BVCI=%u Tx STATUS, cause=%s\n", + bvci ? *bvci : 0, bssgp_cause_str(cause)); + msgb_nsei(msg) = msgb_nsei(orig_msg); + msgb_bvci(msg) = 0; + + bgph->pdu_type = BSSGP_PDUT_STATUS; + msgb_tvlv_put(msg, BSSGP_IE_CAUSE, 1, &cause); + if (bvci) { + uint16_t _bvci = htons(*bvci); + msgb_tvlv_put(msg, BSSGP_IE_BVCI, 2, (uint8_t *) &_bvci); + } + if (orig_msg) + msgb_tvlv_put(msg, BSSGP_IE_PDU_IN_ERROR, + msgb_bssgp_len(orig_msg), msgb_bssgph(orig_msg)); + + return gprs_ns_sendmsg(bssgp_nsi, msg); +} diff --git a/openbsc/src/libgb/gprs_bssgp_vty.c b/openbsc/src/libgb/gprs_bssgp_vty.c new file mode 100644 index 000000000..9ebd09004 --- /dev/null +++ b/openbsc/src/libgb/gprs_bssgp_vty.c @@ -0,0 +1,176 @@ +/* VTY interface for our GPRS BSS Gateway Protocol (BSSGP) implementation */ + +/* (C) 2010 by Harald Welte + * + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + * + */ + +#include +#include +#include +#include + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +/* FIXME: this should go to some common file as it is copied + * in vty_interface.c of the BSC */ +static const struct value_string gprs_bssgp_timer_strs[] = { + { 0, NULL } +}; + +static struct cmd_node bssgp_node = { + BSSGP_NODE, + "%s(bssgp)#", + 1, +}; + +static int config_write_bssgp(struct vty *vty) +{ + vty_out(vty, "bssgp%s", VTY_NEWLINE); + + return CMD_SUCCESS; +} + +DEFUN(cfg_bssgp, cfg_bssgp_cmd, + "bssgp", + "Configure the GPRS BSS Gateway Protocol") +{ + vty->node = BSSGP_NODE; + return CMD_SUCCESS; +} + +static void dump_bvc(struct vty *vty, struct bssgp_bvc_ctx *bvc, int stats) +{ + vty_out(vty, "NSEI %5u, BVCI %5u, RA-ID: %u-%u-%u-%u, CID: %u, " + "STATE: %s%s", bvc->nsei, bvc->bvci, bvc->ra_id.mcc, + bvc->ra_id.mnc, bvc->ra_id.lac, bvc->ra_id.rac, bvc->cell_id, + bvc->state & BVC_S_BLOCKED ? "BLOCKED" : "UNBLOCKED", + VTY_NEWLINE); + if (stats) + vty_out_rate_ctr_group(vty, " ", bvc->ctrg); +} + +static void dump_bssgp(struct vty *vty, int stats) +{ + struct bssgp_bvc_ctx *bvc; + + llist_for_each_entry(bvc, &bssgp_bvc_ctxts, list) { + dump_bvc(vty, bvc, stats); + } +} + +#define BSSGP_STR "Show information about the BSSGP protocol\n" + +DEFUN(show_bssgp, show_bssgp_cmd, "show bssgp", + SHOW_STR BSSGP_STR) +{ + dump_bssgp(vty, 0); + return CMD_SUCCESS; +} + +DEFUN(show_bssgp_stats, show_bssgp_stats_cmd, "show bssgp stats", + SHOW_STR BSSGP_STR + "Include statistics\n") +{ + dump_bssgp(vty, 1); + return CMD_SUCCESS; +} + +DEFUN(show_bvc, show_bvc_cmd, "show bssgp nsei <0-65535> [stats]", + SHOW_STR BSSGP_STR + "Show all BVCs on one NSE\n" + "The NSEI\n" "Include Statistics\n") +{ + struct bssgp_bvc_ctx *bvc; + uint16_t nsei = atoi(argv[1]); + int show_stats = 0; + + if (argc >= 2) + show_stats = 1; + + llist_for_each_entry(bvc, &bssgp_bvc_ctxts, list) { + if (bvc->nsei != nsei) + continue; + dump_bvc(vty, bvc, show_stats); + } + + return CMD_SUCCESS; +} + +DEFUN(logging_fltr_bvc, + logging_fltr_bvc_cmd, + "logging filter bvc nsei <0-65535> bvci <0-65535>", + LOGGING_STR FILTER_STR + "Filter based on BSSGP Virtual Connection\n" + "NSEI of the BVC to be filtered\n" + "Network Service Entity Identifier (NSEI)\n" + "BVCI of the BVC to be filtered\n" + "BSSGP Virtual Connection Identifier (BVCI)\n") +{ + struct log_target *tgt = osmo_log_vty2tgt(vty); + struct bssgp_bvc_ctx *bvc; + uint16_t nsei = atoi(argv[0]); + uint16_t bvci = atoi(argv[1]); + + if (!tgt) + return CMD_WARNING; + + bvc = btsctx_by_bvci_nsei(bvci, nsei); + if (!bvc) { + vty_out(vty, "No BVC by that identifier%s", VTY_NEWLINE); + return CMD_WARNING; + } + + log_set_bvc_filter(tgt, bvc); + return CMD_SUCCESS; +} + +int gprs_bssgp_vty_init(void) +{ + install_element_ve(&show_bssgp_cmd); + install_element_ve(&show_bssgp_stats_cmd); + install_element_ve(&show_bvc_cmd); + install_element_ve(&logging_fltr_bvc_cmd); + + install_element(CFG_LOG_NODE, &logging_fltr_bvc_cmd); + + install_element(CONFIG_NODE, &cfg_bssgp_cmd); + install_node(&bssgp_node, config_write_bssgp); + install_default(BSSGP_NODE); + install_element(BSSGP_NODE, &ournode_exit_cmd); + install_element(BSSGP_NODE, &ournode_end_cmd); + //install_element(BSSGP_NODE, &cfg_bssgp_timer_cmd); + + return 0; +} diff --git a/openbsc/src/libgb/gprs_ns.c b/openbsc/src/libgb/gprs_ns.c new file mode 100644 index 000000000..5a8e35860 --- /dev/null +++ b/openbsc/src/libgb/gprs_ns.c @@ -0,0 +1,992 @@ +/* GPRS Networks Service (NS) messages on the Gb interfacebvci = msgb_bvci(msg); + * 3GPP TS 08.16 version 8.0.1 Release 1999 / ETSI TS 101 299 V8.0.1 (2002-05) */ + +/* (C) 2009-2010 by Harald Welte + * + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + * + */ + +/* Some introduction into NS: NS is used typically on top of frame relay, + * but in the ip.access world it is encapsulated in UDP packets. It serves + * as an intermediate shim betwen BSSGP and the underlying medium. It doesn't + * do much, apart from providing congestion notification and status indication. + * + * Terms: + * NS Network Service + * NSVC NS Virtual Connection + * NSEI NS Entity Identifier + * NSVL NS Virtual Link + * NSVLI NS Virtual Link Identifier + * BVC BSSGP Virtual Connection + * BVCI BSSGP Virtual Connection Identifier + * NSVCG NS Virtual Connection Goup + * Blocked NS-VC cannot be used for user traffic + * Alive Ability of a NS-VC to provide communication + * + * There can be multiple BSSGP virtual connections over one (group of) NSVC's. BSSGP will + * therefore identify the BSSGP virtual connection by a BVCI passed down to NS. + * NS then has to firgure out which NSVC's are responsible for this BVCI. + * Those mappings are administratively configured. + */ + +/* This implementation has the following limitations: + * o Only one NS-VC for each NSE: No load-sharing function + * o NSVCI 65535 and 65534 are reserved for internal use + * o Only UDP is supported as of now, no frame relay support + * o The IP Sub-Network-Service (SNS) as specified in 48.016 is not implemented + * o There are no BLOCK and UNBLOCK timers (yet?) + */ + +#include +#include +#include +#include + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static const struct tlv_definition ns_att_tlvdef = { + .def = { + [NS_IE_CAUSE] = { TLV_TYPE_TvLV, 0 }, + [NS_IE_VCI] = { TLV_TYPE_TvLV, 0 }, + [NS_IE_PDU] = { TLV_TYPE_TvLV, 0 }, + [NS_IE_BVCI] = { TLV_TYPE_TvLV, 0 }, + [NS_IE_NSEI] = { TLV_TYPE_TvLV, 0 }, + }, +}; + +enum ns_ctr { + NS_CTR_PKTS_IN, + NS_CTR_PKTS_OUT, + NS_CTR_BYTES_IN, + NS_CTR_BYTES_OUT, + NS_CTR_BLOCKED, + NS_CTR_DEAD, +}; + +static const struct rate_ctr_desc nsvc_ctr_description[] = { + { "packets.in", "Packets at NS Level ( In)" }, + { "packets.out","Packets at NS Level (Out)" }, + { "bytes.in", "Bytes at NS Level ( In)" }, + { "bytes.out", "Bytes at NS Level (Out)" }, + { "blocked", "NS-VC Block count " }, + { "dead", "NS-VC gone dead count " }, +}; + +static const struct rate_ctr_group_desc nsvc_ctrg_desc = { + .group_name_prefix = "ns.nsvc", + .group_description = "NSVC Peer Statistics", + .num_ctr = ARRAY_SIZE(nsvc_ctr_description), + .ctr_desc = nsvc_ctr_description, +}; + +/* Lookup struct gprs_nsvc based on NSVCI */ +struct gprs_nsvc *nsvc_by_nsvci(struct gprs_ns_inst *nsi, uint16_t nsvci) +{ + struct gprs_nsvc *nsvc; + llist_for_each_entry(nsvc, &nsi->gprs_nsvcs, list) { + if (nsvc->nsvci == nsvci) + return nsvc; + } + return NULL; +} + +/* Lookup struct gprs_nsvc based on NSVCI */ +struct gprs_nsvc *nsvc_by_nsei(struct gprs_ns_inst *nsi, uint16_t nsei) +{ + struct gprs_nsvc *nsvc; + llist_for_each_entry(nsvc, &nsi->gprs_nsvcs, list) { + if (nsvc->nsei == nsei) + return nsvc; + } + return NULL; +} + +/* Lookup struct gprs_nsvc based on remote peer socket addr */ +static struct gprs_nsvc *nsvc_by_rem_addr(struct gprs_ns_inst *nsi, + struct sockaddr_in *sin) +{ + struct gprs_nsvc *nsvc; + llist_for_each_entry(nsvc, &nsi->gprs_nsvcs, list) { + if (nsvc->ip.bts_addr.sin_addr.s_addr == + sin->sin_addr.s_addr && + nsvc->ip.bts_addr.sin_port == sin->sin_port) + return nsvc; + } + return NULL; +} + +static void gprs_ns_timer_cb(void *data); + +struct gprs_nsvc *nsvc_create(struct gprs_ns_inst *nsi, uint16_t nsvci) +{ + struct gprs_nsvc *nsvc; + + LOGP(DNS, LOGL_INFO, "NSVCI=%u Creating NS-VC\n", nsvci); + + nsvc = talloc_zero(nsi, struct gprs_nsvc); + nsvc->nsvci = nsvci; + /* before RESET procedure: BLOCKED and DEAD */ + nsvc->state = NSE_S_BLOCKED; + nsvc->nsi = nsi; + nsvc->timer.cb = gprs_ns_timer_cb; + nsvc->timer.data = nsvc; + nsvc->ctrg = rate_ctr_group_alloc(nsvc, &nsvc_ctrg_desc, nsvci); + + llist_add(&nsvc->list, &nsi->gprs_nsvcs); + + return nsvc; +} + +void nsvc_delete(struct gprs_nsvc *nsvc) +{ + if (bsc_timer_pending(&nsvc->timer)) + bsc_del_timer(&nsvc->timer); + llist_del(&nsvc->list); + talloc_free(nsvc); +} + +static void ns_dispatch_signal(struct gprs_nsvc *nsvc, unsigned int signal, + uint8_t cause) +{ + struct ns_signal_data nssd; + + nssd.nsvc = nsvc; + nssd.cause = cause; + + dispatch_signal(SS_NS, signal, &nssd); +} + +/* Section 10.3.2, Table 13 */ +static const struct value_string ns_cause_str[] = { + { NS_CAUSE_TRANSIT_FAIL, "Transit network failure" }, + { NS_CAUSE_OM_INTERVENTION, "O&M intervention" }, + { NS_CAUSE_EQUIP_FAIL, "Equipment failure" }, + { NS_CAUSE_NSVC_BLOCKED, "NS-VC blocked" }, + { NS_CAUSE_NSVC_UNKNOWN, "NS-VC unknown" }, + { NS_CAUSE_BVCI_UNKNOWN, "BVCI unknown" }, + { NS_CAUSE_SEM_INCORR_PDU, "Semantically incorrect PDU" }, + { NS_CAUSE_PDU_INCOMP_PSTATE, "PDU not compatible with protocol state" }, + { NS_CAUSE_PROTO_ERR_UNSPEC, "Protocol error, unspecified" }, + { NS_CAUSE_INVAL_ESSENT_IE, "Invalid essential IE" }, + { NS_CAUSE_MISSING_ESSENT_IE, "Missing essential IE" }, + { 0, NULL } +}; + +const char *gprs_ns_cause_str(enum ns_cause cause) +{ + return get_value_string(ns_cause_str, cause); +} + +static int nsip_sendmsg(struct gprs_nsvc *nsvc, struct msgb *msg); +extern int grps_ns_frgre_sendmsg(struct gprs_nsvc *nsvc, struct msgb *msg); + +static int gprs_ns_tx(struct gprs_nsvc *nsvc, struct msgb *msg) +{ + int ret; + + log_set_context(BSC_CTX_NSVC, nsvc); + + /* Increment number of Uplink bytes */ + rate_ctr_inc(&nsvc->ctrg->ctr[NS_CTR_PKTS_OUT]); + rate_ctr_add(&nsvc->ctrg->ctr[NS_CTR_BYTES_OUT], msgb_l2len(msg)); + + switch (nsvc->ll) { + case GPRS_NS_LL_UDP: + ret = nsip_sendmsg(nsvc, msg); + break; + case GPRS_NS_LL_FR_GRE: + ret = gprs_ns_frgre_sendmsg(nsvc, msg); + break; + default: + LOGP(DNS, LOGL_ERROR, "unsupported NS linklayer %u\n", nsvc->ll); + msgb_free(msg); + ret = -EIO; + break; + } + return ret; +} + +static int gprs_ns_tx_simple(struct gprs_nsvc *nsvc, uint8_t pdu_type) +{ + struct msgb *msg = gprs_ns_msgb_alloc(); + struct gprs_ns_hdr *nsh; + + log_set_context(BSC_CTX_NSVC, nsvc); + + if (!msg) + return -ENOMEM; + + msg->l2h = msgb_put(msg, sizeof(*nsh)); + nsh = (struct gprs_ns_hdr *) msg->l2h; + + nsh->pdu_type = pdu_type; + + return gprs_ns_tx(nsvc, msg); +} + +int gprs_ns_tx_reset(struct gprs_nsvc *nsvc, uint8_t cause) +{ + struct msgb *msg = gprs_ns_msgb_alloc(); + struct gprs_ns_hdr *nsh; + uint16_t nsvci = htons(nsvc->nsvci); + uint16_t nsei = htons(nsvc->nsei); + + log_set_context(BSC_CTX_NSVC, nsvc); + + if (!msg) + return -ENOMEM; + + LOGP(DNS, LOGL_INFO, "NSEI=%u Tx NS RESET (NSVCI=%u, cause=%s)\n", + nsvc->nsei, nsvc->nsvci, gprs_ns_cause_str(cause)); + + msg->l2h = msgb_put(msg, sizeof(*nsh)); + nsh = (struct gprs_ns_hdr *) msg->l2h; + nsh->pdu_type = NS_PDUT_RESET; + + msgb_tvlv_put(msg, NS_IE_CAUSE, 1, &cause); + msgb_tvlv_put(msg, NS_IE_VCI, 2, (uint8_t *) &nsvci); + msgb_tvlv_put(msg, NS_IE_NSEI, 2, (uint8_t *) &nsei); + + return gprs_ns_tx(nsvc, msg); + +} + +int gprs_ns_tx_status(struct gprs_nsvc *nsvc, uint8_t cause, + uint16_t bvci, struct msgb *orig_msg) +{ + struct msgb *msg = gprs_ns_msgb_alloc(); + struct gprs_ns_hdr *nsh; + uint16_t nsvci = htons(nsvc->nsvci); + + log_set_context(BSC_CTX_NSVC, nsvc); + + bvci = htons(bvci); + + if (!msg) + return -ENOMEM; + + LOGP(DNS, LOGL_NOTICE, "NSEI=%u Tx NS STATUS (NSVCI=%u, cause=%s)\n", + nsvc->nsei, nsvc->nsvci, gprs_ns_cause_str(cause)); + + msg->l2h = msgb_put(msg, sizeof(*nsh)); + nsh = (struct gprs_ns_hdr *) msg->l2h; + nsh->pdu_type = NS_PDUT_STATUS; + + msgb_tvlv_put(msg, NS_IE_CAUSE, 1, &cause); + + /* Section 9.2.7.1: Static conditions for NS-VCI */ + if (cause == NS_CAUSE_NSVC_BLOCKED || + cause == NS_CAUSE_NSVC_UNKNOWN) + msgb_tvlv_put(msg, NS_IE_VCI, 2, (uint8_t *)&nsvci); + + /* Section 9.2.7.2: Static conditions for NS PDU */ + switch (cause) { + case NS_CAUSE_SEM_INCORR_PDU: + case NS_CAUSE_PDU_INCOMP_PSTATE: + case NS_CAUSE_PROTO_ERR_UNSPEC: + case NS_CAUSE_INVAL_ESSENT_IE: + case NS_CAUSE_MISSING_ESSENT_IE: + msgb_tvlv_put(msg, NS_IE_PDU, msgb_l2len(orig_msg), + orig_msg->l2h); + break; + default: + break; + } + + /* Section 9.2.7.3: Static conditions for BVCI */ + if (cause == NS_CAUSE_BVCI_UNKNOWN) + msgb_tvlv_put(msg, NS_IE_VCI, 2, (uint8_t *)&bvci); + + return gprs_ns_tx(nsvc, msg); +} + +int gprs_ns_tx_block(struct gprs_nsvc *nsvc, uint8_t cause) +{ + struct msgb *msg = gprs_ns_msgb_alloc(); + struct gprs_ns_hdr *nsh; + uint16_t nsvci = htons(nsvc->nsvci); + + log_set_context(BSC_CTX_NSVC, nsvc); + + if (!msg) + return -ENOMEM; + + LOGP(DNS, LOGL_INFO, "NSEI=%u Tx NS BLOCK (NSVCI=%u, cause=%s)\n", + nsvc->nsei, nsvc->nsvci, gprs_ns_cause_str(cause)); + + /* be conservative and mark it as blocked even now! */ + nsvc->state |= NSE_S_BLOCKED; + rate_ctr_inc(&nsvc->ctrg->ctr[NS_CTR_BLOCKED]); + + msg->l2h = msgb_put(msg, sizeof(*nsh)); + nsh = (struct gprs_ns_hdr *) msg->l2h; + nsh->pdu_type = NS_PDUT_BLOCK; + + msgb_tvlv_put(msg, NS_IE_CAUSE, 1, &cause); + msgb_tvlv_put(msg, NS_IE_VCI, 2, (uint8_t *) &nsvci); + + return gprs_ns_tx(nsvc, msg); +} + +int gprs_ns_tx_unblock(struct gprs_nsvc *nsvc) +{ + log_set_context(BSC_CTX_NSVC, nsvc); + LOGP(DNS, LOGL_INFO, "NSEI=%u Tx NS UNBLOCK (NSVCI=%u)\n", + nsvc->nsei, nsvc->nsvci); + + return gprs_ns_tx_simple(nsvc, NS_PDUT_UNBLOCK); +} + +int gprs_ns_tx_alive(struct gprs_nsvc *nsvc) +{ + log_set_context(BSC_CTX_NSVC, nsvc); + LOGP(DNS, LOGL_DEBUG, "NSEI=%u Tx NS ALIVE (NSVCI=%u)\n", + nsvc->nsei, nsvc->nsvci); + + return gprs_ns_tx_simple(nsvc, NS_PDUT_ALIVE); +} + +int gprs_ns_tx_alive_ack(struct gprs_nsvc *nsvc) +{ + log_set_context(BSC_CTX_NSVC, nsvc); + LOGP(DNS, LOGL_DEBUG, "NSEI=%u Tx NS ALIVE_ACK (NSVCI=%u)\n", + nsvc->nsei, nsvc->nsvci); + + return gprs_ns_tx_simple(nsvc, NS_PDUT_ALIVE_ACK); +} + +static const enum ns_timeout timer_mode_tout[_NSVC_TIMER_NR] = { + [NSVC_TIMER_TNS_RESET] = NS_TOUT_TNS_RESET, + [NSVC_TIMER_TNS_ALIVE] = NS_TOUT_TNS_ALIVE, + [NSVC_TIMER_TNS_TEST] = NS_TOUT_TNS_TEST, +}; + +static const struct value_string timer_mode_strs[] = { + { NSVC_TIMER_TNS_RESET, "tns-reset" }, + { NSVC_TIMER_TNS_ALIVE, "tns-alive" }, + { NSVC_TIMER_TNS_TEST, "tns-test" }, + { 0, NULL } +}; + +static void nsvc_start_timer(struct gprs_nsvc *nsvc, enum nsvc_timer_mode mode) +{ + enum ns_timeout tout = timer_mode_tout[mode]; + unsigned int seconds = nsvc->nsi->timeout[tout]; + + log_set_context(BSC_CTX_NSVC, nsvc); + DEBUGP(DNS, "NSEI=%u Starting timer in mode %s (%u seconds)\n", + nsvc->nsei, get_value_string(timer_mode_strs, mode), + seconds); + + if (bsc_timer_pending(&nsvc->timer)) + bsc_del_timer(&nsvc->timer); + + nsvc->timer_mode = mode; + bsc_schedule_timer(&nsvc->timer, seconds, 0); +} + +static void gprs_ns_timer_cb(void *data) +{ + struct gprs_nsvc *nsvc = data; + enum ns_timeout tout = timer_mode_tout[nsvc->timer_mode]; + unsigned int seconds = nsvc->nsi->timeout[tout]; + + log_set_context(BSC_CTX_NSVC, nsvc); + DEBUGP(DNS, "NSEI=%u Timer expired in mode %s (%u seconds)\n", + nsvc->nsei, get_value_string(timer_mode_strs, nsvc->timer_mode), + seconds); + + switch (nsvc->timer_mode) { + case NSVC_TIMER_TNS_ALIVE: + /* Tns-alive case: we expired without response ! */ + nsvc->alive_retries++; + if (nsvc->alive_retries > + nsvc->nsi->timeout[NS_TOUT_TNS_ALIVE_RETRIES]) { + /* mark as dead and blocked */ + nsvc->state = NSE_S_BLOCKED; + rate_ctr_inc(&nsvc->ctrg->ctr[NS_CTR_BLOCKED]); + rate_ctr_inc(&nsvc->ctrg->ctr[NS_CTR_DEAD]); + LOGP(DNS, LOGL_NOTICE, + "NSEI=%u Tns-alive expired more then " + "%u times, blocking NS-VC\n", nsvc->nsei, + nsvc->nsi->timeout[NS_TOUT_TNS_ALIVE_RETRIES]); + ns_dispatch_signal(nsvc, S_NS_ALIVE_EXP, 0); + ns_dispatch_signal(nsvc, S_NS_BLOCK, NS_CAUSE_NSVC_BLOCKED); + return; + } + /* Tns-test case: send NS-ALIVE PDU */ + gprs_ns_tx_alive(nsvc); + /* start Tns-alive timer */ + nsvc_start_timer(nsvc, NSVC_TIMER_TNS_ALIVE); + break; + case NSVC_TIMER_TNS_TEST: + /* Tns-test case: send NS-ALIVE PDU */ + gprs_ns_tx_alive(nsvc); + /* start Tns-alive timer (transition into faster + * alive retransmissions) */ + nsvc->alive_retries = 0; + nsvc_start_timer(nsvc, NSVC_TIMER_TNS_ALIVE); + break; + case NSVC_TIMER_TNS_RESET: + /* Chapter 7.3: Re-send the RESET */ + gprs_ns_tx_reset(nsvc, NS_CAUSE_OM_INTERVENTION); + /* Re-start Tns-reset timer */ + nsvc_start_timer(nsvc, NSVC_TIMER_TNS_RESET); + break; + case _NSVC_TIMER_NR: + break; + } +} + +/* Section 9.2.6 */ +static int gprs_ns_tx_reset_ack(struct gprs_nsvc *nsvc) +{ + struct msgb *msg = gprs_ns_msgb_alloc(); + struct gprs_ns_hdr *nsh; + uint16_t nsvci, nsei; + + log_set_context(BSC_CTX_NSVC, nsvc); + if (!msg) + return -ENOMEM; + + nsvci = htons(nsvc->nsvci); + nsei = htons(nsvc->nsei); + + msg->l2h = msgb_put(msg, sizeof(*nsh)); + nsh = (struct gprs_ns_hdr *) msg->l2h; + + nsh->pdu_type = NS_PDUT_RESET_ACK; + + LOGP(DNS, LOGL_INFO, "NSEI=%u Tx NS RESET ACK (NSVCI=%u)\n", + nsvc->nsei, nsvc->nsvci); + + msgb_tvlv_put(msg, NS_IE_VCI, 2, (uint8_t *)&nsvci); + msgb_tvlv_put(msg, NS_IE_NSEI, 2, (uint8_t *)&nsei); + + return gprs_ns_tx(nsvc, msg); +} + +/* Section 9.2.10: transmit side / NS-UNITDATA-REQUEST primitive */ +int gprs_ns_sendmsg(struct gprs_ns_inst *nsi, struct msgb *msg) +{ + struct gprs_nsvc *nsvc; + struct gprs_ns_hdr *nsh; + uint16_t bvci = msgb_bvci(msg); + + nsvc = nsvc_by_nsei(nsi, msgb_nsei(msg)); + if (!nsvc) { + LOGP(DNS, LOGL_ERROR, "Unable to resolve NSEI %u " + "to NS-VC!\n", msgb_nsei(msg)); + msgb_free(msg); + return -EINVAL; + } + log_set_context(BSC_CTX_NSVC, nsvc); + + if (!(nsvc->state & NSE_S_ALIVE)) { + LOGP(DNS, LOGL_ERROR, "NSEI=%u is not alive, cannot send\n", + nsvc->nsei); + msgb_free(msg); + return -EBUSY; + } + if (nsvc->state & NSE_S_BLOCKED) { + LOGP(DNS, LOGL_ERROR, "NSEI=%u is blocked, cannot send\n", + nsvc->nsei); + msgb_free(msg); + return -EBUSY; + } + + msg->l2h = msgb_push(msg, sizeof(*nsh) + 3); + nsh = (struct gprs_ns_hdr *) msg->l2h; + if (!nsh) { + LOGP(DNS, LOGL_ERROR, "Not enough headroom for NS header\n"); + msgb_free(msg); + return -EIO; + } + + nsh->pdu_type = NS_PDUT_UNITDATA; + /* spare octet in data[0] */ + nsh->data[1] = bvci >> 8; + nsh->data[2] = bvci & 0xff; + + return gprs_ns_tx(nsvc, msg); +} + +/* Section 9.2.10: receive side */ +static int gprs_ns_rx_unitdata(struct gprs_nsvc *nsvc, struct msgb *msg) +{ + struct gprs_ns_hdr *nsh = (struct gprs_ns_hdr *)msg->l2h; + uint16_t bvci; + + if (nsvc->state & NSE_S_BLOCKED) + return gprs_ns_tx_status(nsvc, NS_CAUSE_NSVC_BLOCKED, + 0, msg); + + /* spare octet in data[0] */ + bvci = nsh->data[1] << 8 | nsh->data[2]; + msgb_bssgph(msg) = &nsh->data[3]; + msgb_bvci(msg) = bvci; + + /* call upper layer (BSSGP) */ + return nsvc->nsi->cb(GPRS_NS_EVT_UNIT_DATA, nsvc, msg, bvci); +} + +/* Section 9.2.7 */ +static int gprs_ns_rx_status(struct gprs_nsvc *nsvc, struct msgb *msg) +{ + struct gprs_ns_hdr *nsh = (struct gprs_ns_hdr *) msg->l2h; + struct tlv_parsed tp; + uint8_t cause; + int rc; + + LOGP(DNS, LOGL_NOTICE, "NSEI=%u Rx NS STATUS ", nsvc->nsei); + + rc = tlv_parse(&tp, &ns_att_tlvdef, nsh->data, + msgb_l2len(msg) - sizeof(*nsh), 0, 0); + if (rc < 0) { + LOGPC(DNS, LOGL_NOTICE, "Error during TLV Parse\n"); + LOGP(DNS, LOGL_ERROR, "NSEI=%u Rx NS STATUS: " + "Error during TLV Parse\n", nsvc->nsei); + return rc; + } + + if (!TLVP_PRESENT(&tp, NS_IE_CAUSE)) { + LOGPC(DNS, LOGL_INFO, "missing cause IE\n"); + return -EINVAL; + } + + cause = *TLVP_VAL(&tp, NS_IE_CAUSE); + LOGPC(DNS, LOGL_NOTICE, "cause=%s\n", gprs_ns_cause_str(cause)); + + return 0; +} + +/* Section 7.3 */ +static int gprs_ns_rx_reset(struct gprs_nsvc *nsvc, struct msgb *msg) +{ + struct gprs_ns_hdr *nsh = (struct gprs_ns_hdr *) msg->l2h; + struct tlv_parsed tp; + uint8_t *cause; + uint16_t *nsvci, *nsei; + int rc; + + rc = tlv_parse(&tp, &ns_att_tlvdef, nsh->data, + msgb_l2len(msg) - sizeof(*nsh), 0, 0); + if (rc < 0) { + LOGP(DNS, LOGL_ERROR, "NSEI=%u Rx NS RESET " + "Error during TLV Parse\n", nsvc->nsei); + return rc; + } + + if (!TLVP_PRESENT(&tp, NS_IE_CAUSE) || + !TLVP_PRESENT(&tp, NS_IE_VCI) || + !TLVP_PRESENT(&tp, NS_IE_NSEI)) { + LOGP(DNS, LOGL_ERROR, "NS RESET Missing mandatory IE\n"); + gprs_ns_tx_status(nsvc, NS_CAUSE_MISSING_ESSENT_IE, 0, msg); + return -EINVAL; + } + + cause = (uint8_t *) TLVP_VAL(&tp, NS_IE_CAUSE); + nsvci = (uint16_t *) TLVP_VAL(&tp, NS_IE_VCI); + nsei = (uint16_t *) TLVP_VAL(&tp, NS_IE_NSEI); + + LOGP(DNS, LOGL_INFO, "NSEI=%u Rx NS RESET (NSVCI=%u, cause=%s)\n", + nsvc->nsvci, nsvc->nsei, gprs_ns_cause_str(*cause)); + + /* Mark NS-VC as blocked and alive */ + nsvc->state = NSE_S_BLOCKED | NSE_S_ALIVE; + + nsvc->nsei = ntohs(*nsei); + nsvc->nsvci = ntohs(*nsvci); + + /* start the test procedure */ + gprs_ns_tx_simple(nsvc, NS_PDUT_ALIVE); + nsvc_start_timer(nsvc, NSVC_TIMER_TNS_TEST); + + /* inform interested parties about the fact that this NSVC + * has received RESET */ + ns_dispatch_signal(nsvc, S_NS_RESET, *cause); + + return gprs_ns_tx_reset_ack(nsvc); +} + +static int gprs_ns_rx_block(struct gprs_nsvc *nsvc, struct msgb *msg) +{ + struct gprs_ns_hdr *nsh = (struct gprs_ns_hdr *) msg->l2h; + struct tlv_parsed tp; + uint8_t *cause; + int rc; + + LOGP(DNS, LOGL_INFO, "NSEI=%u Rx NS BLOCK\n", nsvc->nsei); + + nsvc->state |= NSE_S_BLOCKED; + + rc = tlv_parse(&tp, &ns_att_tlvdef, nsh->data, + msgb_l2len(msg) - sizeof(*nsh), 0, 0); + if (rc < 0) { + LOGP(DNS, LOGL_ERROR, "NSEI=%u Rx NS BLOCK " + "Error during TLV Parse\n", nsvc->nsei); + return rc; + } + + if (!TLVP_PRESENT(&tp, NS_IE_CAUSE) || + !TLVP_PRESENT(&tp, NS_IE_VCI)) { + LOGP(DNS, LOGL_ERROR, "NS RESET Missing mandatory IE\n"); + gprs_ns_tx_status(nsvc, NS_CAUSE_MISSING_ESSENT_IE, 0, msg); + return -EINVAL; + } + + cause = (uint8_t *) TLVP_VAL(&tp, NS_IE_CAUSE); + //nsvci = (uint16_t *) TLVP_VAL(&tp, NS_IE_VCI); + + ns_dispatch_signal(nsvc, S_NS_BLOCK, *cause); + rate_ctr_inc(&nsvc->ctrg->ctr[NS_CTR_BLOCKED]); + + return gprs_ns_tx_simple(nsvc, NS_PDUT_BLOCK_ACK); +} + +/* main entry point, here incoming NS frames enter */ +int gprs_ns_rcvmsg(struct gprs_ns_inst *nsi, struct msgb *msg, + struct sockaddr_in *saddr, enum gprs_ns_ll ll) +{ + struct gprs_ns_hdr *nsh = (struct gprs_ns_hdr *) msg->l2h; + struct gprs_nsvc *nsvc; + int rc = 0; + + /* look up the NSVC based on source address */ + nsvc = nsvc_by_rem_addr(nsi, saddr); + if (!nsvc) { + struct tlv_parsed tp; + uint16_t nsei; + if (nsh->pdu_type == NS_PDUT_STATUS) { + LOGP(DNS, LOGL_INFO, "Ignoring NS STATUS from %s:%u " + "for non-existing NS-VC\n", + inet_ntoa(saddr->sin_addr), ntohs(saddr->sin_port)); + return 0; + } + /* Only the RESET procedure creates a new NSVC */ + if (nsh->pdu_type != NS_PDUT_RESET) { + /* Since we have no NSVC, we have to use a fake */ + nsvc = nsi->unknown_nsvc; + log_set_context(BSC_CTX_NSVC, nsvc); + LOGP(DNS, LOGL_INFO, "Rejecting NS PDU type 0x%0x " + "from %s:%u for non-existing NS-VC\n", + nsh->pdu_type, inet_ntoa(saddr->sin_addr), + ntohs(saddr->sin_port)); + nsvc->nsvci = nsvc->nsei = 0xfffe; + nsvc->ip.bts_addr = *saddr; + nsvc->state = NSE_S_ALIVE; + nsvc->ll = ll; +#if 0 + return gprs_ns_tx_reset(nsvc, NS_CAUSE_PDU_INCOMP_PSTATE); +#else + return gprs_ns_tx_status(nsvc, + NS_CAUSE_PDU_INCOMP_PSTATE, 0, + msg); +#endif + } + rc = tlv_parse(&tp, &ns_att_tlvdef, nsh->data, + msgb_l2len(msg) - sizeof(*nsh), 0, 0); + if (rc < 0) { + LOGP(DNS, LOGL_ERROR, "Rx NS RESET Error %d during " + "TLV Parse\n", rc); + return rc; + } + if (!TLVP_PRESENT(&tp, NS_IE_CAUSE) || + !TLVP_PRESENT(&tp, NS_IE_VCI) || + !TLVP_PRESENT(&tp, NS_IE_NSEI)) { + LOGP(DNS, LOGL_ERROR, "NS RESET Missing mandatory IE\n"); + gprs_ns_tx_status(nsvc, NS_CAUSE_MISSING_ESSENT_IE, 0, + msg); + return -EINVAL; + } + nsei = ntohs(*(uint16_t *)TLVP_VAL(&tp, NS_IE_NSEI)); + /* Check if we already know this NSEI, the remote end might + * simply have changed addresses, or it is a SGSN */ + nsvc = nsvc_by_nsei(nsi, nsei); + if (!nsvc) { + nsvc = nsvc_create(nsi, 0xffff); + nsvc->ll = ll; + log_set_context(BSC_CTX_NSVC, nsvc); + LOGP(DNS, LOGL_INFO, "Creating NS-VC for BSS at %s:%u\n", + inet_ntoa(saddr->sin_addr), ntohs(saddr->sin_port)); + } + /* Update the remote peer IP address/port */ + nsvc->ip.bts_addr = *saddr; + } else + msgb_nsei(msg) = nsvc->nsei; + + log_set_context(BSC_CTX_NSVC, nsvc); + + /* Increment number of Incoming bytes */ + rate_ctr_inc(&nsvc->ctrg->ctr[NS_CTR_PKTS_IN]); + rate_ctr_add(&nsvc->ctrg->ctr[NS_CTR_BYTES_IN], msgb_l2len(msg)); + + switch (nsh->pdu_type) { + case NS_PDUT_ALIVE: + /* If we're dead and blocked and suddenly receive a + * NS-ALIVE out of the blue, we might have been re-started + * and should send a NS-RESET to make sure everything recovers + * fine. */ + if (nsvc->state == NSE_S_BLOCKED) + rc = gprs_ns_tx_reset(nsvc, NS_CAUSE_PDU_INCOMP_PSTATE); + else + rc = gprs_ns_tx_alive_ack(nsvc); + break; + case NS_PDUT_ALIVE_ACK: + /* stop Tns-alive and start Tns-test */ + nsvc_start_timer(nsvc, NSVC_TIMER_TNS_TEST); + if (nsvc->remote_end_is_sgsn) { + /* FIXME: this should be one level higher */ + if (nsvc->state & NSE_S_BLOCKED) + rc = gprs_ns_tx_unblock(nsvc); + } + break; + case NS_PDUT_UNITDATA: + /* actual user data */ + rc = gprs_ns_rx_unitdata(nsvc, msg); + break; + case NS_PDUT_STATUS: + rc = gprs_ns_rx_status(nsvc, msg); + break; + case NS_PDUT_RESET: + rc = gprs_ns_rx_reset(nsvc, msg); + break; + case NS_PDUT_RESET_ACK: + LOGP(DNS, LOGL_INFO, "NSEI=%u Rx NS RESET ACK\n", nsvc->nsei); + /* mark NS-VC as blocked + active */ + nsvc->state = NSE_S_BLOCKED | NSE_S_ALIVE; + nsvc->remote_state = NSE_S_BLOCKED | NSE_S_ALIVE; + rate_ctr_inc(&nsvc->ctrg->ctr[NS_CTR_BLOCKED]); + if (nsvc->persistent || nsvc->remote_end_is_sgsn) { + /* stop RESET timer */ + bsc_del_timer(&nsvc->timer); + } + /* Initiate TEST proc.: Send ALIVE and start timer */ + rc = gprs_ns_tx_simple(nsvc, NS_PDUT_ALIVE); + nsvc_start_timer(nsvc, NSVC_TIMER_TNS_TEST); + break; + case NS_PDUT_UNBLOCK: + /* Section 7.2: unblocking procedure */ + LOGP(DNS, LOGL_INFO, "NSEI=%u Rx NS UNBLOCK\n", nsvc->nsei); + nsvc->state &= ~NSE_S_BLOCKED; + ns_dispatch_signal(nsvc, S_NS_UNBLOCK, 0); + rc = gprs_ns_tx_simple(nsvc, NS_PDUT_UNBLOCK_ACK); + break; + case NS_PDUT_UNBLOCK_ACK: + LOGP(DNS, LOGL_INFO, "NSEI=%u Rx NS UNBLOCK ACK\n", nsvc->nsei); + /* mark NS-VC as unblocked + active */ + nsvc->state = NSE_S_ALIVE; + nsvc->remote_state = NSE_S_ALIVE; + ns_dispatch_signal(nsvc, S_NS_UNBLOCK, 0); + break; + case NS_PDUT_BLOCK: + rc = gprs_ns_rx_block(nsvc, msg); + break; + case NS_PDUT_BLOCK_ACK: + LOGP(DNS, LOGL_INFO, "NSEI=%u Rx NS BLOCK ACK\n", nsvc->nsei); + /* mark remote NS-VC as blocked + active */ + nsvc->remote_state = NSE_S_BLOCKED | NSE_S_ALIVE; + break; + default: + LOGP(DNS, LOGL_NOTICE, "NSEI=%u Rx Unknown NS PDU type 0x%02x\n", + nsvc->nsei, nsh->pdu_type); + rc = -EINVAL; + break; + } + return rc; +} + +struct gprs_ns_inst *gprs_ns_instantiate(gprs_ns_cb_t *cb) +{ + struct gprs_ns_inst *nsi = talloc_zero(tall_bsc_ctx, struct gprs_ns_inst); + + nsi->cb = cb; + INIT_LLIST_HEAD(&nsi->gprs_nsvcs); + nsi->timeout[NS_TOUT_TNS_BLOCK] = 3; + nsi->timeout[NS_TOUT_TNS_BLOCK_RETRIES] = 3; + nsi->timeout[NS_TOUT_TNS_RESET] = 3; + nsi->timeout[NS_TOUT_TNS_RESET_RETRIES] = 3; + nsi->timeout[NS_TOUT_TNS_TEST] = 30; + nsi->timeout[NS_TOUT_TNS_ALIVE] = 3; + nsi->timeout[NS_TOUT_TNS_ALIVE_RETRIES] = 10; + + /* Create the dummy NSVC that we use for sending + * messages to non-existant/unknown NS-VC's */ + nsi->unknown_nsvc = nsvc_create(nsi, 0xfffe); + llist_del(&nsi->unknown_nsvc->list); + + return nsi; +} + +void gprs_ns_destroy(struct gprs_ns_inst *nsi) +{ + /* FIXME: clear all timers */ + + /* recursively free the NSI and all its NSVCs */ + talloc_free(nsi); +} + + +/* NS-over-IP code, according to 3GPP TS 48.016 Chapter 6.2 + * We don't support Size Procedure, Configuration Procedure, ChangeWeight Procedure */ + +/* Read a single NS-over-IP message */ +static struct msgb *read_nsip_msg(struct bsc_fd *bfd, int *error, + struct sockaddr_in *saddr) +{ + struct msgb *msg = gprs_ns_msgb_alloc(); + int ret = 0; + socklen_t saddr_len = sizeof(*saddr); + + if (!msg) { + *error = -ENOMEM; + return NULL; + } + + ret = recvfrom(bfd->fd, msg->data, NS_ALLOC_SIZE - NS_ALLOC_HEADROOM, 0, + (struct sockaddr *)saddr, &saddr_len); + if (ret < 0) { + LOGP(DNS, LOGL_ERROR, "recv error %s during NSIP recv\n", + strerror(errno)); + msgb_free(msg); + *error = ret; + return NULL; + } else if (ret == 0) { + msgb_free(msg); + *error = ret; + return NULL; + } + + msg->l2h = msg->data; + msgb_put(msg, ret); + + return msg; +} + +static int handle_nsip_read(struct bsc_fd *bfd) +{ + int error; + struct sockaddr_in saddr; + struct gprs_ns_inst *nsi = bfd->data; + struct msgb *msg = read_nsip_msg(bfd, &error, &saddr); + + if (!msg) + return error; + + error = gprs_ns_rcvmsg(nsi, msg, &saddr, GPRS_NS_LL_UDP); + + msgb_free(msg); + + return error; +} + +static int handle_nsip_write(struct bsc_fd *bfd) +{ + /* FIXME: actually send the data here instead of nsip_sendmsg() */ + return -EIO; +} + +static int nsip_sendmsg(struct gprs_nsvc *nsvc, struct msgb *msg) +{ + int rc; + struct gprs_ns_inst *nsi = nsvc->nsi; + struct sockaddr_in *daddr = &nsvc->ip.bts_addr; + + rc = sendto(nsi->nsip.fd.fd, msg->data, msg->len, 0, + (struct sockaddr *)daddr, sizeof(*daddr)); + + talloc_free(msg); + + return rc; +} + +/* UDP Port 23000 carries the LLC-in-BSSGP-in-NS protocol stack */ +static int nsip_fd_cb(struct bsc_fd *bfd, unsigned int what) +{ + int rc = 0; + + if (what & BSC_FD_READ) + rc = handle_nsip_read(bfd); + if (what & BSC_FD_WRITE) + rc = handle_nsip_write(bfd); + + return rc; +} + +/* Listen for incoming GPRS packets */ +int gprs_ns_nsip_listen(struct gprs_ns_inst *nsi) +{ + int ret; + + ret = make_sock(&nsi->nsip.fd, IPPROTO_UDP, nsi->nsip.local_ip, + nsi->nsip.local_port, nsip_fd_cb); + if (ret < 0) + return ret; + + nsi->nsip.fd.data = nsi; + + return ret; +} + +/* Initiate a RESET procedure */ +void gprs_nsvc_reset(struct gprs_nsvc *nsvc, uint8_t cause) +{ + LOGP(DNS, LOGL_INFO, "NSEI=%u RESET procedure based on API request\n", + nsvc->nsei); + + /* Mark NS-VC locally as blocked and dead */ + nsvc->state = NSE_S_BLOCKED; + /* Send NS-RESET PDU */ + if (gprs_ns_tx_reset(nsvc, cause) < 0) { + LOGP(DNS, LOGL_ERROR, "NSEI=%u, error resetting NS-VC\n", + nsvc->nsei); + } + /* Start Tns-reset */ + nsvc_start_timer(nsvc, NSVC_TIMER_TNS_RESET); +} + +/* Establish a connection (from the BSS) to the SGSN */ +struct gprs_nsvc *nsip_connect(struct gprs_ns_inst *nsi, + struct sockaddr_in *dest, uint16_t nsei, + uint16_t nsvci) +{ + struct gprs_nsvc *nsvc; + + nsvc = nsvc_by_rem_addr(nsi, dest); + if (!nsvc) + nsvc = nsvc_create(nsi, nsvci); + nsvc->ip.bts_addr = *dest; + nsvc->nsei = nsei; + nsvc->nsvci = nsvci; + nsvc->remote_end_is_sgsn = 1; + + gprs_nsvc_reset(nsvc, NS_CAUSE_OM_INTERVENTION); + return nsvc; +} diff --git a/openbsc/src/libgb/gprs_ns_frgre.c b/openbsc/src/libgb/gprs_ns_frgre.c new file mode 100644 index 000000000..106f410e6 --- /dev/null +++ b/openbsc/src/libgb/gprs_ns_frgre.c @@ -0,0 +1,304 @@ +/* GPRS Networks Service (NS) messages on the Gb interface + * 3GPP TS 08.16 version 8.0.1 Release 1999 / ETSI TS 101 299 V8.0.1 (2002-05) */ + +/* NS-over-FR-over-GRE implementation */ + +/* (C) 2009-2010 by Harald Welte + * + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + * + */ + +#include +#include +#include + +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include + +#define GRE_PTYPE_FR 0x6559 +#define GRE_PTYPE_IPv4 0x0800 +#define GRE_PTYPE_KAR 0x0000 /* keepalive response */ + +struct gre_hdr { + uint16_t flags; + uint16_t ptype; +} __attribute__ ((packed)); + +/* IPv4 messages inside the GRE tunnel might be GRE keepalives */ +static int handle_rx_gre_ipv4(struct bsc_fd *bfd, struct msgb *msg, + struct iphdr *iph, struct gre_hdr *greh) +{ + struct gprs_ns_inst *nsi = bfd->data; + int gre_payload_len; + struct iphdr *inner_iph; + struct gre_hdr *inner_greh; + struct sockaddr_in daddr; + struct in_addr ia; + + gre_payload_len = msg->len - (iph->ihl*4 + sizeof(*greh)); + + inner_iph = (struct iphdr *) ((uint8_t *)greh + sizeof(*greh)); + + if (gre_payload_len < inner_iph->ihl*4 + sizeof(*inner_greh)) { + LOGP(DNS, LOGL_ERROR, "GRE keepalive too short\n"); + return -EIO; + } + + if (inner_iph->saddr != iph->daddr || + inner_iph->daddr != iph->saddr) { + LOGP(DNS, LOGL_ERROR, + "GRE keepalive with wrong tunnel addresses\n"); + return -EIO; + } + + if (inner_iph->protocol != IPPROTO_GRE) { + LOGP(DNS, LOGL_ERROR, "GRE keepalive with wrong protocol\n"); + return -EIO; + } + + inner_greh = (struct gre_hdr *) ((uint8_t *)inner_iph + iph->ihl*4); + if (inner_greh->ptype != htons(GRE_PTYPE_KAR)) { + LOGP(DNS, LOGL_ERROR, "GRE keepalive inner GRE type != 0\n"); + return -EIO; + } + + /* Actually send the response back */ + + daddr.sin_family = AF_INET; + daddr.sin_addr.s_addr = inner_iph->daddr; + daddr.sin_port = IPPROTO_GRE; + + ia.s_addr = iph->saddr; + LOGP(DNS, LOGL_DEBUG, "GRE keepalive from %s, responding\n", + inet_ntoa(ia)); + + return sendto(nsi->frgre.fd.fd, inner_greh, + gre_payload_len - inner_iph->ihl*4, 0, + (struct sockaddr *)&daddr, sizeof(daddr)); +} + +static struct msgb *read_nsfrgre_msg(struct bsc_fd *bfd, int *error, + struct sockaddr_in *saddr) +{ + struct msgb *msg = msgb_alloc(NS_ALLOC_SIZE, "Gb/NS/FR/GRE Rx"); + int ret = 0; + socklen_t saddr_len = sizeof(*saddr); + struct iphdr *iph; + struct gre_hdr *greh; + uint8_t *frh; + uint16_t dlci; + + if (!msg) { + *error = -ENOMEM; + return NULL; + } + + ret = recvfrom(bfd->fd, msg->data, NS_ALLOC_SIZE, 0, + (struct sockaddr *)saddr, &saddr_len); + if (ret < 0) { + LOGP(DNS, LOGL_ERROR, "recv error %s during NS-FR-GRE recv\n", + strerror(errno)); + *error = ret; + goto out_err; + } else if (ret == 0) { + *error = ret; + goto out_err; + } + + msgb_put(msg, ret); + + if (msg->len < sizeof(*iph) + sizeof(*greh) + 2) { + LOGP(DNS, LOGL_ERROR, "Short IP packet: %u bytes\n", msg->len); + *error = -EIO; + goto out_err; + } + + iph = (struct iphdr *) msg->data; + if (msg->len < (iph->ihl*4 + sizeof(*greh) + 2)) { + LOGP(DNS, LOGL_ERROR, "Short IP packet: %u bytes\n", msg->len); + *error = -EIO; + goto out_err; + } + + greh = (struct gre_hdr *) (msg->data + iph->ihl*4); + if (greh->flags) { + LOGP(DNS, LOGL_NOTICE, "Unknown GRE flags 0x%04x\n", + ntohs(greh->flags)); + } + + switch (ntohs(greh->ptype)) { + case GRE_PTYPE_IPv4: + /* IPv4 messages might be GRE keepalives */ + *error = handle_rx_gre_ipv4(bfd, msg, iph, greh); + goto out_err; + break; + case GRE_PTYPE_FR: + /* continue as usual */ + break; + default: + LOGP(DNS, LOGL_NOTICE, "Unknown GRE protocol 0x%04x != FR\n", + ntohs(greh->ptype)); + *error = -EIO; + goto out_err; + break; + } + + if (msg->len < sizeof(*greh) + 2) { + LOGP(DNS, LOGL_ERROR, "Short FR header: %u bytes\n", msg->len); + *error = -EIO; + goto out_err; + } + + frh = (uint8_t *)greh + sizeof(*greh); + if (frh[0] & 0x01) { + LOGP(DNS, LOGL_NOTICE, "Unsupported single-byte FR address\n"); + *error = -EIO; + goto out_err; + } + dlci = ((frh[0] & 0xfc) << 2); + if ((frh[1] & 0x0f) != 0x01) { + LOGP(DNS, LOGL_NOTICE, "Unknown second FR octet 0x%02x\n", + frh[1]); + *error = -EIO; + goto out_err; + } + dlci |= (frh[1] >> 4); + + msg->l2h = frh+2; + + /* Store DLCI in NETWORK BYTEORDER in sockaddr port member */ + saddr->sin_port = htons(dlci); + + return msg; + +out_err: + msgb_free(msg); + return NULL; +} + +int gprs_ns_rcvmsg(struct gprs_ns_inst *nsi, struct msgb *msg, + struct sockaddr_in *saddr, enum gprs_ns_ll ll); + +static int handle_nsfrgre_read(struct bsc_fd *bfd) +{ + int rc; + struct sockaddr_in saddr; + struct gprs_ns_inst *nsi = bfd->data; + struct msgb *msg; + uint16_t dlci; + + msg = read_nsfrgre_msg(bfd, &rc, &saddr); + if (!msg) + return rc; + + dlci = ntohs(saddr.sin_port); + if (dlci == 0 || dlci == 1023) { + LOGP(DNS, LOGL_INFO, "Received FR on LMI DLCI %u - ignoring\n", + dlci); + rc = 0; + goto out; + } + + rc = gprs_ns_rcvmsg(nsi, msg, &saddr, GPRS_NS_LL_FR_GRE); +out: + msgb_free(msg); + + return rc; +} + +static int handle_nsfrgre_write(struct bsc_fd *bfd) +{ + /* FIXME: actually send the data here instead of nsip_sendmsg() */ + return -EIO; +} + +int gprs_ns_frgre_sendmsg(struct gprs_nsvc *nsvc, struct msgb *msg) +{ + int rc; + struct gprs_ns_inst *nsi = nsvc->nsi; + struct sockaddr_in daddr; + uint16_t dlci = ntohs(nsvc->frgre.bts_addr.sin_port); + uint8_t *frh; + struct gre_hdr *greh; + + /* Build socket address for the packet destionation */ + daddr.sin_family = AF_INET; + daddr.sin_addr = nsvc->frgre.bts_addr.sin_addr; + daddr.sin_port = IPPROTO_GRE; + + /* Prepend the FR header */ + frh = msgb_push(msg, 2); + frh[0] = (dlci >> 2) & 0xfc; + frh[1] = ((dlci & 0xf)<<4) | 0x01; + + /* Prepend the GRE header */ + greh = (struct gre_hdr *) msgb_push(msg, sizeof(*greh)); + greh->flags = 0; + greh->ptype = htons(GRE_PTYPE_FR); + + rc = sendto(nsi->frgre.fd.fd, msg->data, msg->len, 0, + (struct sockaddr *)&daddr, sizeof(daddr)); + + talloc_free(msg); + + return rc; +} + +static int nsfrgre_fd_cb(struct bsc_fd *bfd, unsigned int what) +{ + int rc = 0; + + if (what & BSC_FD_READ) + rc = handle_nsfrgre_read(bfd); + if (what & BSC_FD_WRITE) + rc = handle_nsfrgre_write(bfd); + + return rc; +} + +int gprs_ns_frgre_listen(struct gprs_ns_inst *nsi) +{ + int rc; + + /* Make sure we close any existing socket before changing it */ + if (nsi->frgre.fd.fd) + close(nsi->frgre.fd.fd); + + if (!nsi->frgre.enabled) + return 0; + + rc = make_sock(&nsi->frgre.fd, IPPROTO_GRE, nsi->frgre.local_ip, + 0, nsfrgre_fd_cb); + if (rc < 0) { + LOGP(DNS, LOGL_ERROR, "Error creating GRE socket (%s)\n", + strerror(errno)); + return rc; + } + nsi->frgre.fd.data = nsi; + + return rc; +} diff --git a/openbsc/src/libgb/gprs_ns_vty.c b/openbsc/src/libgb/gprs_ns_vty.c new file mode 100644 index 000000000..39277fc71 --- /dev/null +++ b/openbsc/src/libgb/gprs_ns_vty.c @@ -0,0 +1,569 @@ +/* VTY interface for our GPRS Networks Service (NS) implementation */ + +/* (C) 2009-2010 by Harald Welte + * + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + * + */ + +#include +#include +#include +#include + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +static struct gprs_ns_inst *vty_nsi = NULL; + +/* FIXME: this should go to some common file as it is copied + * in vty_interface.c of the BSC */ +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 struct cmd_node ns_node = { + NS_NODE, + "%s(ns)#", + 1, +}; + +static int config_write_ns(struct vty *vty) +{ + struct gprs_nsvc *nsvc; + unsigned int i; + struct in_addr ia; + + vty_out(vty, "ns%s", VTY_NEWLINE); + + llist_for_each_entry(nsvc, &vty_nsi->gprs_nsvcs, list) { + if (!nsvc->persistent) + continue; + vty_out(vty, " nse %u nsvci %u%s", + nsvc->nsei, nsvc->nsvci, VTY_NEWLINE); + vty_out(vty, " nse %u remote-role %s%s", + nsvc->nsei, nsvc->remote_end_is_sgsn ? "sgsn" : "bss", + VTY_NEWLINE); + switch (nsvc->ll) { + case GPRS_NS_LL_UDP: + vty_out(vty, " nse %u encapsulation udp%s", nsvc->nsei, + VTY_NEWLINE); + vty_out(vty, " nse %u remote-ip %s%s", + nsvc->nsei, + inet_ntoa(nsvc->ip.bts_addr.sin_addr), + VTY_NEWLINE); + vty_out(vty, " nse %u remote-port %u%s", + nsvc->nsei, ntohs(nsvc->ip.bts_addr.sin_port), + VTY_NEWLINE); + break; + case GPRS_NS_LL_FR_GRE: + vty_out(vty, " nse %u encapsulation framerelay-gre%s", + nsvc->nsei, VTY_NEWLINE); + vty_out(vty, " nse %u remote-ip %s%s", + nsvc->nsei, + inet_ntoa(nsvc->frgre.bts_addr.sin_addr), + VTY_NEWLINE); + vty_out(vty, " nse %u fr-dlci %u%s", + nsvc->nsei, ntohs(nsvc->frgre.bts_addr.sin_port), + VTY_NEWLINE); + default: + break; + } + } + + for (i = 0; i < ARRAY_SIZE(vty_nsi->timeout); i++) + vty_out(vty, " timer %s %u%s", + get_value_string(gprs_ns_timer_strs, i), + vty_nsi->timeout[i], VTY_NEWLINE); + + if (vty_nsi->nsip.local_ip) { + ia.s_addr = htonl(vty_nsi->nsip.local_ip); + vty_out(vty, " encapsulation udp local-ip %s%s", + inet_ntoa(ia), VTY_NEWLINE); + } + if (vty_nsi->nsip.local_port) + vty_out(vty, " encapsulation udp local-port %u%s", + vty_nsi->nsip.local_port, VTY_NEWLINE); + + vty_out(vty, " encapsulation framerelay-gre enabled %u%s", + vty_nsi->frgre.enabled ? 1 : 0, VTY_NEWLINE); + if (vty_nsi->frgre.local_ip) { + ia.s_addr = htonl(vty_nsi->frgre.local_ip); + vty_out(vty, " encapsulation framerelay-gre local-ip %s%s", + inet_ntoa(ia), VTY_NEWLINE); + } + + return CMD_SUCCESS; +} + +DEFUN(cfg_ns, cfg_ns_cmd, + "ns", + "Configure the GPRS Network Service") +{ + vty->node = NS_NODE; + return CMD_SUCCESS; +} + +static void dump_nse(struct vty *vty, struct gprs_nsvc *nsvc, int stats) +{ + vty_out(vty, "NSEI %5u, NS-VC %5u, Remote: %-4s, %5s %9s", + nsvc->nsei, nsvc->nsvci, + nsvc->remote_end_is_sgsn ? "SGSN" : "BSS", + nsvc->state & NSE_S_ALIVE ? "ALIVE" : "DEAD", + nsvc->state & NSE_S_BLOCKED ? "BLOCKED" : "UNBLOCKED"); + if (nsvc->ll == GPRS_NS_LL_UDP || nsvc->ll == GPRS_NS_LL_FR_GRE) + vty_out(vty, ", %s %15s:%u", + nsvc->ll == GPRS_NS_LL_UDP ? "UDP " : "FR-GRE", + inet_ntoa(nsvc->ip.bts_addr.sin_addr), + ntohs(nsvc->ip.bts_addr.sin_port)); + vty_out(vty, "%s", VTY_NEWLINE); + if (stats) + vty_out_rate_ctr_group(vty, " ", nsvc->ctrg); +} + +static void dump_ns(struct vty *vty, struct gprs_ns_inst *nsi, int stats) +{ + struct gprs_nsvc *nsvc; + struct in_addr ia; + + ia.s_addr = htonl(vty_nsi->nsip.local_ip); + vty_out(vty, "Encapsulation NS-UDP-IP Local IP: %s, UDP Port: %u%s", + inet_ntoa(ia), vty_nsi->nsip.local_port, VTY_NEWLINE); + + ia.s_addr = htonl(vty_nsi->frgre.local_ip); + vty_out(vty, "Encapsulation NS-FR-GRE-IP Local IP: %s%s", + inet_ntoa(ia), VTY_NEWLINE); + + llist_for_each_entry(nsvc, &nsi->gprs_nsvcs, list) { + if (nsvc == nsi->unknown_nsvc) + continue; + dump_nse(vty, nsvc, stats); + } +} + +DEFUN(show_ns, show_ns_cmd, "show ns", + SHOW_STR "Display information about the NS protocol") +{ + struct gprs_ns_inst *nsi = vty_nsi; + dump_ns(vty, nsi, 0); + return CMD_SUCCESS; +} + +DEFUN(show_ns_stats, show_ns_stats_cmd, "show ns stats", + SHOW_STR + "Display information about the NS protocol\n" + "Include statistics\n") +{ + struct gprs_ns_inst *nsi = vty_nsi; + dump_ns(vty, nsi, 1); + return CMD_SUCCESS; +} + +DEFUN(show_nse, show_nse_cmd, "show ns (nsei|nsvc) <0-65535> [stats]", + SHOW_STR "Display information about the NS protocol\n" + "Select one NSE by its NSE Identifier\n" + "Select one NSE by its NS-VC Identifier\n" + "The Identifier of selected type\n" + "Include Statistics\n") +{ + struct gprs_ns_inst *nsi = vty_nsi; + struct gprs_nsvc *nsvc; + uint16_t id = atoi(argv[1]); + int show_stats = 0; + + if (!strcmp(argv[0], "nsei")) + nsvc = nsvc_by_nsei(nsi, id); + else + nsvc = nsvc_by_nsvci(nsi, id); + + if (!nsvc) { + vty_out(vty, "No such NS Entity%s", VTY_NEWLINE); + return CMD_WARNING; + } + + if (argc >= 3) + show_stats = 1; + + dump_nse(vty, nsvc, show_stats); + return CMD_SUCCESS; +} + +#define NSE_CMD_STR "Persistent NS Entity\n" "NS Entity ID (NSEI)\n" + +DEFUN(cfg_nse_nsvc, cfg_nse_nsvci_cmd, + "nse <0-65535> nsvci <0-65534>", + NSE_CMD_STR + "NS Virtual Connection\n" + "NS Virtual Connection ID (NSVCI)\n" + ) +{ + uint16_t nsei = atoi(argv[0]); + uint16_t nsvci = atoi(argv[1]); + struct gprs_nsvc *nsvc; + + nsvc = nsvc_by_nsei(vty_nsi, nsei); + if (!nsvc) { + nsvc = nsvc_create(vty_nsi, nsvci); + nsvc->nsei = nsei; + } + nsvc->nsvci = nsvci; + /* All NSVCs that are explicitly configured by VTY are + * marked as persistent so we can write them to the config + * file at some later point */ + nsvc->persistent = 1; + + return CMD_SUCCESS; +} + +DEFUN(cfg_nse_remoteip, cfg_nse_remoteip_cmd, + "nse <0-65535> remote-ip A.B.C.D", + NSE_CMD_STR + "Remote IP Address\n" + "Remote IP Address\n") +{ + uint16_t nsei = atoi(argv[0]); + struct gprs_nsvc *nsvc; + + nsvc = nsvc_by_nsei(vty_nsi, nsei); + if (!nsvc) { + vty_out(vty, "No such NSE (%u)%s", nsei, VTY_NEWLINE); + return CMD_WARNING; + } + inet_aton(argv[1], &nsvc->ip.bts_addr.sin_addr); + + return CMD_SUCCESS; + +} + +DEFUN(cfg_nse_remoteport, cfg_nse_remoteport_cmd, + "nse <0-65535> remote-port <0-65535>", + NSE_CMD_STR + "Remote UDP Port\n" + "Remote UDP Port Number\n") +{ + uint16_t nsei = atoi(argv[0]); + uint16_t port = atoi(argv[1]); + struct gprs_nsvc *nsvc; + + nsvc = nsvc_by_nsei(vty_nsi, nsei); + if (!nsvc) { + vty_out(vty, "No such NSE (%u)%s", nsei, VTY_NEWLINE); + return CMD_WARNING; + } + + if (nsvc->ll != GPRS_NS_LL_UDP) { + vty_out(vty, "Cannot set UDP Port on non-UDP NSE%s", + VTY_NEWLINE); + return CMD_WARNING; + } + + nsvc->ip.bts_addr.sin_port = htons(port); + + return CMD_SUCCESS; +} + +DEFUN(cfg_nse_fr_dlci, cfg_nse_fr_dlci_cmd, + "nse <0-65535> fr-dlci <16-1007>", + NSE_CMD_STR + "Frame Relay DLCI\n" + "Frame Relay DLCI Number\n") +{ + uint16_t nsei = atoi(argv[0]); + uint16_t dlci = atoi(argv[1]); + struct gprs_nsvc *nsvc; + + nsvc = nsvc_by_nsei(vty_nsi, nsei); + if (!nsvc) { + vty_out(vty, "No such NSE (%u)%s", nsei, VTY_NEWLINE); + return CMD_WARNING; + } + + if (nsvc->ll != GPRS_NS_LL_FR_GRE) { + vty_out(vty, "Cannot set FR DLCI on non-FR NSE%s", + VTY_NEWLINE); + return CMD_WARNING; + } + + nsvc->frgre.bts_addr.sin_port = htons(dlci); + + return CMD_SUCCESS; +} + +DEFUN(cfg_nse_encaps, cfg_nse_encaps_cmd, + "nse <0-65535> encapsulation (udp|framerelay-gre)", + NSE_CMD_STR + "Encapsulation for NS\n" + "UDP/IP Encapsulation\n" "Frame-Relay/GRE/IP Encapsulation\n") +{ + uint16_t nsei = atoi(argv[0]); + struct gprs_nsvc *nsvc; + + nsvc = nsvc_by_nsei(vty_nsi, nsei); + if (!nsvc) { + vty_out(vty, "No such NSE (%u)%s", nsei, VTY_NEWLINE); + return CMD_WARNING; + } + + if (!strcmp(argv[1], "udp")) + nsvc->ll = GPRS_NS_LL_UDP; + else + nsvc->ll = GPRS_NS_LL_FR_GRE; + + return CMD_SUCCESS; +} + + +DEFUN(cfg_nse_remoterole, cfg_nse_remoterole_cmd, + "nse <0-65535> remote-role (sgsn|bss)", + NSE_CMD_STR + "Remote NSE Role\n" + "Remote Peer is SGSN\n" + "Remote Peer is BSS\n") +{ + uint16_t nsei = atoi(argv[0]); + struct gprs_nsvc *nsvc; + + nsvc = nsvc_by_nsei(vty_nsi, nsei); + if (!nsvc) { + vty_out(vty, "No such NSE (%u)%s", nsei, VTY_NEWLINE); + return CMD_WARNING; + } + + if (!strcmp(argv[1], "sgsn")) + nsvc->remote_end_is_sgsn = 1; + else + nsvc->remote_end_is_sgsn = 0; + + return CMD_SUCCESS; +} + +DEFUN(cfg_no_nse, cfg_no_nse_cmd, + "no nse <0-65535>", + "Delete Persistent NS Entity\n" + "Delete " NSE_CMD_STR) +{ + uint16_t nsei = atoi(argv[0]); + struct gprs_nsvc *nsvc; + + nsvc = nsvc_by_nsei(vty_nsi, nsei); + if (!nsvc) { + vty_out(vty, "No such NSE (%u)%s", nsei, VTY_NEWLINE); + return CMD_WARNING; + } + + if (!nsvc->persistent) { + vty_out(vty, "NSEI %u is not a persistent NSE%s", + nsei, VTY_NEWLINE); + return CMD_WARNING; + } + + nsvc->persistent = 0; + + return CMD_SUCCESS; +} + +DEFUN(cfg_ns_timer, cfg_ns_timer_cmd, + "timer " NS_TIMERS " <0-65535>", + "Network Service Timer\n" + NS_TIMERS_HELP "Timer Value\n") +{ + int idx = get_string_value(gprs_ns_timer_strs, argv[0]); + int val = atoi(argv[1]); + + if (idx < 0 || idx >= ARRAY_SIZE(vty_nsi->timeout)) + return CMD_WARNING; + + vty_nsi->timeout[idx] = val; + + return CMD_SUCCESS; +} + +#define ENCAPS_STR "NS encapsulation options\n" + +DEFUN(cfg_nsip_local_ip, cfg_nsip_local_ip_cmd, + "encapsulation udp local-ip A.B.C.D", + ENCAPS_STR "NS over UDP Encapsulation\n" + "Set the IP address on which we listen for NS/UDP\n" + "IP Address\n") +{ + struct in_addr ia; + + inet_aton(argv[0], &ia); + vty_nsi->nsip.local_ip = ntohl(ia.s_addr); + + return CMD_SUCCESS; +} + +DEFUN(cfg_nsip_local_port, cfg_nsip_local_port_cmd, + "encapsulation udp local-port <0-65535>", + ENCAPS_STR "NS over UDP Encapsulation\n" + "Set the UDP port on which we listen for NS/UDP\n" + "UDP port number\n") +{ + unsigned int port = atoi(argv[0]); + + vty_nsi->nsip.local_port = port; + + return CMD_SUCCESS; +} + +DEFUN(cfg_frgre_local_ip, cfg_frgre_local_ip_cmd, + "encapsulation framerelay-gre local-ip A.B.C.D", + ENCAPS_STR "NS over Frame Relay over GRE Encapsulation\n" + "Set the IP address on which we listen for NS/FR/GRE\n" + "IP Address\n") +{ + struct in_addr ia; + + if (!vty_nsi->frgre.enabled) { + vty_out(vty, "FR/GRE is not enabled%s", VTY_NEWLINE); + return CMD_WARNING; + } + inet_aton(argv[0], &ia); + vty_nsi->frgre.local_ip = ntohl(ia.s_addr); + + return CMD_SUCCESS; +} + +DEFUN(cfg_frgre_enable, cfg_frgre_enable_cmd, + "encapsulation framerelay-gre enabled (1|0)", + ENCAPS_STR "NS over Frame Relay over GRE Encapsulation\n" + "Enable or disable Frame Relay over GRE\n" + "Enable\n" "Disable\n") +{ + int enabled = atoi(argv[0]); + + vty_nsi->frgre.enabled = enabled; + + return CMD_SUCCESS; +} + +DEFUN(nsvc_nsei, nsvc_nsei_cmd, + "nsvc nsei <0-65535> (block|unblock|reset)", + "Perform an operation on a NSVC\n" + "NS-VC Identifier (NS-VCI)\n" + "Initiate BLOCK procedure\n" + "Initiate UNBLOCK procedure\n" + "Initiate RESET procedure\n") +{ + uint16_t nsvci = atoi(argv[0]); + const char *operation = argv[1]; + struct gprs_nsvc *nsvc; + + nsvc = nsvc_by_nsei(vty_nsi, nsvci); + if (!nsvc) { + vty_out(vty, "No such NSVCI (%u)%s", nsvci, VTY_NEWLINE); + return CMD_WARNING; + } + + if (!strcmp(operation, "block")) + gprs_ns_tx_block(nsvc, NS_CAUSE_OM_INTERVENTION); + else if (!strcmp(operation, "unblock")) + gprs_ns_tx_unblock(nsvc); + else if (!strcmp(operation, "reset")) + gprs_nsvc_reset(nsvc, NS_CAUSE_OM_INTERVENTION); + else + return CMD_WARNING; + + return CMD_SUCCESS; +} + +DEFUN(logging_fltr_nsvc, + logging_fltr_nsvc_cmd, + "logging filter nsvc (nsei|nsvci) <0-65535>", + LOGGING_STR FILTER_STR + "Filter based on NS Virtual Connection\n" + "Identify NS-VC by NSEI\n" + "Identify NS-VC by NSVCI\n" + "Numeric identifier\n") +{ + struct log_target *tgt = osmo_log_vty2tgt(vty); + struct gprs_nsvc *nsvc; + uint16_t id = atoi(argv[1]); + + if (!tgt) + return CMD_WARNING; + + if (!strcmp(argv[0], "nsei")) + nsvc = nsvc_by_nsei(vty_nsi, id); + else + nsvc = nsvc_by_nsvci(vty_nsi, id); + + if (!nsvc) { + vty_out(vty, "No NS-VC by that identifier%s", VTY_NEWLINE); + return CMD_WARNING; + } + + log_set_nsvc_filter(tgt, nsvc); + return CMD_SUCCESS; +} + +int gprs_ns_vty_init(struct gprs_ns_inst *nsi) +{ + vty_nsi = nsi; + + install_element_ve(&show_ns_cmd); + install_element_ve(&show_ns_stats_cmd); + install_element_ve(&show_nse_cmd); + install_element_ve(&logging_fltr_nsvc_cmd); + + install_element(CFG_LOG_NODE, &logging_fltr_nsvc_cmd); + + install_element(CONFIG_NODE, &cfg_ns_cmd); + install_node(&ns_node, config_write_ns); + install_default(NS_NODE); + install_element(NS_NODE, &ournode_exit_cmd); + install_element(NS_NODE, &ournode_end_cmd); + install_element(NS_NODE, &cfg_nse_nsvci_cmd); + install_element(NS_NODE, &cfg_nse_remoteip_cmd); + install_element(NS_NODE, &cfg_nse_remoteport_cmd); + install_element(NS_NODE, &cfg_nse_fr_dlci_cmd); + install_element(NS_NODE, &cfg_nse_encaps_cmd); + install_element(NS_NODE, &cfg_nse_remoterole_cmd); + install_element(NS_NODE, &cfg_no_nse_cmd); + install_element(NS_NODE, &cfg_ns_timer_cmd); + install_element(NS_NODE, &cfg_nsip_local_ip_cmd); + install_element(NS_NODE, &cfg_nsip_local_port_cmd); + install_element(NS_NODE, &cfg_frgre_enable_cmd); + install_element(NS_NODE, &cfg_frgre_local_ip_cmd); + + install_element(ENABLE_NODE, &nsvc_nsei_cmd); + + return 0; +} diff --git a/openbsc/src/libmgcp/Makefile.am b/openbsc/src/libmgcp/Makefile.am new file mode 100644 index 000000000..b1d1d158a --- /dev/null +++ b/openbsc/src/libmgcp/Makefile.am @@ -0,0 +1,7 @@ +INCLUDES = $(all_includes) -I$(top_srcdir)/include -I$(top_builddir) +AM_CFLAGS=-Wall $(LIBOSMOCORE_CFLAGS) $(LIBOSMOVTY_CFLAGS) $(COVERAGE_CFLAGS) +AM_LDFLAGS = $(LIBOSMOCORE_LIBS) $(COVERAGE_LDFLAGS) + +noinst_LIBRARIES = libmgcp.a + +libmgcp_a_SOURCES = mgcp_protocol.c mgcp_network.c mgcp_vty.c diff --git a/openbsc/src/libmgcp/mgcp_network.c b/openbsc/src/libmgcp/mgcp_network.c new file mode 100644 index 000000000..51c08d151 --- /dev/null +++ b/openbsc/src/libmgcp/mgcp_network.c @@ -0,0 +1,579 @@ +/* A Media Gateway Control Protocol Media Gateway: RFC 3435 */ +/* The protocol implementation */ + +/* + * (C) 2009-2011 by Holger Hans Peter Freyther + * (C) 2009-2011 by On-Waves + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + * + */ + +#include +#include +#include +#include + +#include +#include + +#include +#include + +#include +#include +#include + +#warning "Make use of the rtp proxy code" + +/* attempt to determine byte order */ +#include +#include +#include + +#ifndef __BYTE_ORDER +#error "__BYTE_ORDER should be defined by someone" +#endif + +/* according to rtp_proxy.c RFC 3550 */ +struct rtp_hdr { +#if __BYTE_ORDER == __LITTLE_ENDIAN + uint8_t csrc_count:4, + extension:1, + padding:1, + version:2; + uint8_t payload_type:7, + marker:1; +#elif __BYTE_ORDER == __BIG_ENDIAN + uint8_t version:2, + padding:1, + extension:1, + csrc_count:4; + uint8_t marker:1, + payload_type:7; +#endif + uint16_t sequence; + uint32_t timestamp; + uint32_t ssrc; +} __attribute__((packed)); + + +enum { + DEST_NETWORK = 0, + DEST_BTS = 1, +}; + +enum { + PROTO_RTP, + PROTO_RTCP, +}; + +#define DUMMY_LOAD 0x23 + + +static int 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[] = { DUMMY_LOAD }; + + return udp_send(endp->net_end.rtp.fd, &endp->net_end.addr, + endp->net_end.rtp_port, buf, 1); +} + +static void patch_and_count(struct mgcp_endpoint *endp, struct mgcp_rtp_state *state, + int payload, struct sockaddr_in *addr, char *data, int len) +{ + uint16_t seq; + uint32_t timestamp; + struct rtp_hdr *rtp_hdr; + + if (len < sizeof(*rtp_hdr)) + return; + + rtp_hdr = (struct rtp_hdr *) data; + seq = ntohs(rtp_hdr->sequence); + timestamp = ntohl(rtp_hdr->timestamp); + + if (!state->initialized) { + state->seq_no = seq - 1; + state->ssrc = state->orig_ssrc = rtp_hdr->ssrc; + state->initialized = 1; + state->last_timestamp = timestamp; + } else if (state->ssrc != rtp_hdr->ssrc) { + state->ssrc = rtp_hdr->ssrc; + state->seq_offset = (state->seq_no + 1) - seq; + state->timestamp_offset = state->last_timestamp - timestamp; + state->patch = endp->allow_patch; + LOGP(DMGCP, LOGL_NOTICE, + "The SSRC changed on 0x%x SSRC: %u offset: %d from %s:%d in %d\n", + ENDPOINT_NUMBER(endp), state->ssrc, state->seq_offset, + inet_ntoa(addr->sin_addr), ntohs(addr->sin_port), endp->conn_mode); + } + + /* apply the offset and store it back to the packet */ + if (state->patch) { + seq += state->seq_offset; + rtp_hdr->sequence = htons(seq); + rtp_hdr->ssrc = state->orig_ssrc; + + timestamp += state->timestamp_offset; + rtp_hdr->timestamp = htonl(timestamp); + } + + /* seq changed, now compare if we have lost something */ + if (state->seq_no + 1u != seq) + state->lost_no = abs(seq - (state->seq_no + 1)); + state->seq_no = seq; + + state->last_timestamp = timestamp; + + 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 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; +} + +static int send_to(struct mgcp_endpoint *endp, int dest, int is_rtp, + struct sockaddr_in *addr, char *buf, int rc) +{ + struct mgcp_trunk_config *tcfg = endp->tcfg; + /* 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 == DEST_NETWORK) { + if (is_rtp) { + patch_and_count(endp, &endp->bts_state, + endp->net_end.payload_type, + addr, buf, rc); + forward_data(endp->net_end.rtp.fd, + &endp->taps[MGCP_TAP_NET_OUT], buf, rc); + return udp_send(endp->net_end.rtp.fd, &endp->net_end.addr, + endp->net_end.rtp_port, buf, rc); + } else { + return udp_send(endp->net_end.rtcp.fd, &endp->net_end.addr, + endp->net_end.rtcp_port, buf, rc); + } + } else { + if (is_rtp) { + patch_and_count(endp, &endp->net_state, + endp->bts_end.payload_type, + addr, buf, rc); + forward_data(endp->bts_end.rtp.fd, + &endp->taps[MGCP_TAP_BTS_OUT], buf, rc); + return udp_send(endp->bts_end.rtp.fd, &endp->bts_end.addr, + endp->bts_end.rtp_port, buf, rc); + } else { + return udp_send(endp->bts_end.rtcp.fd, &endp->bts_end.addr, + endp->bts_end.rtcp_port, buf, rc); + } + } +} + +static int recevice_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 bsc_fd *fd, unsigned int what) +{ + char buf[4096]; + struct sockaddr_in addr; + struct mgcp_endpoint *endp; + int rc, proto; + + endp = (struct mgcp_endpoint *) fd->data; + + rc = recevice_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, + "Data from wrong address %s on 0x%x\n", + inet_ntoa(addr.sin_addr), ENDPOINT_NUMBER(endp)); + return -1; + } + + 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; + } + + /* throw away the dummy message */ + if (rc == 1 && buf[0] == 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 ? PROTO_RTP : PROTO_RTCP; + endp->net_end.packets += 1; + + forward_data(fd->fd, &endp->taps[MGCP_TAP_NET_IN], buf, rc); + if (endp->is_transcoded) + return send_transcoder(&endp->trans_net, endp->cfg, proto == PROTO_RTP, &buf[0], rc); + else + return send_to(endp, DEST_BTS, proto == PROTO_RTP, &addr, &buf[0], rc); +} + +static void discover_bts(struct mgcp_endpoint *endp, int proto, struct sockaddr_in *addr) +{ + struct mgcp_config *cfg = endp->cfg; + + if (proto == 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 == 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 bsc_fd *fd, unsigned int what) +{ + char buf[4096]; + struct sockaddr_in addr; + struct mgcp_endpoint *endp; + struct mgcp_config *cfg; + int rc, proto; + + endp = (struct mgcp_endpoint *) fd->data; + cfg = endp->cfg; + + rc = recevice_from(endp, fd->fd, &addr, buf, sizeof(buf)); + if (rc <= 0) + return -1; + + proto = fd == &endp->bts_end.rtp ? PROTO_RTP : 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] == 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; + + forward_data(fd->fd, &endp->taps[MGCP_TAP_BTS_IN], buf, rc); + if (endp->is_transcoded) + return send_transcoder(&endp->trans_bts, endp->cfg, proto == PROTO_RTP, &buf[0], rc); + else + return send_to(endp, DEST_NETWORK, proto == PROTO_RTP, &addr, &buf[0], rc); +} + +static int rtp_data_transcoder(struct mgcp_rtp_end *end, struct mgcp_endpoint *_endp, + int dest, struct bsc_fd *fd) +{ + char buf[4096]; + struct sockaddr_in addr; + struct mgcp_config *cfg; + int rc, proto; + + cfg = _endp->cfg; + rc = recevice_from(_endp, fd->fd, &addr, buf, sizeof(buf)); + if (rc <= 0) + return -1; + + proto = fd == &end->rtp ? PROTO_RTP : 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] == 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 send_to(_endp, dest, proto == PROTO_RTP, &addr, &buf[0], rc); +} + +static int rtp_data_trans_net(struct bsc_fd *fd, unsigned int what) +{ + struct mgcp_endpoint *endp; + endp = (struct mgcp_endpoint *) fd->data; + + return rtp_data_transcoder(&endp->trans_net, endp, DEST_NETWORK, fd); +} + +static int rtp_data_trans_bts(struct bsc_fd *fd, unsigned int what) +{ + struct mgcp_endpoint *endp; + endp = (struct mgcp_endpoint *) fd->data; + + return rtp_data_transcoder(&endp->trans_bts, endp, DEST_BTS, fd); +} + +static int create_bind(const char *source_addr, struct bsc_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; +} + +static int 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, struct mgcp_rtp_end *rtp_end, int endpno) +{ + if (create_bind(cfg->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", + cfg->source_addr, rtp_end->local_port, endpno); + goto cleanup0; + } + + if (create_bind(cfg->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", + cfg->source_addr, rtp_end->local_port + 1, endpno); + goto cleanup1; + } + + set_ip_tos(rtp_end->rtp.fd, cfg->endp_dscp); + set_ip_tos(rtp_end->rtcp.fd, cfg->endp_dscp); + + rtp_end->rtp.when = BSC_FD_READ; + if (bsc_register_fd(&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 (bsc_register_fd(&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: + bsc_unregister_fd(&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 bsc_fd *, unsigned), + struct mgcp_endpoint *_endp, 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, 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, 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, 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, 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, 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; + bsc_unregister_fd(&end->rtp); + } + + if (end->rtcp.fd != -1) { + close(end->rtcp.fd); + end->rtcp.fd = -1; + bsc_unregister_fd(&end->rtcp); + } + + return 0; +} diff --git a/openbsc/src/libmgcp/mgcp_protocol.c b/openbsc/src/libmgcp/mgcp_protocol.c new file mode 100644 index 000000000..ba290dd90 --- /dev/null +++ b/openbsc/src/libmgcp/mgcp_protocol.c @@ -0,0 +1,1102 @@ +/* A Media Gateway Control Protocol Media Gateway: RFC 3435 */ +/* The protocol implementation */ + +/* + * (C) 2009-2011 by Holger Hans Peter Freyther + * (C) 2009-2011 by On-Waves + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + * + */ + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +/** + * Macro for tokenizing MGCP messages and SDP in one go. + * + */ +#define MSG_TOKENIZE_START \ + line_start = 0; \ + for (i = 0; i < msgb_l3len(msg); ++i) { \ + /* we have a line end */ \ + if (msg->l3h[i] == '\n') { \ + /* skip the first line */ \ + if (line_start == 0) { \ + line_start = i + 1; \ + continue; \ + } \ + \ + /* check if we have a proper param */ \ + if (i - line_start == 1 && msg->l3h[line_start] == '\r') { \ + } else if (i - line_start > 2 \ + && islower(msg->l3h[line_start]) \ + && msg->l3h[line_start + 1] == '=') { \ + } else if (i - line_start < 3 \ + || msg->l3h[line_start + 1] != ':' \ + || msg->l3h[line_start + 2] != ' ') \ + goto error; \ + \ + msg->l3h[i] = '\0'; \ + if (msg->l3h[i-1] == '\r') \ + msg->l3h[i-1] = '\0'; + +#define MSG_TOKENIZE_END \ + line_start = i + 1; \ + } \ + } + +static void mgcp_rtp_end_reset(struct mgcp_rtp_end *end); + +struct mgcp_request { + char *name; + struct msgb *(*handle_request) (struct mgcp_config *cfg, struct msgb *msg); + 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_config *cfg, struct msgb *msg); +static struct msgb *handle_create_con(struct mgcp_config *cfg, struct msgb *msg); +static struct msgb *handle_delete_con(struct mgcp_config *cfg, struct msgb *msg); +static struct msgb *handle_modify_con(struct mgcp_config *cfg, struct msgb *msg); +static struct msgb *handle_rsip(struct mgcp_config *cfg, struct msgb *msg); +static struct msgb *handle_noti_req(struct mgcp_config *cfg, struct msgb *msg); + +static void create_transcoder(struct mgcp_endpoint *endp); +static void delete_transcoder(struct mgcp_endpoint *endp); + +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; +} + +struct msgb *mgcp_create_response_with_data(int code, const char *txt, + const char *msg, const char *trans, + const char *data) +{ + int len; + struct msgb *res; + + res = mgcp_msgb_alloc(); + if (!res) + return NULL; + + if (data) { + len = snprintf((char *) res->data, 2048, "%d %s%s\r\n%s", code, trans, txt, data); + } else { + len = snprintf((char *) res->data, 2048, "%d %s%s\r\n", code, trans, txt); + } + + res->l2h = msgb_put(res, len); + LOGP(DMGCP, LOGL_DEBUG, "Sending response: code: %d for '%s'\n", code, res->l2h); + return res; +} + +static struct msgb *create_ok_response(int code, const char *msg, const char *trans) +{ + return mgcp_create_response_with_data(code, " OK", msg, trans, NULL); +} + +static struct msgb *create_err_response(int code, const char *msg, const char *trans) +{ + return mgcp_create_response_with_data(code, " FAIL", msg, trans, NULL); +} + +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]; + + if (!addr) + addr = endp->cfg->source_addr; + + snprintf(sdp_record, sizeof(sdp_record) - 1, + "I: %u\n\n" + "v=0\r\n" + "c=IN IP4 %s\r\n" + "m=audio %d RTP/AVP %d\r\n" + "a=rtpmap:%d %s\r\n", + endp->ci, addr, endp->net_end.local_port, + endp->bts_end.payload_type, endp->bts_end.payload_type, + endp->tcfg->audio_name); + return mgcp_create_response_with_data(200, " OK", msg, trans_id, sdp_record); +} + +/* + * 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) +{ + int code; + struct msgb *resp = NULL; + + if (msgb_l2len(msg) < 4) { + LOGP(DMGCP, LOGL_ERROR, "mgs too short: %d\n", msg->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); + } else { + int i, handled = 0; + msg->l3h = &msg->l2h[4]; + 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(cfg, msg); + break; + } + if (!handled) { + LOGP(DMGCP, LOGL_NOTICE, "MSG with type: '%.4s' not handled\n", &msg->l2h[0]); + } + } + + return resp; +} + +/* string tokenizer for the poor */ +static int find_msg_pointers(struct msgb *msg, struct mgcp_msg_ptr *ptrs, int ptrs_length) +{ + int i, found = 0; + + int whitespace = 1; + for (i = 0; i < msgb_l3len(msg) && ptrs_length > 0; ++i) { + /* if we have a space we found an end */ + if (msg->l3h[i] == ' ' || msg->l3h[i] == '\r' || msg->l3h[i] == '\n') { + if (!whitespace) { + ++found; + whitespace = 1; + ptrs->length = i - ptrs->start - 1; + ++ptrs; + --ptrs_length; + } else { + /* skip any number of whitespace */ + } + + /* line end... stop */ + if (msg->l3h[i] == '\r' || msg->l3h[i] == '\n') + break; + } else if (msg->l3h[i] == '\r' || msg->l3h[i] == '\n') { + /* line end, be done */ + break; + } else if (whitespace) { + whitespace = 0; + ptrs->start = i; + } + } + + if (ptrs_length == 0) + return -1; + return found; +} + +/** + * 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); + } else { + gw = strtoul(mgcp, &endptr, 16); + if (gw > 0 && gw < cfg->trunk.number_endpoints && strcmp(endptr, "@mgw") == 0) + return &cfg->trunk.endpoints[gw]; + } + + LOGP(DMGCP, LOGL_ERROR, "Not able to find endpoint: '%s'\n", mgcp); + return NULL; +} + +int mgcp_analyze_header(struct mgcp_config *cfg, struct msgb *msg, + struct mgcp_msg_ptr *ptr, int size, + const char **transaction_id, struct mgcp_endpoint **endp) +{ + int found; + + *transaction_id = "000000"; + + if (size < 3) { + LOGP(DMGCP, LOGL_ERROR, "Not enough space in ptr\n"); + return -1; + } + + found = find_msg_pointers(msg, ptr, size); + + if (found <= 3) { + LOGP(DMGCP, LOGL_ERROR, "Gateway: Not enough params. Found: %d\n", found); + return -1; + } + + /* + * replace the space with \0. the main method gurantess that + * we still have + 1 for null termination + */ + msg->l3h[ptr[3].start + ptr[3].length + 1] = '\0'; + msg->l3h[ptr[2].start + ptr[2].length + 1] = '\0'; + msg->l3h[ptr[1].start + ptr[1].length + 1] = '\0'; + msg->l3h[ptr[0].start + ptr[0].length + 1] = '\0'; + + if (strncmp("1.0", (const char *)&msg->l3h[ptr[3].start], 3) != 0 + || strncmp("MGCP", (const char *)&msg->l3h[ptr[2].start], 4) != 0) { + LOGP(DMGCP, LOGL_ERROR, "Wrong MGCP version. Not handling: '%s' '%s'\n", + (const char *)&msg->l3h[ptr[3].start], + (const char *)&msg->l3h[ptr[2].start]); + return -1; + } + + *transaction_id = (const char *)&msg->l3h[ptr[0].start]; + if (endp) { + *endp = find_endpoint(cfg, (const char *)&msg->l3h[ptr[1].start]); + return *endp == NULL; + } + 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_config *cfg, struct msgb *msg) +{ + struct mgcp_msg_ptr data_ptrs[6]; + int found; + const char *trans_id; + struct mgcp_endpoint *endp; + + found = mgcp_analyze_header(cfg, msg, data_ptrs, ARRAY_SIZE(data_ptrs), &trans_id, &endp); + if (found != 0) + return create_err_response(500, "AUEP", trans_id); + else + return create_ok_response(200, "AUEP", trans_id); +} + +static int parse_conn_mode(const char *msg, int *conn_mode) +{ + int ret = 0; + if (strcmp(msg, "recvonly") == 0) + *conn_mode = MGCP_CONN_RECV_ONLY; + else if (strcmp(msg, "sendrecv") == 0) + *conn_mode = MGCP_CONN_RECV_SEND; + else if (strcmp(msg, "sendonly") == 0) + *conn_mode = MGCP_CONN_SEND_ONLY; + else if (strcmp(msg, "loopback") == 0) + *conn_mode = MGCP_CONN_LOOPBACK; + else { + LOGP(DMGCP, LOGL_ERROR, "Unknown connection mode: '%s'\n", msg); + ret = -1; + } + + 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->is_transcoded = 1; + } + + return 0; +} + +static struct msgb *handle_create_con(struct mgcp_config *cfg, struct msgb *msg) +{ + struct mgcp_msg_ptr data_ptrs[6]; + int found, i, line_start; + const char *trans_id; + struct mgcp_trunk_config *tcfg; + struct mgcp_endpoint *endp; + int error_code = 400; + + found = mgcp_analyze_header(cfg, msg, data_ptrs, ARRAY_SIZE(data_ptrs), &trans_id, &endp); + if (found != 0) + return create_err_response(510, "CRCX", trans_id); + + tcfg = endp->tcfg; + + if (endp->allocated) { + if (tcfg->force_realloc) { + LOGP(DMGCP, LOGL_NOTICE, "Endpoint 0x%x already allocated. Forcing realloc.\n", + ENDPOINT_NUMBER(endp)); + mgcp_free_endp(endp); + if (cfg->realloc_cb) + 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(400, "CRCX", trans_id); + } + } + + /* parse CallID C: and LocalParameters L: */ + MSG_TOKENIZE_START + switch (msg->l3h[line_start]) { + case 'L': + endp->local_options = talloc_strdup(tcfg->endpoints, + (const char *)&msg->l3h[line_start + 3]); + break; + case 'C': + endp->callid = talloc_strdup(tcfg->endpoints, + (const char *)&msg->l3h[line_start + 3]); + break; + case 'M': + if (parse_conn_mode((const char *)&msg->l3h[line_start + 3], + &endp->conn_mode) != 0) { + error_code = 517; + goto error2; + } + + endp->orig_mode = endp->conn_mode; + break; + default: + LOGP(DMGCP, LOGL_NOTICE, "Unhandled option: '%c'/%d on 0x%x\n", + msg->l3h[line_start], msg->l3h[line_start], + ENDPOINT_NUMBER(endp)); + break; + } + MSG_TOKENIZE_END + + /* initialize */ + endp->net_end.rtp_port = endp->net_end.rtcp_port = endp->bts_end.rtp_port = endp->bts_end.rtcp_port = 0; + + /* 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(cfg); + if (endp->ci == CI_UNUSED) + goto error2; + + endp->allocated = 1; + endp->bts_end.payload_type = tcfg->audio_payload; + + /* policy CB */ + if (cfg->policy_cb) { + switch (cfg->policy_cb(tcfg, ENDPOINT_NUMBER(endp), MGCP_ENDP_CRCX, trans_id)) { + case MGCP_POLICY_REJECT: + LOGP(DMGCP, LOGL_NOTICE, "CRCX rejected by policy on 0x%x\n", + ENDPOINT_NUMBER(endp)); + mgcp_free_endp(endp); + return create_err_response(400, "CRCX", trans_id); + 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 (cfg->change_cb) + cfg->change_cb(tcfg, ENDPOINT_NUMBER(endp), MGCP_ENDP_CRCX); + + create_transcoder(endp); + return create_response_with_sdp(endp, "CRCX", trans_id); +error: + LOGP(DMGCP, LOGL_ERROR, "Malformed line: %s on 0x%x with: line_start: %d %d\n", + hexdump(msg->l3h, msgb_l3len(msg)), + ENDPOINT_NUMBER(endp), line_start, i); + return create_err_response(error_code, "CRCX", trans_id); + +error2: + mgcp_free_endp(endp); + LOGP(DMGCP, LOGL_NOTICE, "Resource error on 0x%x\n", ENDPOINT_NUMBER(endp)); + return create_err_response(error_code, "CRCX", trans_id); +} + +static struct msgb *handle_modify_con(struct mgcp_config *cfg, struct msgb *msg) +{ + struct mgcp_msg_ptr data_ptrs[6]; + int found, i, line_start; + const char *trans_id; + struct mgcp_endpoint *endp; + int error_code = 500; + int silent = 0; + + found = mgcp_analyze_header(cfg, msg, data_ptrs, ARRAY_SIZE(data_ptrs), &trans_id, &endp); + if (found != 0) + return create_err_response(510, "MDCX", trans_id); + + 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(400, "MDCX", trans_id); + } + + MSG_TOKENIZE_START + switch (msg->l3h[line_start]) { + case 'C': { + if (verify_call_id(endp, (const char *)&msg->l3h[line_start + 3]) != 0) + goto error3; + break; + } + case 'I': { + if (verify_ci(endp, (const char *)&msg->l3h[line_start + 3]) != 0) + goto error3; + break; + } + case 'L': + /* skip */ + break; + case 'M': + if (parse_conn_mode((const char *)&msg->l3h[line_start + 3], + &endp->conn_mode) != 0) { + error_code = 517; + goto error3; + } + endp->orig_mode = endp->conn_mode; + break; + case 'Z': + silent = strcmp("noanswer", (const char *)&msg->l3h[line_start + 3]) == 0; + break; + case '\0': + /* SDP file begins */ + break; + case 'a': + case 'o': + case 's': + case 't': + case 'v': + /* skip these SDP attributes */ + break; + case 'm': { + int port; + int payload; + const char *param = (const char *)&msg->l3h[line_start]; + + if (sscanf(param, "m=audio %d RTP/AVP %d", &port, &payload) == 2) { + endp->net_end.rtp_port = htons(port); + endp->net_end.rtcp_port = htons(port + 1); + endp->net_end.payload_type = payload; + } + break; + } + case 'c': { + char ipv4[16]; + const char *param = (const char *)&msg->l3h[line_start]; + + if (sscanf(param, "c=IN IP4 %15s", ipv4) == 1) { + inet_aton(ipv4, &endp->net_end.addr); + } + break; + } + default: + LOGP(DMGCP, LOGL_NOTICE, "Unhandled option: '%c'/%d on 0x%x\n", + msg->l3h[line_start], msg->l3h[line_start], + ENDPOINT_NUMBER(endp)); + break; + } + MSG_TOKENIZE_END + + /* policy CB */ + if (cfg->policy_cb) { + switch (cfg->policy_cb(endp->tcfg, ENDPOINT_NUMBER(endp), MGCP_ENDP_MDCX, trans_id)) { + 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(400, "MDCX", trans_id); + break; + case MGCP_POLICY_DEFER: + /* stop processing */ + return NULL; + break; + case MGCP_POLICY_CONT: + /* just continue */ + break; + } + } + + /* 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 (cfg->change_cb) + cfg->change_cb(endp->tcfg, ENDPOINT_NUMBER(endp), MGCP_ENDP_MDCX); + if (silent) + goto out_silent; + + return create_response_with_sdp(endp, "MDCX", trans_id); + +error: + LOGP(DMGCP, LOGL_ERROR, "Malformed line: %s on 0x%x with: line_start: %d %d %d\n", + hexdump(msg->l3h, msgb_l3len(msg)), + ENDPOINT_NUMBER(endp), line_start, i, msg->l3h[line_start]); + return create_err_response(error_code, "MDCX", trans_id); + +error3: + return create_err_response(error_code, "MDCX", trans_id); + + +out_silent: + return NULL; +} + +static struct msgb *handle_delete_con(struct mgcp_config *cfg, struct msgb *msg) +{ + struct mgcp_msg_ptr data_ptrs[6]; + int found, i, line_start; + const char *trans_id; + struct mgcp_endpoint *endp; + int error_code = 400; + int silent = 0; + + found = mgcp_analyze_header(cfg, msg, data_ptrs, ARRAY_SIZE(data_ptrs), &trans_id, &endp); + if (found != 0) + return create_err_response(error_code, "DLCX", trans_id); + + if (!endp->allocated) { + LOGP(DMGCP, LOGL_ERROR, "Endpoint is not used. 0x%x\n", ENDPOINT_NUMBER(endp)); + return create_err_response(400, "DLCX", trans_id); + } + + MSG_TOKENIZE_START + switch (msg->l3h[line_start]) { + case 'C': { + if (verify_call_id(endp, (const char *)&msg->l3h[line_start + 3]) != 0) + goto error3; + break; + } + case 'I': { + if (verify_ci(endp, (const char *)&msg->l3h[line_start + 3]) != 0) + goto error3; + break; + case 'Z': + silent = strcmp("noanswer", (const char *)&msg->l3h[line_start + 3]) == 0; + break; + } + default: + LOGP(DMGCP, LOGL_NOTICE, "Unhandled option: '%c'/%d on 0x%x\n", + msg->l3h[line_start], msg->l3h[line_start], + ENDPOINT_NUMBER(endp)); + break; + } + MSG_TOKENIZE_END + + /* policy CB */ + if (cfg->policy_cb) { + switch (cfg->policy_cb(endp->tcfg, ENDPOINT_NUMBER(endp), MGCP_ENDP_DLCX, trans_id)) { + 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(400, "DLCX", trans_id); + 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)); + + delete_transcoder(endp); + mgcp_free_endp(endp); + if (cfg->change_cb) + cfg->change_cb(endp->tcfg, ENDPOINT_NUMBER(endp), MGCP_ENDP_DLCX); + + if (silent) + goto out_silent; + return create_ok_response(250, "DLCX", trans_id); + +error: + LOGP(DMGCP, LOGL_ERROR, "Malformed line: %s on 0x%x with: line_start: %d %d\n", + hexdump(msg->l3h, msgb_l3len(msg)), + ENDPOINT_NUMBER(endp), line_start, i); + return create_err_response(error_code, "DLCX", trans_id); + +error3: + return create_err_response(error_code, "DLCX", trans_id); + +out_silent: + return NULL; +} + +static struct msgb *handle_rsip(struct mgcp_config *cfg, struct msgb *msg) +{ + if (cfg->reset_cb) + cfg->reset_cb(cfg); + return NULL; +} + +/* + * 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_config *cfg, struct msgb *msg) +{ + struct mgcp_msg_ptr data_ptrs[6]; + const char *trans_id; + struct mgcp_endpoint *endp; + int found; + + found = mgcp_analyze_header(cfg, msg, data_ptrs, ARRAY_SIZE(data_ptrs), &trans_id, &endp); + if (found != 0) + return create_err_response(400, "RQNT", trans_id); + + if (!endp->allocated) { + LOGP(DMGCP, LOGL_ERROR, "Endpoint is not used. 0x%x\n", ENDPOINT_NUMBER(endp)); + return create_err_response(400, "RQNT", trans_id); + } + return create_ok_response(200, "RQNT", trans_id); +} + +struct mgcp_config *mgcp_config_alloc(void) +{ + struct mgcp_config *cfg; + + cfg = talloc_zero(NULL, struct mgcp_config); + if (!cfg) { + LOGP(DMGCP, LOGL_FATAL, "Failed to allocate config.\n"); + return NULL; + } + + cfg->source_port = 2427; + cfg->source_addr = talloc_strdup(cfg, "0.0.0.0"); + + cfg->transcoder_remote_base = 4000; + + cfg->bts_ports.base_port = RTP_PORT_DEFAULT; + cfg->net_ports.base_port = RTP_PORT_NET_DEFAULT; + + /* 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; + + 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->number_endpoints = 33; + 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_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; + memset(&end->addr, 0, sizeof(end->addr)); + end->rtp_port = end->rtcp_port = 0; + end->payload_type = -1; + end->local_alloc = -1; +} + +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].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_free_endp(struct mgcp_endpoint *endp) +{ + LOGP(DMGCP, LOGL_DEBUG, "Deleting endpoint on: 0x%x\n", ENDPOINT_NUMBER(endp)); + endp->ci = CI_UNUSED; + endp->allocated = 0; + + if (endp->callid) { + talloc_free(endp->callid); + endp->callid = NULL; + } + + if (endp->local_options) { + talloc_free(endp->local_options); + endp->local_options = 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->is_transcoded = 0; + + 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; + endp->allow_patch = 0; + + memset(&endp->taps, 0, sizeof(endp->taps)); +} + +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; + + /* 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" + "c=IN IP4 %s\r\n" + "m=audio %d RTP/AVP %d\r\n" + "a=rtpmap:%d %s\r\n", + msg, endpoint, mode, endp->cfg->source_addr, + port, endp->tcfg->audio_payload, + endp->tcfg->audio_payload, endp->tcfg->audio_name); + + if (len < 0) + return; + + 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 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->is_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->is_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); +} diff --git a/openbsc/src/libmgcp/mgcp_vty.c b/openbsc/src/libmgcp/mgcp_vty.c new file mode 100644 index 000000000..c299a98cc --- /dev/null +++ b/openbsc/src/libmgcp/mgcp_vty.c @@ -0,0 +1,742 @@ +/* A Media Gateway Control Protocol Media Gateway: RFC 3435 */ +/* The protocol implementation */ + +/* + * (C) 2009-2011 by Holger Hans Peter Freyther + * (C) 2009-2011 by On-Waves + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + * + */ + +#include + +#include + +#include +#include +#include +#include + +#include +#include + +#include + +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(mgcp)#", + 1, +}; + +struct cmd_node trunk_node = { + TRUNK_NODE, + "%s(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->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); + + vty_out(vty, " rtp ip-dscp %d%s", g_cfg->endp_dscp, 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); + 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); + 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); + vty_out(vty, " transcoder-remote-base %u%s", g_cfg->transcoder_remote_base, VTY_NEWLINE); + + return CMD_SUCCESS; +} + +static void dump_trunk(struct vty *vty, struct mgcp_trunk_config *cfg) +{ + 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/%u remote: %u/%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->bts_state.lost_no, + endp->net_end.packets, endp->net_state.lost_no, + endp->trans_net.packets, endp->trans_bts.packets, + VTY_NEWLINE); + } +} + +DEFUN(show_mcgp, show_mgcp_cmd, "show mgcp", + SHOW_STR "Display information about the MGCP Media Gateway") +{ + struct mgcp_trunk_config *trunk; + + dump_trunk(vty, &g_cfg->trunk); + + llist_for_each_entry(trunk, &g_cfg->trunks, entry) + dump_trunk(vty, trunk); + + 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", + "Set the IP to be used in SDP records") +{ + bsc_replace_string(g_cfg, &g_cfg->local_ip, argv[0]); + return CMD_SUCCESS; +} + +DEFUN(cfg_mgcp_bts_ip, + cfg_mgcp_bts_ip_cmd, + "bts ip A.B.C.D", + "Set the IP of the BTS for RTP forwarding") +{ + bsc_replace_string(g_cfg, &g_cfg->bts_ip, argv[0]); + inet_aton(g_cfg->bts_ip, &g_cfg->bts_in); + return CMD_SUCCESS; +} + +DEFUN(cfg_mgcp_bind_ip, + cfg_mgcp_bind_ip_cmd, + "bind ip A.B.C.D", + "Bind the MGCP to this local addr") +{ + bsc_replace_string(g_cfg, &g_cfg->source_addr, argv[0]); + return CMD_SUCCESS; +} + +DEFUN(cfg_mgcp_bind_port, + cfg_mgcp_bind_port_cmd, + "bind port <0-65534>", + "Bind the MGCP to this port") +{ + 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 all RTP ports early") +{ + 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; +} + + +DEFUN(cfg_mgcp_rtp_bts_base_port, + cfg_mgcp_rtp_bts_base_port_cmd, + "rtp bts-base <0-65534>", + "Base port to use") +{ + parse_base(&g_cfg->bts_ports, argv); + return CMD_SUCCESS; +} + +DEFUN(cfg_mgcp_rtp_bts_range, + cfg_mgcp_rtp_bts_range_cmd, + "rtp bts-range <0-65534> <0-65534>", + "Range of ports to allocate for endpoints\n" + "Start of the range of ports\n" "End of the range of ports\n") +{ + 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>", + "Range of ports to allocate for endpoints\n" + "Start of the range of ports\n" "End of the range of ports\n") +{ + 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>", + "Base port to use for network port\n" "Port\n") +{ + 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>", "Base port to use") + +DEFUN(cfg_mgcp_rtp_transcoder_range, + cfg_mgcp_rtp_transcoder_range_cmd, + "rtp transcoder-range <0-65534> <0-65534>", + "Range of ports to allocate for the transcoder\n" + "Start of the range of ports\n" "End of the range of ports\n") +{ + 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>", + "Base port for the transcoder range\n" "Port\n") +{ + parse_base(&g_cfg->transcoder_ports, argv); + return CMD_SUCCESS; +} + +DEFUN(cfg_mgcp_rtp_ip_dscp, + cfg_mgcp_rtp_ip_dscp_cmd, + "rtp ip-dscp <0-255>", + "Set the IP_TOS socket attribute on the RTP/RTCP sockets.\n" "The DSCP value.") +{ + 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>", + "Set the IP_TOS socket attribute on the RTP/RTCP sockets.\n" "The DSCP value.") + + +DEFUN(cfg_mgcp_sdp_payload_number, + cfg_mgcp_sdp_payload_number_cmd, + "sdp audio payload number <1-255>", + "Set the audio codec to use") +{ + unsigned int payload = atoi(argv[0]); + g_cfg->trunk.audio_payload = payload; + return CMD_SUCCESS; +} + +DEFUN(cfg_mgcp_sdp_payload_name, + cfg_mgcp_sdp_payload_name_cmd, + "sdp audio payload name NAME", + "Set the audio name to use") +{ + bsc_replace_string(g_cfg, &g_cfg->trunk.audio_name, argv[0]); + return CMD_SUCCESS; +} + +DEFUN(cfg_mgcp_loop, + cfg_mgcp_loop_cmd, + "loop (0|1)", + "Loop the audio") +{ + 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>", + "The number of endpoints to allocate. This is not dynamic.") +{ + /* + 1 as we start counting at one */ + g_cfg->trunk.number_endpoints = atoi(argv[0]) + 1; + return CMD_SUCCESS; +} + +DEFUN(cfg_mgcp_agent_addr, + cfg_mgcp_agent_addr_cmd, + "call agent ip IP", + "Set the address of the call agent.") +{ + bsc_replace_string(g_cfg, &g_cfg->call_agent_addr, argv[0]); + return CMD_SUCCESS; +} + +DEFUN(cfg_mgcp_transcoder, + cfg_mgcp_transcoder_cmd, + "transcoder-mgw A.B.C.D", + "Use a MGW to detranscoder RTP\n" + "The IP address of the MGW") +{ + bsc_replace_string(g_cfg, &g_cfg->transcoder_ip, argv[0]); + inet_aton(g_cfg->transcoder_ip, &g_cfg->transcoder_in); + + return CMD_SUCCESS; +} + +DEFUN(cfg_mgcp_no_transcoder, + cfg_mgcp_no_transcoder_cmd, + NO_STR "transcoder-mgw", + "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, " loop %d%s", + trunk->audio_loop, VTY_NEWLINE); + } + + return CMD_SUCCESS; +} + +DEFUN(cfg_trunk_payload_number, + cfg_trunk_payload_number_cmd, + "sdp audio payload number <1-255>", + "SDP related\n" "Audio\n" "Payload\n" "Payload Number\n") +{ + struct mgcp_trunk_config *trunk = vty->index; + unsigned int payload = atoi(argv[0]); + + trunk->audio_payload = payload; + return CMD_SUCCESS; +} + +DEFUN(cfg_trunk_payload_name, + cfg_trunk_payload_name_cmd, + "sdp audio payload name NAME", + "SDP related\n" "Audio\n" "Payload\n" "Payload Name\n") +{ + struct mgcp_trunk_config *trunk = vty->index; + + bsc_replace_string(g_cfg, &trunk->audio_name, argv[0]); + return CMD_SUCCESS; +} + +DEFUN(cfg_trunk_loop, + cfg_trunk_loop_cmd, + "loop (0|1)", + "Loop the audio") +{ + struct mgcp_trunk_config *trunk = vty->index; + + trunk->audio_loop = atoi(argv[0]); + 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; + endp->allow_patch = 1; + + 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_free_endp(endp); + 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(CONFIG_NODE, &cfg_mgcp_cmd); + install_node(&mgcp_node, config_write_mgcp); + + install_default(MGCP_NODE); + install_element(MGCP_NODE, &ournode_exit_cmd); + install_element(MGCP_NODE, &ournode_end_cmd); + 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_net_range_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_agent_addr_cmd); + 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_loop_cmd); + install_element(MGCP_NODE, &cfg_mgcp_number_endp_cmd); + + install_element(MGCP_NODE, &cfg_mgcp_trunk_cmd); + install_node(&trunk_node, config_write_trunk); + install_default(TRUNK_NODE); + install_element(TRUNK_NODE, &ournode_exit_cmd); + install_element(TRUNK_NODE, &ournode_end_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_loop_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) +{ + int rc; + struct mgcp_trunk_config *trunk; + + 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; + } + } + + return 0; +} + diff --git a/openbsc/src/libmsc/Makefile.am b/openbsc/src/libmsc/Makefile.am new file mode 100644 index 000000000..7d895c394 --- /dev/null +++ b/openbsc/src/libmsc/Makefile.am @@ -0,0 +1,19 @@ +INCLUDES = $(all_includes) -I$(top_srcdir)/include -I$(top_builddir) +AM_CFLAGS=-Wall $(LIBOSMOCORE_CFLAGS) $(LIBOSMOVTY_CFLAGS) $(COVERAGE_CFLAGS) +AM_LDFLAGS = $(LIBOSMOCORE_LIBS) $(COVERAGE_LDFLAGS) + +noinst_LIBRARIES = libmsc.a + +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 \ + osmo_msc.c + diff --git a/openbsc/src/libmsc/auth.c b/openbsc/src/libmsc/auth.c new file mode 100644 index 000000000..e09bde5f3 --- /dev/null +++ b/openbsc/src/libmsc/auth.c @@ -0,0 +1,132 @@ +/* Authentication related functions */ + +/* + * (C) 2010 by Sylvain Munaut + * + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + * + */ + +#include +#include +#include +#include + +#include + +#include + + +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, + hexdump(ainfo->a3a8_ki, ainfo->a3a8_ki_len)); + return -1; + } + + for (i=0; i<4; i++) + atuple->sres[i] = atuple->rand[i] ^ ainfo->a3a8_ki[i]; + for (i=4; i<12; i++) + atuple->kc[i-4] = atuple->rand[i] ^ ainfo->a3a8_ki[i]; + + return 0; +} + +static int +_use_comp128_v1(struct gsm_auth_info *ainfo, struct gsm_auth_tuple *atuple) +{ + if (ainfo->a3a8_ki_len != A38_COMP128_KEY_LEN) { + LOGP(DMM, LOGL_ERROR, "Invalid COMP128v1 key (len=%d) %s\n", + ainfo->a3a8_ki_len, + hexdump(ainfo->a3a8_ki, ainfo->a3a8_ki_len)); + return -1; + } + + comp128(ainfo->a3a8_ki, atuple->rand, atuple->sres, atuple->kc); + + return 0; +} + +/* Return values + * -1 -> Internal error + * 0 -> Not available + * 1 -> Tuple returned, need to do auth, then enable cipher + * 2 -> Tuple returned, need to enable cipher + */ +int auth_get_tuple_for_subscr(struct gsm_auth_tuple *atuple, + struct gsm_subscriber *subscr, int key_seq) +{ + struct gsm_auth_info ainfo; + int i, rc; + + /* Get subscriber info (if any) */ + rc = db_get_authinfo_for_subscr(&ainfo, subscr); + if (rc < 0) { + LOGP(DMM, LOGL_NOTICE, + "No retrievable Ki for subscriber, skipping auth\n"); + return rc == -ENOENT ? AUTH_NOT_AVAIL : -1; + } + + /* If possible, re-use the last tuple and skip auth */ + rc = db_get_lastauthtuple_for_subscr(atuple, subscr); + if ((rc == 0) && + (key_seq != GSM_KEY_SEQ_INVAL) && + (atuple->use_count < 3)) + { + atuple->use_count++; + db_sync_lastauthtuple_for_subscr(atuple, subscr); + DEBUGP(DMM, "Auth tuple use < 3, just doing ciphering\n"); + return AUTH_DO_CIPH; + } + + /* Generate a new one */ + atuple->use_count = 1; + atuple->key_seq = (atuple->key_seq + 1) % 7; + for (i=0; irand); i++) + atuple->rand[i] = random() & 0xff; + + switch (ainfo.auth_algo) { + case AUTH_ALGO_NONE: + DEBUGP(DMM, "No authentication for subscriber\n"); + return 0; + + case AUTH_ALGO_XOR: + if (_use_xor(&ainfo, atuple)) + return 0; + break; + + case AUTH_ALGO_COMP128v1: + if (_use_comp128_v1(&ainfo, atuple)) + return 0; + break; + + default: + DEBUGP(DMM, "Unsupported auth type algo_id=%d\n", + ainfo.auth_algo); + return 0; + } + + db_sync_lastauthtuple_for_subscr(atuple, subscr); + + DEBUGP(DMM, "Need to do authentication and ciphering\n"); + return AUTH_DO_AUTH_THAN_CIPH; +} + diff --git a/openbsc/src/libmsc/db.c b/openbsc/src/libmsc/db.c new file mode 100644 index 000000000..95a7d36fb --- /dev/null +++ b/openbsc/src/libmsc/db.c @@ -0,0 +1,1303 @@ +/* Simple HLR/VLR database backend using dbi */ +/* (C) 2008 by Jan Luebbe + * (C) 2009 by Holger Hans Peter Freyther + * (C) 2009 by Harald Welte + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include +#include +#include + +static char *db_basename = NULL; +static char *db_dirname = NULL; +static dbi_conn conn; + +static char *create_stmts[] = { + "CREATE TABLE IF NOT EXISTS Meta (" + "id INTEGER PRIMARY KEY AUTOINCREMENT, " + "key TEXT UNIQUE NOT NULL, " + "value TEXT NOT NULL" + ")", + "INSERT OR IGNORE INTO Meta " + "(key, value) " + "VALUES " + "('revision', '2')", + "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" + ")", + "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" + ")", + "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" + ")", + "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) " + ")", + "CREATE TABLE IF NOT EXISTS SMS (" + /* metadata, not part of sms */ + "id INTEGER PRIMARY KEY AUTOINCREMENT, " + "created TIMESTAMP NOT NULL, " + "sent TIMESTAMP, " + "sender_id INTEGER NOT NULL, " + "receiver_id INTEGER NOT NULL, " + "deliver_attempts INTEGER NOT NULL DEFAULT 0, " + /* 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, " + "dest_addr TEXT, " + "user_data BLOB, " /* TP-UD */ + /* additional data, interpreted from SMS */ + "header BLOB, " /* UD Header */ + "text TEXT " /* decoded UD after UDH */ + ")", + "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 " + ")", + "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 " + ")", + "CREATE TABLE IF NOT EXISTS Counters (" + "id INTEGER PRIMARY KEY AUTOINCREMENT, " + "timestamp TIMESTAMP NOT NULL, " + "value INTEGER NOT NULL, " + "name TEXT NOT NULL " + ")", + "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 " + ")", + "CREATE TABLE IF NOT EXISTS AuthKeys (" + "subscriber_id INTEGER PRIMARY KEY, " + "algorithm_id INTEGER NOT NULL, " + "a3a8_ki BLOB " + ")", + "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); +} + +static int check_db_revision(void) +{ + dbi_result result; + const char *rev; + + result = dbi_conn_query(conn, + "SELECT value FROM Meta WHERE key='revision'"); + if (!result) + return -EINVAL; + + if (!dbi_result_next_row(result)) { + dbi_result_free(result); + return -EINVAL; + } + rev = dbi_result_get_string(result, "value"); + if (!rev || atoi(rev) != 2) { + dbi_result_free(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() +{ + 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; + } + + return 0; +} + +int db_fini() +{ + dbi_conn_close(conn); + dbi_shutdown(); + + if (db_dirname) + free(db_dirname); + if (db_basename) + free(db_basename); + return 0; +} + +struct gsm_subscriber *db_create_subscriber(struct gsm_network *net, char *imsi) +{ + dbi_result result; + struct gsm_subscriber *subscr; + + /* Is this subscriber known in the db? */ + subscr = db_get_subscriber(net, GSM_SUBSCRIBER_IMSI, imsi); + if (subscr) { + result = dbi_conn_queryf(conn, + "UPDATE Subscriber set updated = datetime('now') " + "WHERE imsi = %s " , imsi); + if (!result) + LOGP(DDB, LOGL_ERROR, "failed to update timestamp\n"); + else + dbi_result_free(result); + return subscr; + } + + subscr = subscr_alloc(); + subscr->flags |= GSM_SUBSCRIBER_FIRST_CONTACT; + if (!subscr) + return NULL; + 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->net = net; + subscr->id = dbi_conn_sequence_last(conn, NULL); + strncpy(subscr->imsi, imsi, GSM_IMSI_LENGTH-1); + dbi_result_free(result); + LOGP(DDB, LOGL_INFO, "New Subscriber: ID %llu, IMSI %s\n", subscr->id, subscr->imsi); + db_subscriber_alloc_exten(subscr); + return subscr; +} + +static_assert(sizeof(unsigned char) == sizeof(struct gsm48_classmark1), classmark1_size); + +static int get_equipment_by_subscr(struct gsm_subscriber *subscr) +{ + dbi_result result; + const char *string; + unsigned char cm1; + const unsigned char *cm2, *cm3; + struct gsm_equipment *equip = &subscr->equipment; + + result = dbi_conn_queryf(conn, + "SELECT Equipment.* " + "FROM Equipment JOIN EquipmentWatch ON " + "EquipmentWatch.equipment_id=Equipment.id " + "WHERE EquipmentWatch.subscriber_id = %llu " + "ORDER BY EquipmentWatch.updated DESC", subscr->id); + if (!result) + return -EIO; + + if (!dbi_result_next_row(result)) { + dbi_result_free(result); + return -ENOENT; + } + + equip->id = dbi_result_get_ulonglong(result, "id"); + + string = dbi_result_get_string(result, "imei"); + if (string) + strncpy(equip->imei, string, sizeof(equip->imei)); + + string = dbi_result_get_string(result, "classmark1"); + if (string) { + cm1 = atoi(string) & 0xff; + memcpy(&equip->classmark1, &cm1, sizeof(equip->classmark1)); + } + + equip->classmark2_len = dbi_result_get_field_length(result, "classmark2"); + cm2 = dbi_result_get_binary(result, "classmark2"); + if (equip->classmark2_len > sizeof(equip->classmark2)) + equip->classmark2_len = sizeof(equip->classmark2); + memcpy(equip->classmark2, cm2, equip->classmark2_len); + + equip->classmark3_len = dbi_result_get_field_length(result, "classmark3"); + cm3 = dbi_result_get_binary(result, "classmark3"); + if (equip->classmark3_len > sizeof(equip->classmark3)) + equip->classmark3_len = sizeof(equip->classmark3); + memcpy(equip->classmark3, cm3, equip->classmark3_len); + + dbi_result_free(result); + + return 0; +} + +int db_get_authinfo_for_subscr(struct gsm_auth_info *ainfo, + struct gsm_subscriber *subscr) +{ + dbi_result result; + const unsigned char *a3a8_ki; + + result = dbi_conn_queryf(conn, + "SELECT * FROM AuthKeys WHERE subscriber_id=%llu", + subscr->id); + if (!result) + return -EIO; + + if (!dbi_result_next_row(result)) { + dbi_result_free(result); + return -ENOENT; + } + + ainfo->auth_algo = dbi_result_get_ulonglong(result, "algorithm_id"); + ainfo->a3a8_ki_len = dbi_result_get_field_length(result, "a3a8_ki"); + a3a8_ki = dbi_result_get_binary(result, "a3a8_ki"); + if (ainfo->a3a8_ki_len > sizeof(ainfo->a3a8_ki)) + ainfo->a3a8_ki_len = sizeof(ainfo->a3a8_ki_len); + memcpy(ainfo->a3a8_ki, a3a8_ki, ainfo->a3a8_ki_len); + + dbi_result_free(result); + + return 0; +} + +int db_sync_authinfo_for_subscr(struct gsm_auth_info *ainfo, + struct gsm_subscriber *subscr) +{ + dbi_result result; + struct gsm_auth_info ainfo_old; + int rc, upd; + unsigned char *ki_str; + + /* Deletion ? */ + if (ainfo == NULL) { + result = dbi_conn_queryf(conn, + "DELETE FROM AuthKeys WHERE subscriber_id=%llu", + subscr->id); + + if (!result) + return -EIO; + + dbi_result_free(result); + + return 0; + } + + /* Check if already existing */ + rc = db_get_authinfo_for_subscr(&ainfo_old, subscr); + if (rc && rc != -ENOENT) + return rc; + upd = rc ? 0 : 1; + + /* Update / Insert */ + dbi_conn_quote_binary_copy(conn, + ainfo->a3a8_ki, ainfo->a3a8_ki_len, &ki_str); + + if (!upd) { + result = dbi_conn_queryf(conn, + "INSERT INTO AuthKeys " + "(subscriber_id, algorithm_id, a3a8_ki) " + "VALUES (%llu, %u, %s)", + subscr->id, ainfo->auth_algo, ki_str); + } else { + result = dbi_conn_queryf(conn, + "UPDATE AuthKeys " + "SET algorithm_id=%u, a3a8_ki=%s " + "WHERE subscriber_id=%llu", + ainfo->auth_algo, ki_str, subscr->id); + } + + free(ki_str); + + if (!result) + return -EIO; + + dbi_result_free(result); + + return 0; +} + +int db_get_lastauthtuple_for_subscr(struct gsm_auth_tuple *atuple, + struct gsm_subscriber *subscr) +{ + dbi_result result; + int len; + const unsigned char *blob; + + result = dbi_conn_queryf(conn, + "SELECT * FROM AuthLastTuples WHERE subscriber_id=%llu", + subscr->id); + if (!result) + return -EIO; + + if (!dbi_result_next_row(result)) { + dbi_result_free(result); + return -ENOENT; + } + + memset(atuple, 0, sizeof(atuple)); + + atuple->use_count = dbi_result_get_ulonglong(result, "use_count"); + atuple->key_seq = dbi_result_get_ulonglong(result, "key_seq"); + + len = dbi_result_get_field_length(result, "rand"); + if (len != sizeof(atuple->rand)) + goto err_size; + + blob = dbi_result_get_binary(result, "rand"); + memcpy(atuple->rand, blob, len); + + len = dbi_result_get_field_length(result, "sres"); + if (len != sizeof(atuple->sres)) + goto err_size; + + blob = dbi_result_get_binary(result, "sres"); + memcpy(atuple->sres, blob, len); + + len = dbi_result_get_field_length(result, "kc"); + if (len != sizeof(atuple->kc)) + goto err_size; + + blob = dbi_result_get_binary(result, "kc"); + memcpy(atuple->kc, blob, len); + + dbi_result_free(result); + + return 0; + +err_size: + dbi_result_free(result); + return -EIO; +} + +int db_sync_lastauthtuple_for_subscr(struct gsm_auth_tuple *atuple, + struct gsm_subscriber *subscr) +{ + dbi_result result; + int rc, upd; + struct gsm_auth_tuple atuple_old; + unsigned char *rand_str, *sres_str, *kc_str; + + /* Deletion ? */ + if (atuple == NULL) { + result = dbi_conn_queryf(conn, + "DELETE FROM AuthLastTuples WHERE subscriber_id=%llu", + subscr->id); + + if (!result) + return -EIO; + + dbi_result_free(result); + + return 0; + } + + /* Check if already existing */ + rc = db_get_lastauthtuple_for_subscr(&atuple_old, subscr); + if (rc && rc != -ENOENT) + return rc; + upd = rc ? 0 : 1; + + /* Update / Insert */ + dbi_conn_quote_binary_copy(conn, + atuple->rand, sizeof(atuple->rand), &rand_str); + dbi_conn_quote_binary_copy(conn, + atuple->sres, sizeof(atuple->sres), &sres_str); + dbi_conn_quote_binary_copy(conn, + atuple->kc, sizeof(atuple->kc), &kc_str); + + if (!upd) { + result = dbi_conn_queryf(conn, + "INSERT INTO AuthLastTuples " + "(subscriber_id, issued, use_count, " + "key_seq, rand, sres, kc) " + "VALUES (%llu, datetime('now'), %u, " + "%u, %s, %s, %s ) ", + subscr->id, atuple->use_count, atuple->key_seq, + rand_str, sres_str, kc_str); + } else { + char *issued = atuple->key_seq == atuple_old.key_seq ? + "issued" : "datetime('now')"; + result = dbi_conn_queryf(conn, + "UPDATE AuthLastTuples " + "SET issued=%s, use_count=%u, " + "key_seq=%u, rand=%s, sres=%s, kc=%s " + "WHERE subscriber_id = %llu", + issued, atuple->use_count, atuple->key_seq, + rand_str, sres_str, kc_str, subscr->id); + } + + free(rand_str); + free(sres_str); + free(kc_str); + + if (!result) + return -EIO; + + dbi_result_free(result); + + return 0; +} + +static void db_set_from_query(struct gsm_subscriber *subscr, dbi_conn result) +{ + const char *string; + string = dbi_result_get_string(result, "imsi"); + if (string) + strncpy(subscr->imsi, string, GSM_IMSI_LENGTH); + + string = dbi_result_get_string(result, "tmsi"); + if (string) + subscr->tmsi = tmsi_from_string(string); + + string = dbi_result_get_string(result, "name"); + if (string) + strncpy(subscr->name, string, GSM_NAME_LENGTH); + + string = dbi_result_get_string(result, "extension"); + if (string) + strncpy(subscr->extension, string, GSM_EXTENSION_LENGTH); + + subscr->lac = dbi_result_get_uint(result, "lac"); + subscr->authorized = dbi_result_get_uint(result, "authorized"); +} + +#define BASE_QUERY "SELECT * FROM Subscriber " +struct gsm_subscriber *db_get_subscriber(struct gsm_network *net, + enum gsm_subscriber_field field, + const char *id) +{ + dbi_result result; + char *quoted; + struct gsm_subscriber *subscr; + + switch (field) { + case GSM_SUBSCRIBER_IMSI: + dbi_conn_quote_string_copy(conn, id, "ed); + result = dbi_conn_queryf(conn, + BASE_QUERY + "WHERE imsi = %s ", + quoted + ); + free(quoted); + break; + case GSM_SUBSCRIBER_TMSI: + dbi_conn_quote_string_copy(conn, id, "ed); + result = dbi_conn_queryf(conn, + BASE_QUERY + "WHERE tmsi = %s ", + quoted + ); + free(quoted); + break; + case GSM_SUBSCRIBER_EXTENSION: + dbi_conn_quote_string_copy(conn, id, "ed); + result = dbi_conn_queryf(conn, + BASE_QUERY + "WHERE extension = %s ", + quoted + ); + free(quoted); + break; + case GSM_SUBSCRIBER_ID: + dbi_conn_quote_string_copy(conn, id, "ed); + result = dbi_conn_queryf(conn, + BASE_QUERY + "WHERE id = %s ", quoted); + free(quoted); + break; + default: + LOGP(DDB, LOGL_NOTICE, "Unknown query selector for Subscriber.\n"); + return NULL; + } + if (!result) { + LOGP(DDB, LOGL_ERROR, "Failed to query Subscriber.\n"); + return NULL; + } + if (!dbi_result_next_row(result)) { + DEBUGP(DDB, "Failed to find the Subscriber. '%u' '%s'\n", + field, id); + dbi_result_free(result); + return NULL; + } + + subscr = subscr_alloc(); + subscr->net = net; + subscr->id = dbi_result_get_ulonglong(result, "id"); + + db_set_from_query(subscr, result); + DEBUGP(DDB, "Found Subscriber: ID %llu, IMSI %s, NAME '%s', TMSI %u, EXTEN '%s', LAC %hu, AUTH %u\n", + subscr->id, subscr->imsi, subscr->name, subscr->tmsi, subscr->extension, + subscr->lac, subscr->authorized); + dbi_result_free(result); + + get_equipment_by_subscr(subscr); + + return subscr; +} + +int db_subscriber_update(struct gsm_subscriber *subscr) +{ + char buf[32]; + dbi_result result; + + /* Copy the id to a string as queryf with %llu is failing */ + sprintf(buf, "%llu", subscr->id); + result = dbi_conn_queryf(conn, + BASE_QUERY + "WHERE id = %s", buf); + + if (!result) { + LOGP(DDB, LOGL_ERROR, "Failed to query Subscriber: %llu\n", subscr->id); + return -EIO; + } + if (!dbi_result_next_row(result)) { + DEBUGP(DDB, "Failed to find the Subscriber. %llu\n", + subscr->id); + dbi_result_free(result); + return -EIO; + } + + db_set_from_query(subscr, result); + dbi_result_free(result); + get_equipment_by_subscr(subscr); + + return 0; +} + +int db_sync_subscriber(struct gsm_subscriber *subscriber) +{ + dbi_result result; + char tmsi[14]; + char *q_tmsi, *q_name, *q_extension; + + dbi_conn_quote_string_copy(conn, + subscriber->name, &q_name); + dbi_conn_quote_string_copy(conn, + subscriber->extension, &q_extension); + + if (subscriber->tmsi != GSM_RESERVED_TMSI) { + sprintf(tmsi, "%u", subscriber->tmsi); + dbi_conn_quote_string_copy(conn, + tmsi, + &q_tmsi); + } else + q_tmsi = strdup("NULL"); + + result = dbi_conn_queryf(conn, + "UPDATE Subscriber " + "SET updated = datetime('now'), " + "name = %s, " + "extension = %s, " + "authorized = %i, " + "tmsi = %s, " + "lac = %i " + "WHERE imsi = %s ", + q_name, + q_extension, + subscriber->authorized, + q_tmsi, + subscriber->lac, + 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_sync_equipment(struct gsm_equipment *equip) +{ + dbi_result result; + unsigned char *cm2, *cm3; + char *q_imei; + u_int8_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", + hexdump(equip->classmark2, equip->classmark2_len)); + if (equip->classmark3_len) + DEBUGPC(DDB, ", classmark3=%s", + 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_alloc_tmsi(struct gsm_subscriber *subscriber) +{ + dbi_result result = NULL; + char tmsi[14]; + char *tmsi_quoted; + + for (;;) { + subscriber->tmsi = rand(); + if (subscriber->tmsi == GSM_RESERVED_TMSI) + continue; + + sprintf(tmsi, "%u", subscriber->tmsi); + dbi_conn_quote_string_copy(conn, tmsi, &tmsi_quoted); + result = dbi_conn_queryf(conn, + "SELECT * FROM Subscriber " + "WHERE tmsi = %s ", + tmsi_quoted); + + free(tmsi_quoted); + + if (!result) { + LOGP(DDB, LOGL_ERROR, "Failed to query Subscriber " + "while allocating new TMSI.\n"); + return 1; + } + if (dbi_result_get_numrows(result)) { + dbi_result_free(result); + continue; + } + if (!dbi_result_next_row(result)) { + dbi_result_free(result); + DEBUGP(DDB, "Allocated TMSI %u for IMSI %s.\n", + subscriber->tmsi, subscriber->imsi); + return db_sync_subscriber(subscriber); + } + dbi_result_free(result); + } + return 0; +} + +int db_subscriber_alloc_exten(struct gsm_subscriber *subscriber) +{ + dbi_result result = NULL; + u_int32_t try; + + for (;;) { + try = (rand()%(GSM_MAX_EXTEN-GSM_MIN_EXTEN+1)+GSM_MIN_EXTEN); + result = dbi_conn_queryf(conn, + "SELECT * FROM Subscriber " + "WHERE extension = %i", + try + ); + if (!result) { + LOGP(DDB, LOGL_ERROR, "Failed to query Subscriber " + "while allocating new extension.\n"); + return 1; + } + if (dbi_result_get_numrows(result)){ + dbi_result_free(result); + continue; + } + if (!dbi_result_next_row(result)) { + dbi_result_free(result); + break; + } + dbi_result_free(result); + } + sprintf(subscriber->extension, "%i", try); + DEBUGP(DDB, "Allocated extension %i for IMSI %s.\n", try, subscriber->imsi); + return db_sync_subscriber(subscriber); +} +/* + * try to allocate a new unique token for this subscriber and return it + * via a parameter. if the subscriber already has a token, return + * an error. + */ + +int db_subscriber_alloc_token(struct gsm_subscriber *subscriber, u_int32_t *token) +{ + dbi_result result; + u_int32_t try; + + for (;;) { + try = rand(); + if (!try) /* 0 is an invalid token */ + continue; + result = dbi_conn_queryf(conn, + "SELECT * FROM AuthToken " + "WHERE subscriber_id = %llu OR token = \"%08X\" ", + subscriber->id, try); + if (!result) { + LOGP(DDB, LOGL_ERROR, "Failed to query AuthToken " + "while allocating new token.\n"); + return 1; + } + if (dbi_result_get_numrows(result)) { + dbi_result_free(result); + continue; + } + if (!dbi_result_next_row(result)) { + dbi_result_free(result); + break; + } + dbi_result_free(result); + } + result = dbi_conn_queryf(conn, + "INSERT INTO AuthToken " + "(subscriber_id, created, token) " + "VALUES " + "(%llu, datetime('now'), \"%08X\") ", + subscriber->id, try); + if (!result) { + LOGP(DDB, LOGL_ERROR, "Failed to create token %08X for " + "IMSI %s.\n", try, subscriber->imsi); + return 1; + } + dbi_result_free(result); + *token = try; + DEBUGP(DDB, "Allocated token %08X for IMSI %s.\n", try, subscriber->imsi); + + return 0; +} + +int db_subscriber_assoc_imei(struct gsm_subscriber *subscriber, char imei[GSM_IMEI_LENGTH]) +{ + unsigned long long equipment_id, watch_id; + dbi_result result; + + strncpy(subscriber->equipment.imei, imei, + sizeof(subscriber->equipment.imei)-1), + + result = dbi_conn_queryf(conn, + "INSERT OR IGNORE INTO Equipment " + "(imei, created, updated) " + "VALUES " + "(%s, datetime('now'), datetime('now')) ", + imei); + if (!result) { + LOGP(DDB, LOGL_ERROR, "Failed to create Equipment by IMEI.\n"); + return 1; + } + + equipment_id = 0; + if (dbi_result_get_numrows_affected(result)) { + equipment_id = dbi_conn_sequence_last(conn, NULL); + } + dbi_result_free(result); + + if (equipment_id) + DEBUGP(DDB, "New Equipment: ID %llu, IMEI %s\n", equipment_id, imei); + else { + result = dbi_conn_queryf(conn, + "SELECT id FROM Equipment " + "WHERE imei = %s ", + imei + ); + if (!result) { + LOGP(DDB, LOGL_ERROR, "Failed to query Equipment by IMEI.\n"); + return 1; + } + if (!dbi_result_next_row(result)) { + LOGP(DDB, LOGL_ERROR, "Failed to find the Equipment.\n"); + dbi_result_free(result); + return 1; + } + equipment_id = dbi_result_get_ulonglong(result, "id"); + dbi_result_free(result); + } + + result = dbi_conn_queryf(conn, + "INSERT OR IGNORE INTO EquipmentWatch " + "(subscriber_id, equipment_id, created, updated) " + "VALUES " + "(%llu, %llu, datetime('now'), datetime('now')) ", + subscriber->id, equipment_id); + if (!result) { + LOGP(DDB, LOGL_ERROR, "Failed to create EquipmentWatch.\n"); + return 1; + } + + watch_id = 0; + if (dbi_result_get_numrows_affected(result)) + watch_id = dbi_conn_sequence_last(conn, NULL); + + dbi_result_free(result); + if (watch_id) + DEBUGP(DDB, "New EquipmentWatch: ID %llu, IMSI %s, IMEI %s\n", + equipment_id, subscriber->imsi, imei); + else { + result = dbi_conn_queryf(conn, + "UPDATE EquipmentWatch " + "SET updated = datetime('now') " + "WHERE subscriber_id = %llu AND equipment_id = %llu ", + subscriber->id, equipment_id); + if (!result) { + LOGP(DDB, LOGL_ERROR, "Failed to update EquipmentWatch.\n"); + return 1; + } + dbi_result_free(result); + DEBUGP(DDB, "Updated EquipmentWatch: ID %llu, IMSI %s, IMEI %s\n", + equipment_id, subscriber->imsi, imei); + } + + return 0; +} + +/* store an [unsent] SMS to the database */ +int db_sms_store(struct gsm_sms *sms) +{ + dbi_result result; + char *q_text, *q_daddr; + 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->dest_addr, &q_daddr); + 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, sender_id, receiver_id, valid_until, " + "reply_path_req, status_rep_req, protocol_id, " + "data_coding_scheme, ud_hdr_ind, dest_addr, " + "user_data, text) VALUES " + "(datetime('now'), %llu, %llu, %u, " + "%u, %u, %u, %u, %u, %s, %s, %s)", + sms->sender->id, + sms->receiver ? sms->receiver->id : 0, validity_timestamp, + sms->reply_path_req, sms->status_rep_req, sms->protocol_id, + sms->data_coding_scheme, sms->ud_hdr_ind, + q_daddr, q_udata, q_text); + free(q_text); + free(q_daddr); + free(q_udata); + + 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(); + long long unsigned int sender_id, receiver_id; + const char *text, *daddr; + const unsigned char *user_data; + + if (!sms) + return NULL; + + sms->id = dbi_result_get_ulonglong(result, "id"); + + sender_id = dbi_result_get_ulonglong(result, "sender_id"); + sms->sender = subscr_get_by_id(net, sender_id); + + receiver_id = dbi_result_get_ulonglong(result, "receiver_id"); + sms->receiver = subscr_get_by_id(net, receiver_id); + + /* FIXME: validity */ + /* FIXME: those should all be get_uchar, but sqlite3 is braindead */ + sms->reply_path_req = dbi_result_get_uint(result, "reply_path_req"); + sms->status_rep_req = dbi_result_get_uint(result, "status_rep_req"); + sms->ud_hdr_ind = dbi_result_get_uint(result, "ud_hdr_ind"); + sms->protocol_id = dbi_result_get_uint(result, "protocol_id"); + sms->data_coding_scheme = dbi_result_get_uint(result, + "data_coding_scheme"); + /* sms->msg_ref is temporary and not stored in DB */ + + daddr = dbi_result_get_string(result, "dest_addr"); + if (daddr) { + strncpy(sms->dest_addr, daddr, sizeof(sms->dest_addr)); + sms->dest_addr[sizeof(sms->dest_addr)-1] = '\0'; + } + + sms->user_data_len = dbi_result_get_field_length(result, "user_data"); + user_data = dbi_result_get_binary(result, "user_data"); + if (sms->user_data_len > sizeof(sms->user_data)) + sms->user_data_len = (u_int8_t) sizeof(sms->user_data); + memcpy(sms->user_data, user_data, sms->user_data_len); + + text = dbi_result_get_string(result, "text"); + if (text) { + strncpy(sms->text, text, sizeof(sms->text)); + sms->text[sizeof(sms->text)-1] = '\0'; + } + return sms; +} + +struct gsm_sms *db_sms_get(struct gsm_network *net, unsigned long long id) +{ + dbi_result result; + struct gsm_sms *sms; + + result = dbi_conn_queryf(conn, + "SELECT * FROM SMS WHERE SMS.id = %llu", id); + if (!result) + return NULL; + + if (!dbi_result_next_row(result)) { + dbi_result_free(result); + return NULL; + } + + sms = sms_from_result(net, result); + + dbi_result_free(result); + + return sms; +} + +/* retrieve the next unsent SMS with ID >= min_id */ +struct gsm_sms *db_sms_get_unsent(struct gsm_network *net, unsigned long long min_id) +{ + dbi_result result; + struct gsm_sms *sms; + + result = dbi_conn_queryf(conn, + "SELECT SMS.* " + "FROM SMS JOIN Subscriber ON " + "SMS.receiver_id = Subscriber.id " + "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.receiver_id = Subscriber.id " + "WHERE SMS.receiver_id >= %llu AND SMS.sent IS NULL " + "AND Subscriber.lac > 0 AND SMS.deliver_attempts < %u " + "ORDER BY SMS.receiver_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.receiver_id = Subscriber.id " + "WHERE SMS.receiver_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->net, result); + + dbi_result_free(result); + + return sms; +} + +/* mark a given SMS as read */ +int db_sms_mark_sent(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, + u_int8_t apdu_id_flags, u_int8_t len, + u_int8_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 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 new file mode 100644 index 000000000..2b61aa9b0 --- /dev/null +++ b/openbsc/src/libmsc/gsm_04_08.c @@ -0,0 +1,3345 @@ +/* GSM Mobile Radio Interface Layer 3 messages on the A-bis interface + * 3GPP TS 04.08 version 7.21.0 Release 1998 / ETSI TS 100 940 V7.21.0 */ + +/* (C) 2008-2009 by Harald Welte + * (C) 2008-2010 by Holger Hans Peter Freyther + * + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + * + */ + + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +void *tall_locop_ctx; +void *tall_authciphop_ctx; + +int gsm0408_loc_upd_acc(struct gsm_subscriber_connection *conn, u_int32_t tmsi); +static int gsm48_tx_simple(struct gsm_subscriber_connection *conn, + u_int8_t pdisc, u_int8_t msg_type); +static void schedule_reject(struct gsm_subscriber_connection *conn); +static void release_anchor(struct gsm_subscriber_connection *conn); + +struct gsm_lai { + u_int16_t mcc; + u_int16_t mnc; + u_int16_t lac; +}; + +static u_int32_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) { + msg->trx = msg->lchan->ts->trx; + if ((gh->proto_discr & GSM48_PDISC_MASK) == GSM48_PDISC_CC) + DEBUGP(DCC, "(bts %d trx %d ts %d ti %02x) " + "Sending '%s' to MS.\n", msg->trx->bts->nr, + msg->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", msg->trx->bts->nr, + msg->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); +} + +static 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); +} + +static void allocate_security_operation(struct gsm_subscriber_connection *conn) +{ + conn->sec_operation = talloc_zero(tall_authciphop_ctx, + struct gsm_security_operation); +} + +int gsm48_secure_channel(struct gsm_subscriber_connection *conn, int key_seq, + gsm_cbfn *cb, void *cb_data) +{ + struct gsm_network *net = conn->bts->network; + struct gsm_subscriber *subscr = conn->subscr; + struct gsm_security_operation *op; + struct gsm_auth_tuple atuple; + int status = -1, rc; + + /* Check if we _can_ enable encryption. Cases where we can't: + * - Encryption disabled in config + * - Channel already secured (nothing to do) + * - Subscriber equipment doesn't support configured encryption + */ + if (!net->a5_encryption) { + status = GSM_SECURITY_NOAVAIL; + } else if (conn->lchan->encr.alg_id > RSL_ENC_ALG_A5(0)) { + DEBUGP(DMM, "Requesting to secure an already secure channel"); + status = GSM_SECURITY_SUCCEEDED; + } else if (!ms_cm2_a5n_support(subscr->equipment.classmark2, + net->a5_encryption)) { + DEBUGP(DMM, "Subscriber equipment doesn't support requested encryption"); + status = GSM_SECURITY_NOAVAIL; + } + + /* If not done yet, try to get info for this user */ + if (status < 0) { + rc = auth_get_tuple_for_subscr(&atuple, subscr, key_seq); + if (rc <= 0) + status = GSM_SECURITY_NOAVAIL; + } + + /* Are we done yet ? */ + if (status >= 0) + return cb ? + cb(GSM_HOOK_RR_SECURITY, status, NULL, conn, cb_data) : + 0; + + /* Start an operation (can't have more than one pending !!!) */ + if (conn->sec_operation) + return -EBUSY; + + allocate_security_operation(conn); + op = conn->sec_operation; + op->cb = cb; + op->cb_data = cb_data; + memcpy(&op->atuple, &atuple, sizeof(struct gsm_auth_tuple)); + + /* FIXME: Should start a timer for completion ... */ + + /* Then do whatever is needed ... */ + if (rc == AUTH_DO_AUTH_THAN_CIPH) { + /* Start authentication */ + return gsm48_tx_mm_auth_req(conn, op->atuple.rand, op->atuple.key_seq); + } else if (rc == AUTH_DO_CIPH) { + /* Start ciphering directly */ + return gsm0808_cipher_mode(conn, net->a5_encryption, + op->atuple.kc, 8, 0); + } + + return -EINVAL; /* not reached */ +} + +static int authorize_subscriber(struct gsm_loc_updating_operation *loc, + struct gsm_subscriber *subscriber) +{ + if (!subscriber) + return 0; + + /* + * Do not send accept yet as more information should arrive. Some + * phones will not send us the information and we will have to check + * what we want to do with that. + */ + if (loc && (loc->waiting_for_imsi || loc->waiting_for_imei)) + return 0; + + switch (subscriber->net->auth_policy) { + case GSM_AUTH_POLICY_CLOSED: + return subscriber->authorized; + case GSM_AUTH_POLICY_TOKEN: + if (subscriber->authorized) + return subscriber->authorized; + return (subscriber->flags & GSM_SUBSCRIBER_FIRST_CONTACT); + case GSM_AUTH_POLICY_ACCEPT_ALL: + return 1; + default: + return 0; + } +} + +static void release_loc_updating_req(struct gsm_subscriber_connection *conn) +{ + if (!conn->loc_operation) + return; + + /* No need to keep the connection up */ + release_anchor(conn); + + bsc_del_timer(&conn->loc_operation->updating_timer); + talloc_free(conn->loc_operation); + conn->loc_operation = NULL; + msc_release_connection(conn); +} + +static void allocate_loc_updating_req(struct gsm_subscriber_connection *conn) +{ + if (conn->loc_operation) + LOGP(DMM, LOGL_ERROR, "Connection already had operation.\n"); + release_loc_updating_req(conn); + + conn->loc_operation = talloc_zero(tall_locop_ctx, + struct gsm_loc_updating_operation); +} + +static int _gsm0408_authorize_sec_cb(unsigned int hooknum, unsigned int event, + struct msgb *msg, void *data, void *param) +{ + struct gsm_subscriber_connection *conn = data; + int rc = 0; + + switch (event) { + case GSM_SECURITY_AUTH_FAILED: + release_loc_updating_req(conn); + break; + + case GSM_SECURITY_NOAVAIL: + case GSM_SECURITY_SUCCEEDED: + /* We're all good */ + db_subscriber_alloc_tmsi(conn->subscr); + rc = gsm0408_loc_upd_acc(conn, conn->subscr->tmsi); + if (conn->bts->network->send_mm_info) { + /* send MM INFO with network name */ + rc = gsm48_tx_mm_info(conn); + } + + /* call subscr_update after putting the loc_upd_acc + * in the transmit queue, since S_SUBSCR_ATTACHED might + * trigger further action like SMS delivery */ + subscr_update(conn->subscr, conn->bts, + GSM_SUBSCRIBER_UPDATE_ATTACHED); + + /* + * The gsm0408_loc_upd_acc sends a MI with the TMSI. The + * MS needs to respond with a TMSI REALLOCATION COMPLETE + * (even if the TMSI is the same). + */ + break; + + default: + rc = -EINVAL; + }; + + return rc; +} + +static int gsm0408_authorize(struct gsm_subscriber_connection *conn, struct msgb *msg) +{ + if (authorize_subscriber(conn->loc_operation, conn->subscr)) + return gsm48_secure_channel(conn, + conn->loc_operation->key_seq, + _gsm0408_authorize_sec_cb, NULL); + return 0; +} + +void gsm0408_clear_request(struct gsm_subscriber_connection *conn, uint32_t cause) +{ + struct gsm_trans *trans, *temp; + + /* avoid someone issuing a clear */ + conn->in_release = 1; + + /* + * Cancel any outstanding location updating request + * operation taking place on the subscriber connection. + */ + release_loc_updating_req(conn); + + /* 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 lchan */ + /* FIXME: this is not neccessarily the right thing to do, we should + * only set trans->lchan to NULL and wait for another lchan to be + * established to the same MM entity (phone/subscriber) */ + llist_for_each_entry_safe(trans, temp, &conn->bts->network->trans_list, entry) { + if (trans->conn == conn) + trans_free(trans); + } +} + +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, u_int8_t cause) +{ + struct gsm_bts *bts = conn->bts; + struct msgb *msg; + + counter_inc(bts->network->stats.loc_upd_resp.reject); + + msg = gsm48_create_loc_upd_rej(cause); + if (!msg) { + LOGP(DMM, LOGL_ERROR, "Failed to create msg for LOCATION UPDATING REJECT.\n"); + return -1; + } + + msg->lchan = conn->lchan; + + LOGP(DMM, LOGL_INFO, "Subscriber %s: LOCATION UPDATING REJECT " + "LAC=%u BTS=%u\n", conn->subscr ? + subscr_name(conn->subscr) : "unknown", + bts->location_area_code, bts->nr); + + return gsm48_conn_sendmsg(msg, conn, NULL); +} + +/* Chapter 9.2.13 : Send LOCATION UPDATE ACCEPT */ +int gsm0408_loc_upd_acc(struct gsm_subscriber_connection *conn, u_int32_t tmsi) +{ + struct gsm_bts *bts = conn->bts; + struct msgb *msg = gsm48_msgb_alloc(); + struct gsm48_hdr *gh; + struct gsm48_loc_area_id *lai; + u_int8_t *mid; + + msg->lchan = conn->lchan; + + gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh)); + gh->proto_discr = GSM48_PDISC_MM; + gh->msg_type = GSM48_MT_MM_LOC_UPD_ACCEPT; + + lai = (struct gsm48_loc_area_id *) msgb_put(msg, sizeof(*lai)); + gsm48_generate_lai(lai, bts->network->country_code, + bts->network->network_code, bts->location_area_code); + + mid = msgb_put(msg, GSM48_MID_TMSI_LEN); + gsm48_generate_mid_from_tmsi(mid, tmsi); + + DEBUGP(DMM, "-> LOCATION UPDATE ACCEPT\n"); + + counter_inc(bts->network->stats.loc_upd_resp.accept); + + return gsm48_conn_sendmsg(msg, conn, NULL); +} + +/* Transmit Chapter 9.2.10 Identity Request */ +static int mm_tx_identity_req(struct gsm_subscriber_connection *conn, u_int8_t id_type) +{ + struct msgb *msg = gsm48_msgb_alloc(); + struct gsm48_hdr *gh; + + msg->lchan = conn->lchan; + + gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh) + 1); + gh->proto_discr = GSM48_PDISC_MM; + gh->msg_type = GSM48_MT_MM_ID_REQ; + gh->data[0] = id_type; + + return gsm48_conn_sendmsg(msg, conn, NULL); +} + + +/* Parse Chapter 9.2.11 Identity Response */ +static int mm_rx_id_resp(struct gsm_subscriber_connection *conn, struct msgb *msg) +{ + struct gsm48_hdr *gh = msgb_l3(msg); + struct gsm_lchan *lchan = msg->lchan; + struct gsm_bts *bts = lchan->ts->trx->bts; + struct gsm_network *net = bts->network; + u_int8_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_type=0x%02x MI(%s)\n", + mi_type, mi_string); + + dispatch_signal(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, mi_string); + if (!conn->subscr) + conn->subscr = db_create_subscriber(net, mi_string); + } + if (conn->loc_operation) + conn->loc_operation->waiting_for_imsi = 0; + break; + case GSM_MI_TYPE_IMEI: + case GSM_MI_TYPE_IMEISV: + /* update subscribe <-> IMEI mapping */ + if (conn->subscr) { + db_subscriber_assoc_imei(conn->subscr, mi_string); + db_sync_equipment(&conn->subscr->equipment); + } + if (conn->loc_operation) + conn->loc_operation->waiting_for_imei = 0; + break; + } + + /* Check if we can let the mobile station enter */ + return gsm0408_authorize(conn, msg); +} + + +static void loc_upd_rej_cb(void *data) +{ + struct gsm_subscriber_connection *conn = data; + struct gsm_lchan *lchan = conn->lchan; + struct gsm_bts *bts = lchan->ts->trx->bts; + + gsm0408_loc_upd_rej(conn, bts->network->reject_cause); + release_loc_updating_req(conn); +} + +static void schedule_reject(struct gsm_subscriber_connection *conn) +{ + conn->loc_operation->updating_timer.cb = loc_upd_rej_cb; + conn->loc_operation->updating_timer.data = conn; + bsc_schedule_timer(&conn->loc_operation->updating_timer, 5, 0); +} + +static const char *lupd_name(u_int8_t type) +{ + switch (type) { + case GSM48_LUPD_NORMAL: + return "NORMAL"; + case GSM48_LUPD_PERIODIC: + return "PEROIDOC"; + case GSM48_LUPD_IMSI_ATT: + return "IMSI ATTACH"; + default: + return "UNKNOWN"; + } +} + +/* Chapter 9.2.15: Receive Location Updating Request */ +static int mm_rx_loc_upd_req(struct gsm_subscriber_connection *conn, struct msgb *msg) +{ + struct gsm48_hdr *gh = msgb_l3(msg); + struct gsm48_loc_upd_req *lu; + struct gsm_subscriber *subscr = NULL; + struct gsm_bts *bts = conn->bts; + u_int8_t mi_type; + char mi_string[GSM48_MI_SIZE]; + int rc; + + lu = (struct gsm48_loc_upd_req *) gh->data; + + mi_type = lu->mi[0] & GSM_MI_TYPE_MASK; + + gsm48_mi_to_string(mi_string, sizeof(mi_string), lu->mi, lu->mi_len); + + DEBUGPC(DMM, "mi_type=0x%02x MI(%s) type=%s ", mi_type, mi_string, + lupd_name(lu->type)); + + dispatch_signal(SS_SUBSCR, S_SUBSCR_IDENTITY, &lu->mi_len); + + switch (lu->type) { + case GSM48_LUPD_NORMAL: + counter_inc(bts->network->stats.loc_upd_type.normal); + break; + case GSM48_LUPD_IMSI_ATT: + counter_inc(bts->network->stats.loc_upd_type.attach); + break; + case GSM48_LUPD_PERIODIC: + counter_inc(bts->network->stats.loc_upd_type.periodic); + break; + } + + /* + * Pseudo Spoof detection: Just drop a second/concurrent + * location updating request. + */ + if (conn->loc_operation) { + DEBUGPC(DMM, "ignoring request due an existing one: %p.\n", + conn->loc_operation); + gsm0408_loc_upd_rej(conn, GSM48_REJECT_PROTOCOL_ERROR); + return 0; + } + + allocate_loc_updating_req(conn); + + conn->loc_operation->key_seq = lu->key_seq; + + switch (mi_type) { + case GSM_MI_TYPE_IMSI: + DEBUGPC(DMM, "\n"); + /* we always want the IMEI, too */ + rc = mm_tx_identity_req(conn, GSM_MI_TYPE_IMEI); + conn->loc_operation->waiting_for_imei = 1; + + /* look up subscriber based on IMSI, create if not found */ + subscr = subscr_get_by_imsi(bts->network, mi_string); + if (!subscr) { + subscr = db_create_subscriber(bts->network, mi_string); + } + break; + case GSM_MI_TYPE_TMSI: + DEBUGPC(DMM, "\n"); + /* look up the subscriber based on TMSI, request IMSI if it fails */ + subscr = subscr_get_by_tmsi(bts->network, + tmsi_from_string(mi_string)); + if (!subscr) { + /* send IDENTITY REQUEST message to get IMSI */ + rc = mm_tx_identity_req(conn, GSM_MI_TYPE_IMSI); + conn->loc_operation->waiting_for_imsi = 1; + } + /* we always want the IMEI, too */ + rc = 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); +} + +#if 0 +static u_int8_t to_bcd8(u_int8_t val) +{ + return ((val / 10) << 4) | (val % 10); +} +#endif + +/* Section 9.2.15a */ +int gsm48_tx_mm_info(struct gsm_subscriber_connection *conn) +{ + struct msgb *msg = gsm48_msgb_alloc(); + struct gsm48_hdr *gh; + struct gsm_network *net = conn->bts->network; + u_int8_t *ptr8; + int name_len, name_pad; +#if 0 + time_t cur_t; + struct tm* cur_time; + int tz15min; +#endif + + 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 = (u_int16_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(ptr8, net->name_long); + + } + + if (net->name_short) { +#if 0 + name_len = strlen(net->name_short); + /* 10.5.3.5a */ + ptr8 = (u_int8_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 = (u_int16_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 = (u_int8_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(ptr8, net->name_short); + + } + +#if 0 + /* Section 10.5.3.9 */ + cur_t = time(NULL); + cur_time = gmtime(&cur_t); + ptr8 = msgb_put(msg, 8); + ptr8[0] = GSM48_IE_NET_TIME_TZ; + ptr8[1] = to_bcd8(cur_time->tm_year % 100); + ptr8[2] = to_bcd8(cur_time->tm_mon); + ptr8[3] = to_bcd8(cur_time->tm_mday); + ptr8[4] = to_bcd8(cur_time->tm_hour); + ptr8[5] = to_bcd8(cur_time->tm_min); + ptr8[6] = to_bcd8(cur_time->tm_sec); + /* 02.42: coded as BCD encoded signed value in units of 15 minutes */ + tz15min = (cur_time->tm_gmtoff)/(60*15); + ptr8[7] = to_bcd8(tz15min); + if (tz15min < 0) + ptr8[7] |= 0x80; +#endif + + DEBUGP(DMM, "-> MM INFO\n"); + + return gsm48_conn_sendmsg(msg, conn, NULL); +} + +/* Section 9.2.2 */ +int gsm48_tx_mm_auth_req(struct gsm_subscriber_connection *conn, u_int8_t *rand, int key_seq) +{ + struct msgb *msg = gsm48_msgb_alloc(); + struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh)); + struct gsm48_auth_req *ar = (struct gsm48_auth_req *) msgb_put(msg, sizeof(*ar)); + + DEBUGP(DMM, "-> AUTH REQ (rand = %s)\n", hexdump(rand, 16)); + + msg->lchan = conn->lchan; + gh->proto_discr = GSM48_PDISC_MM; + gh->msg_type = GSM48_MT_MM_AUTH_REQ; + + ar->key_seq = key_seq; + + /* 16 bytes RAND parameters */ + if (rand) + memcpy(ar->rand, rand, 16); + + return gsm48_conn_sendmsg(msg, conn, NULL); +} + +/* Section 9.2.1 */ +int gsm48_tx_mm_auth_rej(struct gsm_subscriber_connection *conn) +{ + DEBUGP(DMM, "-> AUTH REJECT\n"); + return gsm48_tx_simple(conn, GSM48_PDISC_MM, GSM48_MT_MM_AUTH_REJ); +} + +static int gsm48_tx_mm_serv_ack(struct gsm_subscriber_connection *conn) +{ + DEBUGP(DMM, "-> CM SERVICE ACK\n"); + return gsm48_tx_simple(conn, GSM48_PDISC_MM, GSM48_MT_MM_CM_SERV_ACC); +} + +/* 9.2.6 CM service reject */ +static 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); + msg->lchan = conn->lchan; + return gsm48_conn_sendmsg(msg, conn, NULL); +} + +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; + + switch (event) { + case GSM_SECURITY_AUTH_FAILED: + /* Nothing to do */ + break; + + case GSM_SECURITY_NOAVAIL: + rc = gsm48_tx_mm_serv_ack(conn); + break; + + case GSM_SECURITY_SUCCEEDED: + /* nothing to do. CIPHER MODE COMMAND is + * implicit CM SERV ACK */ + 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) +{ + u_int8_t mi_type; + char mi_string[GSM48_MI_SIZE]; + + struct gsm_bts *bts = conn->bts; + struct gsm_subscriber *subscr; + struct gsm48_hdr *gh = msgb_l3(msg); + struct gsm48_service_request *req = + (struct gsm48_service_request *)gh->data; + /* unfortunately in Phase1 the classmark2 length is variable */ + u_int8_t classmark2_len = gh->data[1]; + u_int8_t *classmark2 = gh->data+2; + u_int8_t mi_len = *(classmark2 + classmark2_len); + u_int8_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); + } + + mi_type = mi[0] & GSM_MI_TYPE_MASK; + if (mi_type != GSM_MI_TYPE_TMSI) { + DEBUGPC(DMM, "mi_type is not TMSI: %d\n", mi_type); + return gsm48_tx_mm_serv_rej(conn, + GSM48_REJECT_INCORRECT_MESSAGE); + } + + gsm48_mi_to_string(mi_string, sizeof(mi_string), mi, mi_len); + DEBUGPC(DMM, "serv_type=0x%02x mi_type=0x%02x M(%s)\n", + req->cm_service_type, mi_type, mi_string); + + dispatch_signal(SS_SUBSCR, S_SUBSCR_IDENTITY, (classmark2 + classmark2_len)); + + if (is_siemens_bts(bts)) + send_siemens_mrpci(msg->lchan, classmark2-1); + + subscr = subscr_get_by_tmsi(bts->network, + tmsi_from_string(mi_string)); + + /* 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_HLR); + + 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); + + 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 msgb *msg) +{ + struct gsm_bts *bts = msg->lchan->ts->trx->bts; + struct gsm48_hdr *gh = msgb_l3(msg); + struct gsm48_imsi_detach_ind *idi = + (struct gsm48_imsi_detach_ind *) gh->data; + u_int8_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_type=0x%02x MI(%s): ", + mi_type, mi_string); + + counter_inc(bts->network->stats.loc_upd_type.detach); + + switch (mi_type) { + case GSM_MI_TYPE_TMSI: + subscr = subscr_get_by_tmsi(bts->network, + tmsi_from_string(mi_string)); + break; + case GSM_MI_TYPE_IMSI: + subscr = subscr_get_by_imsi(bts->network, 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, msg->trx->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! */ + + /* subscriber is detached: should we release lchan? */ + return 0; +} + +static int gsm48_rx_mm_status(struct msgb *msg) +{ + struct gsm48_hdr *gh = msgb_l3(msg); + + DEBUGP(DMM, "MM STATUS (reject cause 0x%02x)\n", gh->data[0]); + + return 0; +} + +/* Chapter 9.2.3: Authentication Response */ +static int gsm48_rx_mm_auth_resp(struct gsm_subscriber_connection *conn, struct msgb *msg) +{ + struct gsm48_hdr *gh = msgb_l3(msg); + struct gsm48_auth_resp *ar = (struct gsm48_auth_resp*) gh->data; + struct gsm_network *net = conn->bts->network; + + DEBUGP(DMM, "MM AUTHENTICATION RESPONSE (sres = %s): ", + hexdump(ar->sres, 4)); + + /* Safety check */ + if (!conn->sec_operation) { + DEBUGP(DMM, "No authentication/cipher operation in progress !!!\n"); + return -EIO; + } + + /* Validate SRES */ + if (memcmp(conn->sec_operation->atuple.sres, ar->sres,4)) { + int rc; + gsm_cbfn *cb = conn->sec_operation->cb; + + DEBUGPC(DMM, "Invalid (expected %s)\n", + hexdump(conn->sec_operation->atuple.sres, 4)); + + if (cb) + cb(GSM_HOOK_RR_SECURITY, GSM_SECURITY_AUTH_FAILED, + NULL, conn, conn->sec_operation->cb_data); + + rc = gsm48_tx_mm_auth_rej(conn); + release_security_operation(conn); + return rc; + } + + DEBUGPC(DMM, "OK\n"); + + /* Start ciphering */ + return gsm0808_cipher_mode(conn, net->a5_encryption, + conn->sec_operation->atuple.kc, 8, 0); +} + +/* Receive a GSM 04.08 Mobility Management (MM) message */ +static int gsm0408_rcv_mm(struct gsm_subscriber_connection *conn, struct msgb *msg) +{ + struct gsm48_hdr *gh = msgb_l3(msg); + int rc = 0; + + switch (gh->msg_type & 0xbf) { + case GSM48_MT_MM_LOC_UPD_REQUEST: + DEBUGP(DMM, "LOCATION UPDATING REQUEST: "); + rc = mm_rx_loc_upd_req(conn, msg); + break; + case GSM48_MT_MM_ID_RESP: + rc = mm_rx_id_resp(conn, msg); + break; + case GSM48_MT_MM_CM_SERV_REQ: + rc = gsm48_rx_mm_serv_req(conn, msg); + break; + case GSM48_MT_MM_STATUS: + rc = gsm48_rx_mm_status(msg); + break; + case GSM48_MT_MM_TMSI_REALL_COMPL: + DEBUGP(DMM, "TMSI Reallocation Completed. Subscriber: %s\n", + conn->subscr ? + subscr_name(conn->subscr) : + "unknown subscriber"); + release_loc_updating_req(conn); + break; + case GSM48_MT_MM_IMSI_DETACH_IND: + rc = gsm48_rx_mm_imsi_detach_ind(msg); + break; + case GSM48_MT_MM_CM_REEST_REQ: + DEBUGP(DMM, "CM REESTABLISH REQUEST: Not implemented\n"); + break; + case GSM48_MT_MM_AUTH_RESP: + rc = gsm48_rx_mm_auth_resp(conn, msg); + break; + default: + LOGP(DMM, LOGL_NOTICE, "Unknown GSM 04.08 MM msg type 0x%02x\n", + gh->msg_type); + break; + } + + return rc; +} + +/* Receive a PAGING RESPONSE message from the MS */ +static int gsm48_rx_rr_pag_resp(struct gsm_subscriber_connection *conn, struct msgb *msg) +{ + struct gsm_bts *bts = conn->bts; + struct gsm48_hdr *gh = msgb_l3(msg); + struct gsm48_pag_resp *resp; + u_int8_t *classmark2_lv = gh->data + 1; + u_int8_t mi_type; + char mi_string[GSM48_MI_SIZE]; + struct gsm_subscriber *subscr = NULL; + int rc = 0; + + resp = (struct gsm48_pag_resp *) &gh->data[0]; + gsm48_paging_extract_mi(resp, msgb_l3len(msg) - sizeof(*gh), + mi_string, &mi_type); + DEBUGP(DRR, "PAGING RESPONSE: mi_type=0x%02x MI(%s)\n", + mi_type, mi_string); + + switch (mi_type) { + case GSM_MI_TYPE_TMSI: + subscr = subscr_get_by_tmsi(bts->network, + tmsi_from_string(mi_string)); + break; + case GSM_MI_TYPE_IMSI: + subscr = subscr_get_by_imsi(bts->network, mi_string); + break; + } + + if (!subscr) { + DEBUGP(DRR, "<- Can't find any subscriber for this ID\n"); + /* FIXME: request id? close channel? */ + return -EINVAL; + } + 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); + + rc = gsm48_handle_paging_resp(conn, msg, subscr); + return rc; +} + +static int gsm48_rx_rr_classmark(struct gsm_subscriber_connection *conn, struct msgb *msg) +{ + struct gsm48_hdr *gh = msgb_l3(msg); + struct gsm_subscriber *subscr = conn->subscr; + unsigned int payload_len = msgb_l3len(msg) - sizeof(*gh); + u_int8_t cm2_len, cm3_len = 0; + u_int8_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 -EINVAL; + } + if (cm2_len > 3) { + DEBUGPC(DRR, "CM2 too long!\n"); + return -EINVAL; + } + + 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 -EINVAL; + } + DEBUGPC(DRR, "CM3(len=%u)\n", cm3_len); + } + 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); + } + + return 0; +} + +static int gsm48_rx_rr_status(struct msgb *msg) +{ + struct gsm48_hdr *gh = msgb_l3(msg); + + DEBUGP(DRR, "STATUS rr_cause = %s\n", + rr_cause_name(gh->data[0])); + + return 0; +} + +static int gsm48_rx_rr_meas_rep(struct msgb *msg) +{ + struct gsm_meas_rep *meas_rep = lchan_next_meas_rep(msg->lchan); + + /* 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 */ + DEBUGP(DMEAS, "DIRECT GSM48 MEASUREMENT REPORT ?!? "); + gsm48_parse_meas_rep(meas_rep, msg); + + return 0; +} + +static int gsm48_rx_rr_app_info(struct gsm_subscriber_connection *conn, struct msgb *msg) +{ + struct gsm48_hdr *gh = msgb_l3(msg); + u_int8_t apdu_id_flags; + u_int8_t apdu_len; + u_int8_t *apdu_data; + + apdu_id_flags = gh->data[0]; + apdu_len = gh->data[1]; + apdu_data = gh->data+2; + + DEBUGP(DNM, "RX APPLICATION INFO id/flags=0x%02x apdu_len=%u apdu=%s", + apdu_id_flags, apdu_len, hexdump(apdu_data, apdu_len)); + + return db_apdu_blob_store(conn->subscr, apdu_id_flags, apdu_len, apdu_data); +} + +/* Chapter 9.1.10 Ciphering Mode Complete */ +static int gsm48_rx_rr_ciph_m_compl(struct gsm_subscriber_connection *conn, struct msgb *msg) +{ + gsm_cbfn *cb; + int rc = 0; + + DEBUGP(DRR, "CIPHERING MODE COMPLETE\n"); + + /* Safety check */ + if (!conn->sec_operation) { + DEBUGP(DRR, "No authentication/cipher operation in progress !!!\n"); + return -EIO; + } + + /* FIXME: check for MI (if any) */ + + /* Call back whatever was in progress (if anything) ... */ + cb = conn->sec_operation->cb; + if (cb) { + rc = cb(GSM_HOOK_RR_SECURITY, GSM_SECURITY_SUCCEEDED, + NULL, conn, conn->sec_operation->cb_data); + } + + /* Complete the operation */ + release_security_operation(conn); + + return rc; +} + +/* Chapter 9.1.16 Handover complete */ +static int gsm48_rx_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; + dispatch_signal(SS_LCHAN, S_LCHAN_HANDOVER_COMPL, &sig); + /* FIXME: release old channel */ + + return 0; +} + +/* Chapter 9.1.17 Handover Failure */ +static int gsm48_rx_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; + dispatch_signal(SS_LCHAN, S_LCHAN_HANDOVER_FAIL, &sig); + /* FIXME: release allocated new channel */ + + return 0; +} + +/* 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_CLSM_CHG: + rc = gsm48_rx_rr_classmark(conn, msg); + break; + case GSM48_MT_RR_GPRS_SUSP_REQ: + DEBUGP(DRR, "GRPS SUSPEND REQUEST\n"); + break; + case GSM48_MT_RR_PAG_RESP: + rc = gsm48_rx_rr_pag_resp(conn, msg); + break; + case GSM48_MT_RR_STATUS: + rc = gsm48_rx_rr_status(msg); + break; + case GSM48_MT_RR_MEAS_REP: + rc = gsm48_rx_rr_meas_rep(msg); + break; + case GSM48_MT_RR_APP_INFO: + rc = gsm48_rx_rr_app_info(conn, msg); + break; + case GSM48_MT_RR_CIPH_M_COMPL: + rc = gsm48_rx_rr_ciph_m_compl(conn, msg); + break; + case GSM48_MT_RR_HANDO_COMPL: + rc = gsm48_rx_rr_ho_compl(msg); + break; + case GSM48_MT_RR_HANDO_FAIL: + rc = gsm48_rx_rr_ho_fail(msg); + break; + default: + LOGP(DRR, LOGL_NOTICE, "Unimplemented " + "GSM 04.08 RR msg type 0x%02x\n", gh->msg_type); + break; + } + + return rc; +} + +int gsm48_send_rr_app_info(struct gsm_subscriber_connection *conn, u_int8_t apdu_id, + u_int8_t apdu_len, const u_int8_t *apdu) +{ + struct msgb *msg = gsm48_msgb_alloc(); + struct gsm48_hdr *gh; + + msg->lchan = conn->lchan; + + DEBUGP(DRR, "TX APPLICATION INFO id=0x%02x, len=%u\n", + apdu_id, apdu_len); + + gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh) + 2 + apdu_len); + gh->proto_discr = GSM48_PDISC_RR; + gh->msg_type = GSM48_MT_RR_APP_INFO; + gh->data[0] = apdu_id; + gh->data[1] = apdu_len; + memcpy(gh->data+2, apdu, apdu_len); + + return gsm48_conn_sendmsg(msg, conn, NULL); +} + +/* Call Control */ + +/* The entire call control code is written in accordance with Figure 7.10c + * for 'very early assignment', i.e. we allocate a TCH/F during IMMEDIATE + * ASSIGN, then first use that TCH/F for signalling and later MODE MODIFY + * it for voice */ + +static void new_cc_state(struct gsm_trans *trans, int state) +{ + if (state > 31 || state < 0) + return; + + DEBUGP(DCC, "new state %s -> %s\n", + gsm48_cc_state_name(trans->cc.state), + gsm48_cc_state_name(state)); + + trans->cc.state = state; +} + +static int gsm48_cc_tx_status(struct gsm_trans *trans, void *arg) +{ + struct msgb *msg = gsm48_msgb_alloc(); + struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh)); + u_int8_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, + u_int8_t pdisc, u_int8_t msg_type) +{ + struct msgb *msg = gsm48_msgb_alloc(); + struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh)); + + msg->lchan = conn->lchan; + + gh->proto_discr = pdisc; + gh->msg_type = msg_type; + + return gsm48_conn_sendmsg(msg, conn, NULL); +} + +static void gsm48_stop_cc_timer(struct gsm_trans *trans) +{ + if (bsc_timer_pending(&trans->cc.timer)) { + DEBUGP(DCC, "stopping pending timer T%x\n", trans->cc.Tcurrent); + bsc_del_timer(&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, + u_int32_t callref, int location, int value) +{ + struct gsm_mncc rel; + + memset(&rel, 0, sizeof(rel)); + rel.callref = callref; + mncc_set_cause(&rel, location, value); + 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->subscr->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 *param) +{ + int found = 0; + struct gsm_subscriber_connection *conn = _conn; + struct gsm_network **paging_request = param, *net; + struct gsm_trans *transt, *tmp; + + if (hooknum != GSM_HOOK_RR_PAGING) + return -EINVAL; + + net = *paging_request; + if (!net) { + DEBUGP(DCC, "Error Network not set!\n"); + return -EINVAL; + } + + /* check all tranactions (without lchan) for subscriber */ + llist_for_each_entry_safe(transt, tmp, &net->trans_list, entry) { + if (transt->paging_request != paging_request || transt->conn) + continue; + switch (event) { + case GSM_PAGING_SUCCEEDED: + if (!conn) // paranoid + break; + DEBUGP(DCC, "Paging subscr %s succeeded!\n", + transt->subscr->extension); + found = 1; + /* Assign lchan */ + if (!transt->conn) { + transt->paging_request = NULL; + transt->conn = conn; + conn->put_channel = 1; + } + /* 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 */ + found = 1; + mncc_release_ind(transt->subscr->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; + } + } + + talloc_free(paging_request); + + /* + * FIXME: The queue needs to be kicked. This is likely to go through a RF + * failure and then the subscr will be poke again. This needs a lot of fixing + * in the subscriber queue code. + */ + if (!found && conn) + conn->put_channel = 1; + return 0; +} + +static int tch_recv_mncc(struct gsm_network *net, u_int32_t callref, int enable); + +/* handle audio path for handover */ +static int handle_ho_signal(unsigned int subsys, unsigned int signal, + void *handler_data, void *signal_data) +{ + struct rtp_socket *old_rs, *new_rs, *other_rs; + struct ho_signal_data *sig = signal_data; + + if (subsys != SS_HO || signal != S_HANDOVER_ACK) + 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 = sig->new_lchan->abis_ip.rtp_socket; + old_rs = sig->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(sig->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 (sig->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; +} + +/* 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; + + /* 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); + } + } + break; + case S_ABISIP_DLCX_IND: + /* the BTS tells us a RTP stream has been disconnected */ + if (lchan->abis_ip.rtp_socket) { + rtp_socket_free(lchan->abis_ip.rtp_socket); + lchan->abis_ip.rtp_socket = NULL; + } + + break; + } + + return 0; +} + +/* map two ipaccess RTP streams onto each other */ +static int tch_map(struct gsm_lchan *lchan, struct gsm_lchan *remote_lchan) +{ + struct gsm_bts *bts = lchan->ts->trx->bts; + struct gsm_bts *remote_bts = remote_lchan->ts->trx->bts; + int rc; + + DEBUGP(DCC, "Setting up TCH map between (bts=%u,trx=%u,ts=%u) and (bts=%u,trx=%u,ts=%u)\n", + bts->nr, lchan->ts->trx->nr, lchan->ts->nr, + remote_bts->nr, remote_lchan->ts->trx->nr, remote_lchan->ts->nr); + + if (bts->type != remote_bts->type) { + DEBUGP(DCC, "Cannot switch calls between different BTS types yet\n"); + return -EINVAL; + } + + // todo: map between different bts types + switch (bts->type) { + case GSM_BTS_TYPE_NANOBTS: + if (!ipacc_rtp_direct) { + /* 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: + trau_mux_map_lchan(lchan, remote_lchan); + break; + default: + DEBUGP(DCC, "Unknown BTS type %u\n", bts->type); + return -EINVAL; + } + + return 0; +} + +/* bridge channels of two transactions */ +static int tch_bridge(struct gsm_network *net, u_int32_t *refs) +{ + struct gsm_trans *trans1 = trans_find_by_callref(net, refs[0]); + struct gsm_trans *trans2 = trans_find_by_callref(net, refs[1]); + + if (!trans1 || !trans2) + return -EIO; + + if (!trans1->conn || !trans2->conn) + return -EIO; + + /* 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, u_int32_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; + lchan = trans->conn->lchan; + bts = lchan->ts->trx->bts; + + switch (bts->type) { + case GSM_BTS_TYPE_NANOBTS: + if (ipacc_rtp_direct) { + DEBUGP(DCC, "Error: RTP proxy is disabled\n"); + return -EINVAL; + } + /* in case, we don't have a RTP socket yet, we note this + * in the transaction and try later */ + if (!lchan->abis_ip.rtp_socket) { + trans->tch_recv = enable; + 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: + if (enable) + return trau_recv_lchan(lchan, callref); + return trau_mux_unmap(NULL, callref); + break; + default: + DEBUGP(DCC, "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->subscr->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->subscr->net, trans, MNCC_DISC_IND, &l4_rel); + } + + /* process disconnect towards mobile station */ + if (disconnect || release) { + mncc_set_cause(&mo_rel, mo_location, mo_cause); + mo_rel.cause.diag[0] = ((trans->cc.Tcurrent & 0xf00) >> 8) + '0'; + mo_rel.cause.diag[1] = ((trans->cc.Tcurrent & 0x0f0) >> 4) + '0'; + mo_rel.cause.diag[2] = (trans->cc.Tcurrent & 0x00f) + '0'; + mo_rel.cause.diag_len = 3; + + if (disconnect) + gsm48_cc_tx_disconnect(trans, &mo_rel); + if (release) + gsm48_cc_tx_release(trans, &mo_rel); + } + +} + +static void gsm48_start_cc_timer(struct gsm_trans *trans, int current, + int sec, int micro) +{ + DEBUGP(DCC, "starting timer T%x with %d seconds\n", current, sec); + trans->cc.timer.cb = gsm48_cc_timeout; + trans->cc.timer.data = trans; + bsc_schedule_timer(&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); + u_int8_t msg_type = gh->msg_type & 0xbf; + unsigned int payload_len = msgb_l3len(msg) - sizeof(*gh); + struct tlv_parsed tp; + struct gsm_mncc setup; + + memset(&setup, 0, sizeof(struct gsm_mncc)); + setup.callref = trans->callref; + 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 */ + if (trans->subscr) { + setup.fields |= MNCC_F_CALLING; + strncpy(setup.calling.number, trans->subscr->extension, + sizeof(setup.calling.number)-1); + strncpy(setup.imsi, trans->subscr->imsi, + sizeof(setup.imsi)-1); + } + /* bearer capability */ + if (TLVP_PRESENT(&tp, GSM48_IE_BEARER_CAP)) { + setup.fields |= MNCC_F_BEARER_CAP; + gsm48_decode_bearer_cap(&setup.bearer_cap, + TLVP_VAL(&tp, GSM48_IE_BEARER_CAP)-1); + } + /* 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); + + counter_inc(trans->subscr->net->stats.call.mo_setup); + + /* indicate setup to MNCC */ + mncc_recvmsg(trans->subscr->net, trans, MNCC_SETUP_IND, &setup); + + /* MNCC code will modify the channel asynchronously, we should + * ipaccess-bind only after the modification has been made to the + * lchan->tch_mode */ + return 0; +} + +static int gsm48_cc_tx_setup(struct gsm_trans *trans, void *arg) +{ + struct msgb *msg = gsm48_msgb_alloc(); + struct gsm48_hdr *gh; + struct gsm_mncc *setup = arg; + int rc, trans_id; + + gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh)); + + /* transaction id must not be assigned */ + if (trans->transaction_id != 0xff) { /* unasssigned */ + DEBUGP(DCC, "TX Setup with assigned transaction. " + "This is not allowed!\n"); + /* Temporarily out of order */ + rc = mncc_release_ind(trans->subscr->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->subscr, GSM48_PDISC_CC, 0); + if (trans_id < 0) { + /* no free transaction ID */ + rc = mncc_release_ind(trans->subscr->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); + + counter_inc(trans->subscr->net->stats.call.mt_setup); + + return gsm48_conn_sendmsg(msg, trans->conn, trans); +} + +static int gsm48_cc_rx_call_conf(struct gsm_trans *trans, struct msgb *msg) +{ + struct gsm48_hdr *gh = msgb_l3(msg); + unsigned int payload_len = msgb_l3len(msg) - sizeof(*gh); + struct tlv_parsed tp; + struct gsm_mncc call_conf; + + gsm48_stop_cc_timer(trans); + gsm48_start_cc_timer(trans, 0x310, GSM48_T310); + + memset(&call_conf, 0, sizeof(struct gsm_mncc)); + call_conf.callref = trans->callref; + 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); + } + /* 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); + } + + new_cc_state(trans, GSM_CSTATE_MO_TERM_CALL_CONF); + + return mncc_recvmsg(trans->subscr->net, trans, MNCC_CALL_CONF_IND, + &call_conf); +} + +static int gsm48_cc_tx_call_proc(struct gsm_trans *trans, void *arg) +{ + struct gsm_mncc *proceeding = arg; + struct msgb *msg = gsm48_msgb_alloc(); + struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh)); + + gh->msg_type = GSM48_MT_CC_CALL_PROC; + + new_cc_state(trans, GSM_CSTATE_MO_CALL_PROC); + + /* bearer capability */ + if (proceeding->fields & MNCC_F_BEARER_CAP) + gsm48_encode_bearer_cap(msg, 0, &proceeding->bearer_cap); + /* facility */ + if (proceeding->fields & MNCC_F_FACILITY) + gsm48_encode_facility(msg, 0, &proceeding->facility); + /* progress */ + if (proceeding->fields & MNCC_F_PROGRESS) + gsm48_encode_progress(msg, 0, &proceeding->progress); + + return gsm48_conn_sendmsg(msg, trans->conn, trans); +} + +static int gsm48_cc_rx_alerting(struct gsm_trans *trans, struct msgb *msg) +{ + struct gsm48_hdr *gh = msgb_l3(msg); + unsigned int payload_len = msgb_l3len(msg) - sizeof(*gh); + struct tlv_parsed tp; + struct gsm_mncc alerting; + + gsm48_stop_cc_timer(trans); + gsm48_start_cc_timer(trans, 0x301, GSM48_T301); + + memset(&alerting, 0, sizeof(struct gsm_mncc)); + alerting.callref = trans->callref; + tlv_parse(&tp, &gsm48_att_tlvdef, gh->data, payload_len, 0, 0); + /* facility */ + if (TLVP_PRESENT(&tp, GSM48_IE_FACILITY)) { + alerting.fields |= MNCC_F_FACILITY; + gsm48_decode_facility(&alerting.facility, + TLVP_VAL(&tp, GSM48_IE_FACILITY)-1); + } + + /* progress */ + if (TLVP_PRESENT(&tp, GSM48_IE_PROGR_IND)) { + alerting.fields |= MNCC_F_PROGRESS; + gsm48_decode_progress(&alerting.progress, + TLVP_VAL(&tp, GSM48_IE_PROGR_IND)-1); + } + /* ss-version */ + if (TLVP_PRESENT(&tp, GSM48_IE_SS_VERS)) { + alerting.fields |= MNCC_F_SSVERSION; + gsm48_decode_ssversion(&alerting.ssversion, + TLVP_VAL(&tp, GSM48_IE_SS_VERS)-1); + } + + new_cc_state(trans, GSM_CSTATE_CALL_RECEIVED); + + return mncc_recvmsg(trans->subscr->net, trans, MNCC_ALERT_IND, + &alerting); +} + +static int gsm48_cc_tx_alerting(struct gsm_trans *trans, void *arg) +{ + struct gsm_mncc *alerting = arg; + struct msgb *msg = gsm48_msgb_alloc(); + struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh)); + + gh->msg_type = GSM48_MT_CC_ALERTING; + + /* facility */ + if (alerting->fields & MNCC_F_FACILITY) + gsm48_encode_facility(msg, 0, &alerting->facility); + /* progress */ + if (alerting->fields & MNCC_F_PROGRESS) + gsm48_encode_progress(msg, 0, &alerting->progress); + /* user-user */ + if (alerting->fields & MNCC_F_USERUSER) + gsm48_encode_useruser(msg, 0, &alerting->useruser); + + new_cc_state(trans, GSM_CSTATE_CALL_DELIVERED); + + return gsm48_conn_sendmsg(msg, trans->conn, trans); +} + +static int gsm48_cc_tx_progress(struct gsm_trans *trans, void *arg) +{ + struct gsm_mncc *progress = arg; + struct msgb *msg = gsm48_msgb_alloc(); + struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh)); + + gh->msg_type = GSM48_MT_CC_PROGRESS; + + /* progress */ + gsm48_encode_progress(msg, 1, &progress->progress); + /* user-user */ + if (progress->fields & MNCC_F_USERUSER) + gsm48_encode_useruser(msg, 0, &progress->useruser); + + return gsm48_conn_sendmsg(msg, trans->conn, trans); +} + +static int gsm48_cc_tx_connect(struct gsm_trans *trans, void *arg) +{ + struct gsm_mncc *connect = arg; + struct msgb *msg = gsm48_msgb_alloc(); + struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh)); + + gh->msg_type = GSM48_MT_CC_CONNECT; + + gsm48_stop_cc_timer(trans); + gsm48_start_cc_timer(trans, 0x313, GSM48_T313); + + /* facility */ + if (connect->fields & MNCC_F_FACILITY) + gsm48_encode_facility(msg, 0, &connect->facility); + /* progress */ + if (connect->fields & MNCC_F_PROGRESS) + gsm48_encode_progress(msg, 0, &connect->progress); + /* connected number */ + if (connect->fields & MNCC_F_CONNECTED) + gsm48_encode_connected(msg, &connect->connected); + /* user-user */ + if (connect->fields & MNCC_F_USERUSER) + gsm48_encode_useruser(msg, 0, &connect->useruser); + + new_cc_state(trans, GSM_CSTATE_CONNECT_IND); + + return gsm48_conn_sendmsg(msg, trans->conn, trans); +} + +static int gsm48_cc_rx_connect(struct gsm_trans *trans, struct msgb *msg) +{ + struct gsm48_hdr *gh = msgb_l3(msg); + unsigned int payload_len = msgb_l3len(msg) - sizeof(*gh); + struct tlv_parsed tp; + struct gsm_mncc connect; + + gsm48_stop_cc_timer(trans); + + memset(&connect, 0, sizeof(struct gsm_mncc)); + connect.callref = trans->callref; + tlv_parse(&tp, &gsm48_att_tlvdef, gh->data, payload_len, 0, 0); + /* use subscriber as connected party number */ + if (trans->subscr) { + connect.fields |= MNCC_F_CONNECTED; + strncpy(connect.connected.number, trans->subscr->extension, + sizeof(connect.connected.number)-1); + strncpy(connect.imsi, trans->subscr->imsi, + sizeof(connect.imsi)-1); + } + /* facility */ + if (TLVP_PRESENT(&tp, GSM48_IE_FACILITY)) { + connect.fields |= MNCC_F_FACILITY; + gsm48_decode_facility(&connect.facility, + TLVP_VAL(&tp, GSM48_IE_FACILITY)-1); + } + /* user-user */ + if (TLVP_PRESENT(&tp, GSM48_IE_USER_USER)) { + connect.fields |= MNCC_F_USERUSER; + gsm48_decode_useruser(&connect.useruser, + TLVP_VAL(&tp, GSM48_IE_USER_USER)-1); + } + /* ss-version */ + if (TLVP_PRESENT(&tp, GSM48_IE_SS_VERS)) { + connect.fields |= MNCC_F_SSVERSION; + gsm48_decode_ssversion(&connect.ssversion, + TLVP_VAL(&tp, GSM48_IE_SS_VERS)-1); + } + + new_cc_state(trans, GSM_CSTATE_CONNECT_REQUEST); + counter_inc(trans->subscr->net->stats.call.mt_connect); + + return mncc_recvmsg(trans->subscr->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); + counter_inc(trans->subscr->net->stats.call.mo_connect_ack); + + memset(&connect_ack, 0, sizeof(struct gsm_mncc)); + connect_ack.callref = trans->callref; + + return mncc_recvmsg(trans->subscr->net, trans, MNCC_SETUP_COMPL_IND, + &connect_ack); +} + +static int gsm48_cc_tx_connect_ack(struct gsm_trans *trans, void *arg) +{ + struct msgb *msg = gsm48_msgb_alloc(); + struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh)); + + gh->msg_type = GSM48_MT_CC_CONNECT_ACK; + + new_cc_state(trans, GSM_CSTATE_ACTIVE); + + return gsm48_conn_sendmsg(msg, trans->conn, trans); +} + +static int gsm48_cc_rx_disconnect(struct gsm_trans *trans, struct msgb *msg) +{ + struct gsm48_hdr *gh = msgb_l3(msg); + unsigned int payload_len = msgb_l3len(msg) - sizeof(*gh); + struct tlv_parsed tp; + struct gsm_mncc disc; + + gsm48_stop_cc_timer(trans); + + new_cc_state(trans, GSM_CSTATE_DISCONNECT_REQ); + + memset(&disc, 0, sizeof(struct gsm_mncc)); + disc.callref = trans->callref; + tlv_parse(&tp, &gsm48_att_tlvdef, gh->data, payload_len, GSM48_IE_CAUSE, 0); + /* cause */ + if (TLVP_PRESENT(&tp, GSM48_IE_CAUSE)) { + disc.fields |= MNCC_F_CAUSE; + gsm48_decode_cause(&disc.cause, + TLVP_VAL(&tp, GSM48_IE_CAUSE)-1); + } + /* facility */ + if (TLVP_PRESENT(&tp, GSM48_IE_FACILITY)) { + disc.fields |= MNCC_F_FACILITY; + gsm48_decode_facility(&disc.facility, + TLVP_VAL(&tp, GSM48_IE_FACILITY)-1); + } + /* user-user */ + if (TLVP_PRESENT(&tp, GSM48_IE_USER_USER)) { + disc.fields |= MNCC_F_USERUSER; + gsm48_decode_useruser(&disc.useruser, + TLVP_VAL(&tp, GSM48_IE_USER_USER)-1); + } + /* ss-version */ + if (TLVP_PRESENT(&tp, GSM48_IE_SS_VERS)) { + disc.fields |= MNCC_F_SSVERSION; + gsm48_decode_ssversion(&disc.ssversion, + TLVP_VAL(&tp, GSM48_IE_SS_VERS)-1); + } + + return mncc_recvmsg(trans->subscr->net, trans, MNCC_DISC_IND, &disc); + +} + +static struct gsm_mncc_cause default_cause = { + .location = GSM48_CAUSE_LOC_PRN_S_LU, + .coding = 0, + .rec = 0, + .rec_val = 0, + .value = GSM48_CC_CAUSE_NORMAL_UNSPEC, + .diag_len = 0, + .diag = { 0 }, +}; + +static int gsm48_cc_tx_disconnect(struct gsm_trans *trans, void *arg) +{ + struct gsm_mncc *disc = arg; + struct msgb *msg = gsm48_msgb_alloc(); + struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh)); + + gh->msg_type = GSM48_MT_CC_DISCONNECT; + + gsm48_stop_cc_timer(trans); + gsm48_start_cc_timer(trans, 0x306, GSM48_T306); + + /* cause */ + if (disc->fields & MNCC_F_CAUSE) + gsm48_encode_cause(msg, 1, &disc->cause); + else + gsm48_encode_cause(msg, 1, &default_cause); + + /* facility */ + if (disc->fields & MNCC_F_FACILITY) + gsm48_encode_facility(msg, 0, &disc->facility); + /* progress */ + if (disc->fields & MNCC_F_PROGRESS) + gsm48_encode_progress(msg, 0, &disc->progress); + /* user-user */ + if (disc->fields & MNCC_F_USERUSER) + gsm48_encode_useruser(msg, 0, &disc->useruser); + + /* store disconnect cause for T306 expiry */ + memcpy(&trans->cc.msg, disc, sizeof(struct gsm_mncc)); + + new_cc_state(trans, GSM_CSTATE_DISCONNECT_IND); + + return gsm48_conn_sendmsg(msg, trans->conn, trans); +} + +static int gsm48_cc_rx_release(struct gsm_trans *trans, struct msgb *msg) +{ + struct gsm48_hdr *gh = msgb_l3(msg); + unsigned int payload_len = msgb_l3len(msg) - sizeof(*gh); + struct tlv_parsed tp; + struct gsm_mncc rel; + int rc; + + gsm48_stop_cc_timer(trans); + + memset(&rel, 0, sizeof(struct gsm_mncc)); + rel.callref = trans->callref; + tlv_parse(&tp, &gsm48_att_tlvdef, gh->data, payload_len, 0, 0); + /* cause */ + if (TLVP_PRESENT(&tp, GSM48_IE_CAUSE)) { + rel.fields |= MNCC_F_CAUSE; + gsm48_decode_cause(&rel.cause, + TLVP_VAL(&tp, GSM48_IE_CAUSE)-1); + } + /* facility */ + if (TLVP_PRESENT(&tp, GSM48_IE_FACILITY)) { + rel.fields |= MNCC_F_FACILITY; + gsm48_decode_facility(&rel.facility, + TLVP_VAL(&tp, GSM48_IE_FACILITY)-1); + } + /* user-user */ + if (TLVP_PRESENT(&tp, GSM48_IE_USER_USER)) { + rel.fields |= MNCC_F_USERUSER; + gsm48_decode_useruser(&rel.useruser, + TLVP_VAL(&tp, GSM48_IE_USER_USER)-1); + } + /* ss-version */ + if (TLVP_PRESENT(&tp, GSM48_IE_SS_VERS)) { + rel.fields |= MNCC_F_SSVERSION; + gsm48_decode_ssversion(&rel.ssversion, + TLVP_VAL(&tp, GSM48_IE_SS_VERS)-1); + } + + if (trans->cc.state == GSM_CSTATE_RELEASE_REQ) { + /* release collision 5.4.5 */ + rc = mncc_recvmsg(trans->subscr->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->subscr->net, trans, MNCC_REL_IND, &rel); + } + + new_cc_state(trans, GSM_CSTATE_NULL); + + trans->callref = 0; + trans_free(trans); + + return rc; +} + +static int gsm48_cc_tx_release(struct gsm_trans *trans, void *arg) +{ + struct gsm_mncc *rel = arg; + struct msgb *msg = gsm48_msgb_alloc(); + struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh)); + + gh->msg_type = GSM48_MT_CC_RELEASE; + + trans->callref = 0; + + 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->subscr->net, trans, + MNCC_REJ_IND, &rel); + break; + case GSM_CSTATE_RELEASE_REQ: + rc = mncc_recvmsg(trans->subscr->net, trans, + MNCC_REL_CNF, &rel); + break; + default: + rc = mncc_recvmsg(trans->subscr->net, trans, + MNCC_REL_IND, &rel); + } + } + + trans->callref = 0; + trans_free(trans); + + return rc; +} + +static int gsm48_cc_tx_release_compl(struct gsm_trans *trans, void *arg) +{ + struct gsm_mncc *rel = arg; + struct msgb *msg = gsm48_msgb_alloc(); + struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh)); + int ret; + + gh->msg_type = GSM48_MT_CC_RELEASE_COMPL; + + trans->callref = 0; + + gsm48_stop_cc_timer(trans); + + /* cause */ + if (rel->fields & MNCC_F_CAUSE) + gsm48_encode_cause(msg, 0, &rel->cause); + /* facility */ + if (rel->fields & MNCC_F_FACILITY) + gsm48_encode_facility(msg, 0, &rel->facility); + /* user-user */ + if (rel->fields & MNCC_F_USERUSER) + gsm48_encode_useruser(msg, 0, &rel->useruser); + + ret = gsm48_conn_sendmsg(msg, trans->conn, trans); + + trans_free(trans); + + return ret; +} + +static int gsm48_cc_rx_facility(struct gsm_trans *trans, struct msgb *msg) +{ + struct gsm48_hdr *gh = msgb_l3(msg); + unsigned int payload_len = msgb_l3len(msg) - sizeof(*gh); + struct tlv_parsed tp; + struct gsm_mncc fac; + + memset(&fac, 0, sizeof(struct gsm_mncc)); + fac.callref = trans->callref; + tlv_parse(&tp, &gsm48_att_tlvdef, gh->data, payload_len, GSM48_IE_FACILITY, 0); + /* facility */ + if (TLVP_PRESENT(&tp, GSM48_IE_FACILITY)) { + fac.fields |= MNCC_F_FACILITY; + gsm48_decode_facility(&fac.facility, + TLVP_VAL(&tp, GSM48_IE_FACILITY)-1); + } + /* ss-version */ + if (TLVP_PRESENT(&tp, GSM48_IE_SS_VERS)) { + fac.fields |= MNCC_F_SSVERSION; + gsm48_decode_ssversion(&fac.ssversion, + TLVP_VAL(&tp, GSM48_IE_SS_VERS)-1); + } + + return mncc_recvmsg(trans->subscr->net, trans, MNCC_FACILITY_IND, &fac); +} + +static int gsm48_cc_tx_facility(struct gsm_trans *trans, void *arg) +{ + struct gsm_mncc *fac = arg; + struct msgb *msg = gsm48_msgb_alloc(); + struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh)); + + gh->msg_type = GSM48_MT_CC_FACILITY; + + /* facility */ + gsm48_encode_facility(msg, 1, &fac->facility); + + return gsm48_conn_sendmsg(msg, trans->conn, trans); +} + +static int gsm48_cc_rx_hold(struct gsm_trans *trans, struct msgb *msg) +{ + struct gsm_mncc hold; + + memset(&hold, 0, sizeof(struct gsm_mncc)); + hold.callref = trans->callref; + return mncc_recvmsg(trans->subscr->net, trans, MNCC_HOLD_IND, &hold); +} + +static int gsm48_cc_tx_hold_ack(struct gsm_trans *trans, void *arg) +{ + struct msgb *msg = gsm48_msgb_alloc(); + struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh)); + + gh->msg_type = GSM48_MT_CC_HOLD_ACK; + + return gsm48_conn_sendmsg(msg, trans->conn, trans); +} + +static int gsm48_cc_tx_hold_rej(struct gsm_trans *trans, void *arg) +{ + struct gsm_mncc *hold_rej = arg; + struct msgb *msg = gsm48_msgb_alloc(); + struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh)); + + gh->msg_type = GSM48_MT_CC_HOLD_REJ; + + /* cause */ + if (hold_rej->fields & MNCC_F_CAUSE) + gsm48_encode_cause(msg, 1, &hold_rej->cause); + else + gsm48_encode_cause(msg, 1, &default_cause); + + return gsm48_conn_sendmsg(msg, trans->conn, trans); +} + +static int gsm48_cc_rx_retrieve(struct gsm_trans *trans, struct msgb *msg) +{ + struct gsm_mncc retrieve; + + memset(&retrieve, 0, sizeof(struct gsm_mncc)); + retrieve.callref = trans->callref; + return mncc_recvmsg(trans->subscr->net, trans, MNCC_RETRIEVE_IND, + &retrieve); +} + +static int gsm48_cc_tx_retrieve_ack(struct gsm_trans *trans, void *arg) +{ + struct msgb *msg = gsm48_msgb_alloc(); + struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh)); + + gh->msg_type = GSM48_MT_CC_RETR_ACK; + + return gsm48_conn_sendmsg(msg, trans->conn, trans); +} + +static int gsm48_cc_tx_retrieve_rej(struct gsm_trans *trans, void *arg) +{ + struct gsm_mncc *retrieve_rej = arg; + struct msgb *msg = gsm48_msgb_alloc(); + struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh)); + + gh->msg_type = GSM48_MT_CC_RETR_REJ; + + /* cause */ + if (retrieve_rej->fields & MNCC_F_CAUSE) + gsm48_encode_cause(msg, 1, &retrieve_rej->cause); + else + gsm48_encode_cause(msg, 1, &default_cause); + + return gsm48_conn_sendmsg(msg, trans->conn, trans); +} + +static int gsm48_cc_rx_start_dtmf(struct gsm_trans *trans, struct msgb *msg) +{ + struct gsm48_hdr *gh = msgb_l3(msg); + unsigned int payload_len = msgb_l3len(msg) - sizeof(*gh); + struct tlv_parsed tp; + struct gsm_mncc dtmf; + + memset(&dtmf, 0, sizeof(struct gsm_mncc)); + dtmf.callref = trans->callref; + tlv_parse(&tp, &gsm48_att_tlvdef, gh->data, payload_len, 0, 0); + /* keypad facility */ + if (TLVP_PRESENT(&tp, GSM48_IE_KPD_FACILITY)) { + dtmf.fields |= MNCC_F_KEYPAD; + gsm48_decode_keypad(&dtmf.keypad, + TLVP_VAL(&tp, GSM48_IE_KPD_FACILITY)-1); + } + + return mncc_recvmsg(trans->subscr->net, trans, MNCC_START_DTMF_IND, &dtmf); +} + +static int gsm48_cc_tx_start_dtmf_ack(struct gsm_trans *trans, void *arg) +{ + struct gsm_mncc *dtmf = arg; + struct msgb *msg = gsm48_msgb_alloc(); + struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh)); + + gh->msg_type = GSM48_MT_CC_START_DTMF_ACK; + + /* keypad */ + if (dtmf->fields & MNCC_F_KEYPAD) + gsm48_encode_keypad(msg, dtmf->keypad); + + return gsm48_conn_sendmsg(msg, trans->conn, trans); +} + +static int gsm48_cc_tx_start_dtmf_rej(struct gsm_trans *trans, void *arg) +{ + struct gsm_mncc *dtmf = arg; + struct msgb *msg = gsm48_msgb_alloc(); + struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh)); + + gh->msg_type = GSM48_MT_CC_START_DTMF_REJ; + + /* cause */ + if (dtmf->fields & MNCC_F_CAUSE) + gsm48_encode_cause(msg, 1, &dtmf->cause); + else + gsm48_encode_cause(msg, 1, &default_cause); + + return gsm48_conn_sendmsg(msg, trans->conn, trans); +} + +static int gsm48_cc_tx_stop_dtmf_ack(struct gsm_trans *trans, void *arg) +{ + struct msgb *msg = gsm48_msgb_alloc(); + struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh)); + + gh->msg_type = GSM48_MT_CC_STOP_DTMF_ACK; + + return gsm48_conn_sendmsg(msg, trans->conn, trans); +} + +static int gsm48_cc_rx_stop_dtmf(struct gsm_trans *trans, struct msgb *msg) +{ + struct gsm_mncc dtmf; + + memset(&dtmf, 0, sizeof(struct gsm_mncc)); + dtmf.callref = trans->callref; + + return mncc_recvmsg(trans->subscr->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); + } + + new_cc_state(trans, GSM_CSTATE_MO_ORIG_MODIFY); + + return mncc_recvmsg(trans->subscr->net, trans, MNCC_MODIFY_IND, &modify); +} + +static int gsm48_cc_tx_modify(struct gsm_trans *trans, void *arg) +{ + struct gsm_mncc *modify = arg; + struct msgb *msg = gsm48_msgb_alloc(); + struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh)); + + gh->msg_type = GSM48_MT_CC_MODIFY; + + gsm48_start_cc_timer(trans, 0x323, GSM48_T323); + + /* bearer capability */ + gsm48_encode_bearer_cap(msg, 1, &modify->bearer_cap); + + new_cc_state(trans, GSM_CSTATE_MO_TERM_MODIFY); + + return gsm48_conn_sendmsg(msg, trans->conn, trans); +} + +static int gsm48_cc_rx_modify_complete(struct gsm_trans *trans, struct msgb *msg) +{ + struct gsm48_hdr *gh = msgb_l3(msg); + unsigned int payload_len = msgb_l3len(msg) - sizeof(*gh); + struct tlv_parsed tp; + struct gsm_mncc modify; + + gsm48_stop_cc_timer(trans); + + memset(&modify, 0, sizeof(struct gsm_mncc)); + modify.callref = trans->callref; + tlv_parse(&tp, &gsm48_att_tlvdef, gh->data, payload_len, GSM48_IE_BEARER_CAP, 0); + /* bearer capability */ + if (TLVP_PRESENT(&tp, GSM48_IE_BEARER_CAP)) { + modify.fields |= MNCC_F_BEARER_CAP; + gsm48_decode_bearer_cap(&modify.bearer_cap, + TLVP_VAL(&tp, GSM48_IE_BEARER_CAP)-1); + } + + new_cc_state(trans, GSM_CSTATE_ACTIVE); + + return mncc_recvmsg(trans->subscr->net, trans, MNCC_MODIFY_CNF, &modify); +} + +static int gsm48_cc_tx_modify_complete(struct gsm_trans *trans, void *arg) +{ + struct gsm_mncc *modify = arg; + struct msgb *msg = gsm48_msgb_alloc(); + struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh)); + + gh->msg_type = GSM48_MT_CC_MODIFY_COMPL; + + /* bearer capability */ + gsm48_encode_bearer_cap(msg, 1, &modify->bearer_cap); + + new_cc_state(trans, GSM_CSTATE_ACTIVE); + + return gsm48_conn_sendmsg(msg, trans->conn, trans); +} + +static int gsm48_cc_rx_modify_reject(struct gsm_trans *trans, struct msgb *msg) +{ + struct gsm48_hdr *gh = msgb_l3(msg); + unsigned int payload_len = msgb_l3len(msg) - sizeof(*gh); + struct tlv_parsed tp; + struct gsm_mncc modify; + + gsm48_stop_cc_timer(trans); + + memset(&modify, 0, sizeof(struct gsm_mncc)); + modify.callref = trans->callref; + tlv_parse(&tp, &gsm48_att_tlvdef, gh->data, payload_len, GSM48_IE_BEARER_CAP, GSM48_IE_CAUSE); + /* bearer capability */ + if (TLVP_PRESENT(&tp, GSM48_IE_BEARER_CAP)) { + modify.fields |= GSM48_IE_BEARER_CAP; + gsm48_decode_bearer_cap(&modify.bearer_cap, + TLVP_VAL(&tp, GSM48_IE_BEARER_CAP)-1); + } + /* 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->subscr->net, trans, MNCC_MODIFY_REJ, &modify); +} + +static int gsm48_cc_tx_modify_reject(struct gsm_trans *trans, void *arg) +{ + struct gsm_mncc *modify = arg; + struct msgb *msg = gsm48_msgb_alloc(); + struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh)); + + gh->msg_type = GSM48_MT_CC_MODIFY_REJECT; + + /* bearer capability */ + gsm48_encode_bearer_cap(msg, 1, &modify->bearer_cap); + /* cause */ + gsm48_encode_cause(msg, 1, &modify->cause); + + new_cc_state(trans, GSM_CSTATE_ACTIVE); + + return gsm48_conn_sendmsg(msg, trans->conn, trans); +} + +static int gsm48_cc_tx_notify(struct gsm_trans *trans, void *arg) +{ + struct gsm_mncc *notify = arg; + struct msgb *msg = gsm48_msgb_alloc(); + struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh)); + + gh->msg_type = GSM48_MT_CC_NOTIFY; + + /* notify */ + gsm48_encode_notify(msg, notify->notify); + + return gsm48_conn_sendmsg(msg, trans->conn, trans); +} + +static int gsm48_cc_rx_notify(struct gsm_trans *trans, struct msgb *msg) +{ + struct gsm48_hdr *gh = msgb_l3(msg); + unsigned int payload_len = msgb_l3len(msg) - sizeof(*gh); +// struct tlv_parsed tp; + struct gsm_mncc notify; + + memset(¬ify, 0, sizeof(struct gsm_mncc)); + notify.callref = trans->callref; +// tlv_parse(&tp, &gsm48_att_tlvdef, gh->data, payload_len); + if (payload_len >= 1) + gsm48_decode_notify(¬ify.notify, gh->data); + + return mncc_recvmsg(trans->subscr->net, trans, MNCC_NOTIFY_IND, ¬ify); +} + +static int gsm48_cc_tx_userinfo(struct gsm_trans *trans, void *arg) +{ + struct gsm_mncc *user = arg; + struct msgb *msg = gsm48_msgb_alloc(); + struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh)); + + gh->msg_type = GSM48_MT_CC_USER_INFO; + + /* user-user */ + if (user->fields & MNCC_F_USERUSER) + gsm48_encode_useruser(msg, 1, &user->useruser); + /* more data */ + if (user->more) + gsm48_encode_more(msg); + + return gsm48_conn_sendmsg(msg, trans->conn, trans); +} + +static int gsm48_cc_rx_userinfo(struct gsm_trans *trans, struct msgb *msg) +{ + struct gsm48_hdr *gh = msgb_l3(msg); + unsigned int payload_len = msgb_l3len(msg) - sizeof(*gh); + struct tlv_parsed tp; + struct gsm_mncc user; + + memset(&user, 0, sizeof(struct gsm_mncc)); + user.callref = trans->callref; + tlv_parse(&tp, &gsm48_att_tlvdef, gh->data, payload_len, GSM48_IE_USER_USER, 0); + /* user-user */ + if (TLVP_PRESENT(&tp, GSM48_IE_USER_USER)) { + user.fields |= MNCC_F_USERUSER; + gsm48_decode_useruser(&user.useruser, + TLVP_VAL(&tp, GSM48_IE_USER_USER)-1); + } + /* more data */ + if (TLVP_PRESENT(&tp, GSM48_IE_MORE_DATA)) + user.more = 1; + + return mncc_recvmsg(trans->subscr->net, trans, MNCC_USERINFO_IND, &user); +} + +static int _gsm48_lchan_modify(struct gsm_trans *trans, void *arg) +{ + struct gsm_mncc *mode = arg; + + return gsm0808_assign_req(trans->conn, mode->lchan_mode, 1); +} + +static struct downstate { + u_int32_t states; + int type; + int (*rout) (struct gsm_trans *trans, void *arg); +} downstatelist[] = { + /* mobile originating call establishment */ + {SBIT(GSM_CSTATE_INITIATED), /* 5.2.1.2 */ + MNCC_CALL_PROC_REQ, gsm48_cc_tx_call_proc}, + {SBIT(GSM_CSTATE_INITIATED) | SBIT(GSM_CSTATE_MO_CALL_PROC), /* 5.2.1.2 | 5.2.1.5 */ + MNCC_ALERT_REQ, gsm48_cc_tx_alerting}, + {SBIT(GSM_CSTATE_INITIATED) | SBIT(GSM_CSTATE_MO_CALL_PROC) | SBIT(GSM_CSTATE_CALL_DELIVERED), /* 5.2.1.2 | 5.2.1.6 | 5.2.1.6 */ + MNCC_SETUP_RSP, gsm48_cc_tx_connect}, + {SBIT(GSM_CSTATE_MO_CALL_PROC), /* 5.2.1.4.2 */ + MNCC_PROGRESS_REQ, gsm48_cc_tx_progress}, + /* mobile terminating call establishment */ + {SBIT(GSM_CSTATE_NULL), /* 5.2.2.1 */ + MNCC_SETUP_REQ, gsm48_cc_tx_setup}, + {SBIT(GSM_CSTATE_CONNECT_REQUEST), + MNCC_SETUP_COMPL_REQ, gsm48_cc_tx_connect_ack}, + /* signalling during call */ + {SBIT(GSM_CSTATE_ACTIVE), + MNCC_NOTIFY_REQ, gsm48_cc_tx_notify}, + {ALL_STATES - SBIT(GSM_CSTATE_NULL) - SBIT(GSM_CSTATE_RELEASE_REQ), + MNCC_FACILITY_REQ, gsm48_cc_tx_facility}, + {ALL_STATES, + MNCC_START_DTMF_RSP, gsm48_cc_tx_start_dtmf_ack}, + {ALL_STATES, + MNCC_START_DTMF_REJ, gsm48_cc_tx_start_dtmf_rej}, + {ALL_STATES, + MNCC_STOP_DTMF_RSP, gsm48_cc_tx_stop_dtmf_ack}, + {SBIT(GSM_CSTATE_ACTIVE), + MNCC_HOLD_CNF, gsm48_cc_tx_hold_ack}, + {SBIT(GSM_CSTATE_ACTIVE), + MNCC_HOLD_REJ, gsm48_cc_tx_hold_rej}, + {SBIT(GSM_CSTATE_ACTIVE), + MNCC_RETRIEVE_CNF, gsm48_cc_tx_retrieve_ack}, + {SBIT(GSM_CSTATE_ACTIVE), + MNCC_RETRIEVE_REJ, gsm48_cc_tx_retrieve_rej}, + {SBIT(GSM_CSTATE_ACTIVE), + MNCC_MODIFY_REQ, gsm48_cc_tx_modify}, + {SBIT(GSM_CSTATE_MO_ORIG_MODIFY), + MNCC_MODIFY_RSP, gsm48_cc_tx_modify_complete}, + {SBIT(GSM_CSTATE_MO_ORIG_MODIFY), + MNCC_MODIFY_REJ, gsm48_cc_tx_modify_reject}, + {SBIT(GSM_CSTATE_ACTIVE), + MNCC_USERINFO_REQ, gsm48_cc_tx_userinfo}, + /* clearing */ + {SBIT(GSM_CSTATE_INITIATED), + MNCC_REJ_REQ, gsm48_cc_tx_release_compl}, + {ALL_STATES - SBIT(GSM_CSTATE_NULL) - SBIT(GSM_CSTATE_DISCONNECT_IND) - SBIT(GSM_CSTATE_RELEASE_REQ) - SBIT(GSM_CSTATE_DISCONNECT_REQ), /* 5.4.4 */ + MNCC_DISC_REQ, gsm48_cc_tx_disconnect}, + {ALL_STATES - SBIT(GSM_CSTATE_NULL) - SBIT(GSM_CSTATE_RELEASE_REQ), /* 5.4.3.2 */ + MNCC_REL_REQ, gsm48_cc_tx_release}, + /* special */ + {ALL_STATES, + MNCC_LCHAN_MODIFY, _gsm48_lchan_modify}, +}; + +#define DOWNSLLEN \ + (sizeof(downstatelist) / sizeof(struct downstate)) + + +int mncc_tx_to_cc(struct gsm_network *net, int msg_type, void *arg) +{ + int i, rc = 0; + struct gsm_trans *trans = NULL, *transt; + struct gsm_subscriber_connection *conn = NULL; + struct gsm_bts *bts = NULL; + struct gsm_mncc *data = arg, rel; + + DEBUGP(DMNCC, "receive message %s\n", get_mncc_name(msg_type)); + + /* handle special messages */ + switch(msg_type) { + case MNCC_BRIDGE: + return tch_bridge(net, arg); + case MNCC_FRAME_DROP: + return tch_recv_mncc(net, data->callref, 0); + case MNCC_FRAME_RECV: + return tch_recv_mncc(net, data->callref, 1); + case GSM_TCHF_FRAME: + /* 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; + } + if (!trans->conn) { + LOGP(DMNCC, LOGL_NOTICE, "TCH frame for trans without conn\n"); + return 0; + } + if (trans->conn->lchan->type != GSM_LCHAN_TCH_F) { + /* 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\n"); + return 0; + } + bts = trans->conn->lchan->ts->trx->bts; + switch (bts->type) { + case GSM_BTS_TYPE_NANOBTS: + 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: + return trau_send_frame(trans->conn->lchan, arg); + default: + DEBUGP(DCC, "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, + data->called.number); + else + subscr = subscr_get_by_imsi(net, data->imsi); + /* 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(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)); + + /* Get a channel */ + trans->paging_request = talloc_zero(subscr->net, struct gsm_network*); + if (!trans->paging_request) { + LOGP(DCC, LOGL_ERROR, "Failed to allocate paging token.\n"); + subscr_put(subscr); + trans_free(trans); + return 0; + } + + *trans->paging_request = subscr->net; + subscr_get_channel(subscr, RSL_CHANNEED_TCH_F, setup_trig_pag_evt, trans->paging_request); + + subscr_put(subscr); + return 0; + } + /* Assign lchan */ + trans->conn = conn; + subscr_put(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 { + u_int32_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); + u_int8_t msg_type = gh->msg_type & 0xbf; + u_int8_t transaction_id = ((gh->proto_discr & 0xf0) ^ 0x80) >> 4; /* flip */ + struct gsm_trans *trans = NULL; + int i, rc = 0; + + if (msg_type & 0x80) { + DEBUGP(DCC, "MSG 0x%2x not defined for PD error\n", msg_type); + return -EINVAL; + } + + /* Find transaction */ + trans = trans_find_by_id(conn->subscr, 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->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; + } + + 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; + + bsc_del_timer(&conn->anch_operation->timeout); + talloc_free(conn->anch_operation); + conn->anch_operation = NULL; +} + +static void anchor_timeout(void *_data) +{ + struct gsm_subscriber_connection *con = _data; + + release_anchor(con); + msc_release_connection(con); +} + +int gsm0408_new_conn(struct gsm_subscriber_connection *conn) +{ + conn->anch_operation = talloc_zero(conn, struct gsm_anchor_operation); + if (!conn->anch_operation) + return -1; + + conn->anch_operation->timeout.data = conn; + conn->anch_operation->timeout.cb = anchor_timeout; + bsc_schedule_timer(&conn->anch_operation->timeout, 5, 0); + return 0; +} + +/* here we get data from the BSC level... */ +int gsm0408_dispatch(struct gsm_subscriber_connection *conn, struct msgb *msg) +{ + struct gsm48_hdr *gh = msgb_l3(msg); + u_int8_t pdisc = gh->proto_discr & 0x0f; + int rc = 0; + + if (silent_call_reroute(conn, msg)) + return silent_call_rx(conn, msg); + + switch (pdisc) { + case GSM48_PDISC_CC: + release_anchor(conn); + rc = gsm0408_rcv_cc(conn, msg); + break; + case GSM48_PDISC_MM: + rc = gsm0408_rcv_mm(conn, msg); + break; + case GSM48_PDISC_RR: + rc = gsm0408_rcv_rr(conn, msg); + break; + case GSM48_PDISC_SMS: + release_anchor(conn); + rc = gsm0411_rcv_sms(conn, msg); + break; + case GSM48_PDISC_MM_GPRS: + case GSM48_PDISC_SM_GPRS: + LOGP(DRLL, LOGL_NOTICE, "Unimplemented " + "GSM 04.08 discriminator 0x%02x\n", pdisc); + break; + case GSM48_PDISC_NC_SS: + release_anchor(conn); + rc = handle_rcv_ussd(conn, msg); + break; + default: + LOGP(DRLL, LOGL_NOTICE, "Unknown " + "GSM 04.08 discriminator 0x%02x\n", pdisc); + break; + } + + return rc; +} + +/* + * This will be ran by the linker when loading the DSO. We use it to + * do system initialization, e.g. registration of signal handlers. + */ +static __attribute__((constructor)) void on_dso_load_0408(void) +{ + register_signal_handler(SS_HO, handle_ho_signal, NULL); + register_signal_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 new file mode 100644 index 000000000..812e758cd --- /dev/null +++ b/openbsc/src/libmsc/gsm_04_11.c @@ -0,0 +1,1240 @@ +/* Point-to-Point (PP) Short Message Service (SMS) + * Support on Mobile Radio Interface + * 3GPP TS 04.11 version 7.1.0 Release 1998 / ETSI TS 100 942 V7.1.0 */ + +/* (C) 2008 by Daniel Willmann + * (C) 2009 by Harald Welte + * (C) 2010 by Holger Hans Peter Freyther + * (C) 2010 by On-Waves + * + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + * + */ + + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define GSM411_ALLOC_SIZE 1024 +#define GSM411_ALLOC_HEADROOM 128 + +void *tall_gsms_ctx; +static u_int32_t new_callref = 0x40000001; + +static const struct value_string cp_cause_strs[] = { + { GSM411_CP_CAUSE_NET_FAIL, "Network Failure" }, + { GSM411_CP_CAUSE_CONGESTION, "Congestion" }, + { GSM411_CP_CAUSE_INV_TRANS_ID, "Invalid Transaction ID" }, + { GSM411_CP_CAUSE_SEMANT_INC_MSG, "Semantically Incorrect Message" }, + { GSM411_CP_CAUSE_INV_MAND_INF, "Invalid Mandatory Information" }, + { GSM411_CP_CAUSE_MSGTYPE_NOTEXIST, "Message Type doesn't exist" }, + { GSM411_CP_CAUSE_MSG_INCOMP_STATE, + "Message incompatible with protocol state" }, + { GSM411_CP_CAUSE_IE_NOTEXIST, "IE does not exist" }, + { GSM411_CP_CAUSE_PROTOCOL_ERR, "Protocol Error" }, + { 0, 0 } +}; + +static const struct value_string rp_cause_strs[] = { + { GSM411_RP_CAUSE_MO_NUM_UNASSIGNED, "(MO) Number not assigned" }, + { GSM411_RP_CAUSE_MO_OP_DET_BARR, "(MO) Operator determined barring" }, + { GSM411_RP_CAUSE_MO_CALL_BARRED, "(MO) Call barred" }, + { GSM411_RP_CAUSE_MO_SMS_REJECTED, "(MO) SMS rejected" }, + { GSM411_RP_CAUSE_MO_DEST_OUT_OF_ORDER, "(MO) Destination out of order" }, + { GSM411_RP_CAUSE_MO_UNIDENTIFIED_SUBSCR, "(MO) Unidentified subscriber" }, + { GSM411_RP_CAUSE_MO_FACILITY_REJ, "(MO) Facility reject" }, + { GSM411_RP_CAUSE_MO_UNKNOWN_SUBSCR, "(MO) Unknown subscriber" }, + { GSM411_RP_CAUSE_MO_NET_OUT_OF_ORDER, "(MO) Network out of order" }, + { GSM411_RP_CAUSE_MO_TEMP_FAIL, "(MO) Temporary failure" }, + { GSM411_RP_CAUSE_MO_CONGESTION, "(MO) Congestion" }, + { GSM411_RP_CAUSE_MO_RES_UNAVAIL, "(MO) Resource unavailable" }, + { GSM411_RP_CAUSE_MO_REQ_FAC_NOTSUBSCR, "(MO) Requested facility not subscribed" }, + { GSM411_RP_CAUSE_MO_REQ_FAC_NOTIMPL, "(MO) Requested facility not implemented" }, + { GSM411_RP_CAUSE_MO_INTERWORKING, "(MO) Interworking" }, + /* valid only for MT */ + { GSM411_RP_CAUSE_MT_MEM_EXCEEDED, "(MT) Memory Exceeded" }, + /* valid for both directions */ + { GSM411_RP_CAUSE_INV_TRANS_REF, "Invalid Transaction Reference" }, + { GSM411_RP_CAUSE_SEMANT_INC_MSG, "Semantically Incorrect Message" }, + { GSM411_RP_CAUSE_INV_MAND_INF, "Invalid Mandatory Information" }, + { GSM411_RP_CAUSE_MSGTYPE_NOTEXIST, "Message Type non-existant" }, + { GSM411_RP_CAUSE_MSG_INCOMP_STATE, "Message incompatible with protocol state" }, + { GSM411_RP_CAUSE_IE_NOTEXIST, "Information Element not existing" }, + { GSM411_RP_CAUSE_PROTOCOL_ERR, "Protocol Error" }, + { 0, NULL } +}; + + +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->sender) + subscr_put(sms->sender); + if (sms->receiver) + subscr_put(sms->receiver); + + talloc_free(sms); +} + +struct gsm_sms *sms_from_text(struct gsm_subscriber *receiver, int dcs, const char *text) +{ + struct gsm_sms *sms = sms_alloc(); + + if (!sms) + return NULL; + + sms->receiver = subscr_get(receiver); + strncpy(sms->text, text, sizeof(sms->text)-1); + + /* FIXME: don't use ID 1 static */ + sms->sender = subscr_get_by_id(receiver->net, 1); + sms->reply_path_req = 0; + sms->status_rep_req = 0; + sms->ud_hdr_ind = 0; + sms->protocol_id = 0; /* implicit */ + sms->data_coding_scheme = dcs; + strncpy(sms->dest_addr, receiver->extension, sizeof(sms->dest_addr)-1); + /* Generate user_data */ + sms->user_data_len = gsm_7bit_encode(sms->user_data, sms->text); + + 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; + dispatch_signal(SS_SMS, sig_no, &sig); +} + +/* + * This should be called whenever all SMS to a given subscriber + * on a given connection has been sent. This will inform the higher + * layers that a channel can be given up. + */ +static void gsm411_release_conn(struct gsm_subscriber_connection *conn) +{ + if (!conn) + return; + + subscr_put_channel(conn->subscr); +} + +struct msgb *gsm411_msgb_alloc(void) +{ + return msgb_alloc_headroom(GSM411_ALLOC_SIZE, GSM411_ALLOC_HEADROOM, + "GSM 04.11"); +} + +static int gsm411_sendmsg(struct gsm_subscriber_connection *conn, struct msgb *msg, u_int8_t link_id) +{ + DEBUGP(DSMS, "GSM4.11 TX %s\n", hexdump(msg->data, msg->len)); + msg->l3h = msg->data; + return gsm0808_submit_dtap(conn, msg, link_id, 1); +} + +/* SMC TC1* is expired */ +static void cp_timer_expired(void *data) +{ + struct gsm_trans *trans = data; + + DEBUGP(DSMS, "SMC Timer TC1* is expired, calling trans_free()\n"); + /* FIXME: we need to re-transmit the last CP-DATA 1..3 times */ + trans_free(trans); +} + +/* Prefix msg with a 04.08/04.11 CP header */ +static int gsm411_cp_sendmsg(struct msgb *msg, struct gsm_trans *trans, + u_int8_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; + + /* mobile originating */ + switch (gh->msg_type) { + case GSM411_MT_CP_DATA: + /* 5.2.3.1.2: enter MO-wait for CP-ack */ + /* 5.2.3.2.3: enter MT-wait for CP-ACK */ + trans->sms.cp_state = GSM411_CPS_WAIT_CP_ACK; + trans->sms.cp_timer.data = trans; + trans->sms.cp_timer.cb = cp_timer_expired; + /* 5.3.2.1: Set Timer TC1A */ + bsc_schedule_timer(&trans->sms.cp_timer, GSM411_TMR_TC1A); + DEBUGP(DSMS, "TX: CP-DATA "); + break; + case GSM411_MT_CP_ACK: + DEBUGP(DSMS, "TX: CP-ACK "); + break; + case GSM411_MT_CP_ERROR: + DEBUGP(DSMS, "TX: CP-ERROR "); + break; + } + + DEBUGPC(DSMS, "trans=%x\n", trans->transaction_id); + + return gsm411_sendmsg(trans->conn, msg, trans->sms.link_id); +} + +/* Prefix msg with a RP-DATA header and send as CP-DATA */ +static int gsm411_rp_sendmsg(struct msgb *msg, struct gsm_trans *trans, + u_int8_t rp_msg_type, u_int8_t rp_msg_ref) +{ + struct gsm411_rp_hdr *rp; + u_int8_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; /* FIXME: Choose randomly */ + + return gsm411_cp_sendmsg(msg, trans, GSM411_MT_CP_DATA); +} + +/* Turn int into semi-octet representation: 98 => 0x89 */ +static u_int8_t bcdify(u_int8_t value) +{ + u_int8_t ret; + + ret = value / 10; + ret |= (value % 10) << 4; + + return ret; +} + +/* Turn semi-octet representation into int: 0x89 => 98 */ +static u_int8_t unbcdify(u_int8_t value) +{ + u_int8_t ret; + + if ((value & 0x0F) > 9 || (value >> 4) > 9) + LOGP(DSMS, LOGL_ERROR, + "unbcdify got too big nibble: 0x%02X\n", value); + + ret = (value&0x0F)*10; + ret += value>>4; + + return ret; +} + +/* Generate 03.40 TP-SCTS */ +static void gsm340_gen_scts(u_int8_t *scts, time_t time) +{ + struct tm *tm = localtime(&time); + + *scts++ = bcdify(tm->tm_year % 100); + *scts++ = bcdify(tm->tm_mon + 1); + *scts++ = bcdify(tm->tm_mday); + *scts++ = bcdify(tm->tm_hour); + *scts++ = bcdify(tm->tm_min); + *scts++ = bcdify(tm->tm_sec); + *scts++ = bcdify(tm->tm_gmtoff/(60*15)); +} + +/* Decode 03.40 TP-SCTS (into utc/gmt timestamp) */ +static time_t gsm340_scts(u_int8_t *scts) +{ + struct tm tm; + + u_int8_t yr = unbcdify(*scts++); + + if (yr <= 80) + tm.tm_year = 100 + yr; + else + tm.tm_year = yr; + tm.tm_mon = unbcdify(*scts++) - 1; + tm.tm_mday = unbcdify(*scts++); + tm.tm_hour = unbcdify(*scts++); + tm.tm_min = unbcdify(*scts++); + tm.tm_sec = unbcdify(*scts++); + /* according to gsm 03.40 time zone is + "expressed in quarters of an hour" */ + tm.tm_gmtoff = unbcdify(*scts++) * 15*60; + + return mktime(&tm); +} + +/* Return the default validity period in minutes */ +static unsigned long gsm340_vp_default(void) +{ + unsigned long minutes; + /* Default validity: two days */ + minutes = 24 * 60 * 2; + return minutes; +} + +/* Decode validity period format 'relative' */ +static unsigned long gsm340_vp_relative(u_int8_t *sms_vp) +{ + /* Chapter 9.2.3.12.1 */ + u_int8_t vp; + unsigned long minutes; + + vp = *(sms_vp); + if (vp <= 143) + minutes = vp + 1 * 5; + else if (vp <= 167) + minutes = 12*60 + (vp-143) * 30; + else if (vp <= 196) + minutes = vp-166 * 60 * 24; + else + minutes = vp-192 * 60 * 24 * 7; + return minutes; +} + +/* Decode validity period format 'absolute' */ +static unsigned long gsm340_vp_absolute(u_int8_t *sms_vp) +{ + /* Chapter 9.2.3.12.2 */ + time_t expires, now; + unsigned long minutes; + + expires = gsm340_scts(sms_vp); + now = time(NULL); + if (expires <= now) + minutes = 0; + else + minutes = (expires-now)/60; + return minutes; +} + +/* Decode validity period format 'relative in integer representation' */ +static unsigned long gsm340_vp_relative_integer(u_int8_t *sms_vp) +{ + u_int8_t vp; + unsigned long minutes; + vp = *(sms_vp); + if (vp == 0) { + LOGP(DSMS, LOGL_ERROR, + "reserved relative_integer validity period\n"); + return gsm340_vp_default(); + } + minutes = vp/60; + return minutes; +} + +/* Decode validity period format 'relative in semi-octet representation' */ +static unsigned long gsm340_vp_relative_semioctet(u_int8_t *sms_vp) +{ + unsigned long minutes; + minutes = unbcdify(*sms_vp++)*60; /* hours */ + minutes += unbcdify(*sms_vp++); /* minutes */ + minutes += unbcdify(*sms_vp++)/60; /* seconds */ + return minutes; +} + +/* decode validity period. return minutes */ +static unsigned long gsm340_validity_period(u_int8_t sms_vpf, u_int8_t *sms_vp) +{ + u_int8_t fi; /* functionality indicator */ + + switch (sms_vpf) { + case GSM340_TP_VPF_RELATIVE: + return gsm340_vp_relative(sms_vp); + case GSM340_TP_VPF_ABSOLUTE: + return gsm340_vp_absolute(sms_vp); + case GSM340_TP_VPF_ENHANCED: + /* Chapter 9.2.3.12.3 */ + fi = *sms_vp++; + /* ignore additional fi */ + if (fi & (1<<7)) sms_vp++; + /* read validity period format */ + switch (fi & 0x7) { + case 0x0: + return gsm340_vp_default(); /* no vpf specified */ + case 0x1: + return gsm340_vp_relative(sms_vp); + case 0x2: + return gsm340_vp_relative_integer(sms_vp); + case 0x3: + return gsm340_vp_relative_semioctet(sms_vp); + default: + /* The GSM spec says that the SC should reject any + unsupported and/or undefined values. FIXME */ + LOGP(DSMS, LOGL_ERROR, + "Reserved enhanced validity period format\n"); + return gsm340_vp_default(); + } + case GSM340_TP_VPF_NONE: + default: + return gsm340_vp_default(); + } +} + +/* determine coding alphabet dependent on GSM 03.38 Section 4 DCS */ +enum sms_alphabet gsm338_get_sms_alphabet(u_int8_t dcs) +{ + u_int8_t cgbits = dcs >> 4; + enum sms_alphabet alpha = DCS_NONE; + + if ((cgbits & 0xc) == 0) { + if (cgbits & 2) { + LOGP(DSMS, LOGL_NOTICE, + "Compressed SMS not supported yet\n"); + return 0xffffffff; + } + + switch ((dcs >> 2)&0x03) { + case 0: + alpha = DCS_7BIT_DEFAULT; + break; + case 1: + alpha = DCS_8BIT_DATA; + break; + case 2: + alpha = DCS_UCS2; + break; + } + } else if (cgbits == 0xc || cgbits == 0xd) + alpha = DCS_7BIT_DEFAULT; + else if (cgbits == 0xe) + alpha = DCS_UCS2; + else if (cgbits == 0xf) { + if (dcs & 4) + alpha = DCS_8BIT_DATA; + else + alpha = DCS_7BIT_DEFAULT; + } + + return alpha; +} + +static int gsm340_rx_sms_submit(struct msgb *msg, struct gsm_sms *gsms) +{ + if (db_sms_store(gsms) != 0) { + LOGP(DSMS, 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(u_int8_t *oa, unsigned int oa_len, + struct gsm_subscriber *subscr) +{ + int len_in_bytes; + + oa[1] = 0xb9; /* networks-specific number, private numbering plan */ + + len_in_bytes = gsm48_encode_bcd_number(oa, oa_len, 1, subscr->extension); + + /* GSM 03.40 tells us the length is in 'useful semi-octets' */ + oa[0] = strlen(subscr->extension) & 0xff; + + return len_in_bytes; +} + +/* generate a msgb containing a TPDU derived from struct gsm_sms, + * returns total size of TPDU */ +static int gsm340_gen_tpdu(struct msgb *msg, struct gsm_sms *sms) +{ + u_int8_t *smsp; + u_int8_t oa[12]; /* max len per 03.40 */ + u_int8_t oa_len = 0; + u_int8_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(oa, sizeof(oa), sms->sender); + 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(DSMS, LOGL_NOTICE, "Unhandled Data Coding Scheme: 0x%02X\n", + sms->data_coding_scheme); + break; + } + + return msg->len - old_msg_len; +} + +/* process an incoming TPDU (called from RP-DATA) + * return value > 0: RP CAUSE for ERROR; < 0: silent error; 0 = success */ +static int gsm340_rx_tpdu(struct gsm_subscriber_connection *conn, struct msgb *msg) +{ + u_int8_t *smsp = msgb_sms(msg); + struct gsm_sms *gsms; + u_int8_t sms_mti, sms_mms, sms_vpf, sms_alphabet, sms_rp; + u_int8_t *sms_vp; + u_int8_t da_len_bytes; + u_int8_t address_lv[12]; /* according to 03.40 / 9.1.2.5 */ + int rc = 0; + + counter_inc(conn->bts->network->stats.sms.submitted); + + gsms = sms_alloc(); + if (!gsms) + return GSM411_RP_CAUSE_MO_NET_OUT_OF_ORDER; + + /* invert those fields where 0 means active/present */ + sms_mti = *smsp & 0x03; + sms_mms = !!(*smsp & 0x04); + sms_vpf = (*smsp & 0x18) >> 3; + gsms->status_rep_req = (*smsp & 0x20); + gsms->ud_hdr_ind = (*smsp & 0x40); + sms_rp = (*smsp & 0x80); + + smsp++; + gsms->msg_ref = *smsp++; + + /* length in bytes of the destination address */ + da_len_bytes = 2 + *smsp/2 + *smsp%2; + if (da_len_bytes > 12) { + LOGP(DSMS, LOGL_ERROR, "Destination Address > 12 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; + /* convert to real number */ + gsm48_decode_bcd_number(gsms->dest_addr, sizeof(gsms->dest_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) { + sms_free(gsms); + return GSM411_RP_CAUSE_MO_NET_OUT_OF_ORDER; + } + + 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(DSMS, LOGL_NOTICE, + "SMS Validity period not implemented: 0x%02x\n", sms_vpf); + return GSM411_RP_CAUSE_MO_NET_OUT_OF_ORDER; + } + 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(gsms->text, smsp, gsms->user_data_len); + break; + case DCS_8BIT_DATA: + case DCS_UCS2: + case DCS_NONE: + break; + } + } + + gsms->sender = subscr_get(conn->subscr); + + LOGP(DSMS, 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(gsms->sender), sms_mti, sms_vpf, gsms->msg_ref, + gsms->protocol_id, gsms->data_coding_scheme, gsms->dest_addr, + gsms->user_data_len, + sms_alphabet == DCS_7BIT_DEFAULT ? gsms->text : + 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); + + /* determine gsms->receiver based on dialled number */ + gsms->receiver = subscr_get_by_extension(conn->bts->network, gsms->dest_addr); + if (!gsms->receiver) { + rc = 1; /* cause 1: unknown subscriber */ + counter_inc(conn->bts->network->stats.sms.no_receiver); + goto out; + } + + 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(DSMS, LOGL_NOTICE, "Unimplemented MTI 0x%02x\n", sms_mti); + rc = GSM411_RP_CAUSE_IE_NOTEXIST; + break; + default: + LOGP(DSMS, 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; + +out: + sms_free(gsms); + + return rc; +} + +static int gsm411_send_rp_ack(struct gsm_trans *trans, u_int8_t msg_ref) +{ + struct msgb *msg = gsm411_msgb_alloc(); + + DEBUGP(DSMS, "TX: SMS RP ACK\n"); + + return gsm411_rp_sendmsg(msg, trans, GSM411_MT_RP_ACK_MT, msg_ref); +} + +static int gsm411_send_rp_error(struct gsm_trans *trans, + u_int8_t msg_ref, u_int8_t cause) +{ + struct msgb *msg = gsm411_msgb_alloc(); + + msgb_tv_put(msg, 1, cause); + + LOGP(DSMS, LOGL_NOTICE, "TX: SMS RP ERROR, cause %d (%s)\n", cause, + get_value_string(rp_cause_strs, cause)); + + return gsm411_rp_sendmsg(msg, trans, GSM411_MT_RP_ERROR_MT, msg_ref); +} + +/* 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, + u_int8_t src_len, u_int8_t *src, + u_int8_t dst_len, u_int8_t *dst, + u_int8_t tpdu_len, u_int8_t *tpdu) +{ + int rc = 0; + + if (src_len && src) + LOGP(DSMS, LOGL_ERROR, "RP-DATA (MO) with SRC ?!?\n"); + + if (!dst_len || !dst || !tpdu_len || !tpdu) { + LOGP(DSMS, 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(DSMS, "DST(%u,%s)\n", dst_len, hexdump(dst, dst_len)); + + rc = gsm340_rx_tpdu(trans->conn, msg); + if (rc == 0) + return gsm411_send_rp_ack(trans, rph->msg_ref); + else if (rc > 0) + return gsm411_send_rp_error(trans, rph->msg_ref, rc); + else + return rc; +} + +/* Receive a 04.11 RP-DATA message in accordance with Section 7.3.1.2 */ +static int gsm411_rx_rp_data(struct msgb *msg, struct gsm_trans *trans, + struct gsm411_rp_hdr *rph) +{ + u_int8_t src_len, dst_len, rpud_len; + u_int8_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(DSMS, "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 (!trans->sms.is_mt) { + LOGP(DSMS, LOGL_ERROR, "RX RP-ACK on a MO transfer ?\n"); + return gsm411_send_rp_error(trans, rph->msg_ref, + GSM411_RP_CAUSE_MSG_INCOMP_STATE); + } + + if (!sms) { + LOGP(DSMS, 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_sent(sms); + + send_signal(S_SMS_DELIVERED, trans, sms, 0); + + sms_free(sms); + trans->sms.sms = NULL; + + /* check for more messages for this subscriber */ + sms = db_sms_get_unsent_for_subscr(trans->subscr); + if (sms) + gsm411_send_sms(trans->conn, sms); + else + gsm411_release_conn(trans->conn); + + /* free the transaction here */ + trans_free(trans); + return 0; +} + +static int gsm411_rx_rp_error(struct msgb *msg, struct gsm_trans *trans, + struct gsm411_rp_hdr *rph) +{ + struct gsm_network *net = trans->conn->bts->network; + struct gsm_sms *sms = trans->sms.sms; + u_int8_t cause_len = rph->data[0]; + u_int8_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(DSMS, LOGL_NOTICE, "%s: RX SMS RP-ERROR, cause %d:%d (%s)\n", + subscr_name(trans->conn->subscr), cause_len, cause, + get_value_string(rp_cause_strs, cause)); + + if (!trans->sms.is_mt) { + LOGP(DSMS, LOGL_ERROR, "RX RP-ERR on a MO transfer ?\n"); +#if 0 + return gsm411_send_rp_error(trans, rph->msg_ref, + GSM411_RP_CAUSE_MSG_INCOMP_STATE); +#endif + } + + if (!sms) { + LOGP(DSMS, 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); + counter_inc(net->stats.sms.rp_err_mem); + } else { + send_signal(S_SMS_UNKNOWN_ERROR, trans, sms, 0); + counter_inc(net->stats.sms.rp_err_other); + } + + sms_free(sms); + trans->sms.sms = NULL; + + return 0; +} + +static int gsm411_rx_rp_smma(struct msgb *msg, struct gsm_trans *trans, + struct gsm411_rp_hdr *rph) +{ + struct gsm_sms *sms; + int rc; + + rc = gsm411_send_rp_ack(trans, rph->msg_ref); + trans->sms.rp_state = GSM411_RPS_IDLE; + + /* 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); + + /* check for more messages for this subscriber */ + sms = db_sms_get_unsent_for_subscr(trans->subscr); + if (sms) + gsm411_send_sms(trans->conn, sms); + else + gsm411_release_conn(trans->conn); + + return rc; +} + +static int gsm411_rx_cp_data(struct msgb *msg, struct gsm48_hdr *gh, + struct gsm_trans *trans) +{ + struct gsm411_rp_hdr *rp_data = (struct gsm411_rp_hdr*)&gh->data; + u_int8_t msg_type = rp_data->msg_type & 0x07; + int rc = 0; + + switch (msg_type) { + case GSM411_MT_RP_DATA_MO: + DEBUGP(DSMS, "RX SMS RP-DATA (MO)\n"); + /* start TR2N and enter 'wait to send RP-ACK state' */ + trans->sms.rp_state = GSM411_RPS_WAIT_TO_TX_RP_ACK; + rc = gsm411_rx_rp_data(msg, trans, rp_data); + break; + case GSM411_MT_RP_ACK_MO: + DEBUGP(DSMS,"RX SMS RP-ACK (MO)\n"); + rc = gsm411_rx_rp_ack(msg, trans, rp_data); + break; + case GSM411_MT_RP_SMMA_MO: + DEBUGP(DSMS, "RX SMS RP-SMMA\n"); + /* start TR2N and enter 'wait to send RP-ACK state' */ + trans->sms.rp_state = GSM411_RPS_WAIT_TO_TX_RP_ACK; + rc = gsm411_rx_rp_smma(msg, trans, rp_data); + break; + case GSM411_MT_RP_ERROR_MO: + rc = gsm411_rx_rp_error(msg, trans, rp_data); + break; + default: + LOGP(DSMS, LOGL_NOTICE, "Invalid RP type 0x%02x\n", msg_type); + rc = gsm411_send_rp_error(trans, rp_data->msg_ref, + GSM411_RP_CAUSE_MSGTYPE_NOTEXIST); + break; + } + + return rc; +} + +/* send CP-ACK to given transaction */ +static int gsm411_tx_cp_ack(struct gsm_trans *trans) +{ + struct msgb *msg = gsm411_msgb_alloc(); + int rc; + + rc = gsm411_cp_sendmsg(msg, trans, GSM411_MT_CP_ACK); + + if (trans->sms.is_mt) { + /* If this is a MT SMS DELIVER, we can clear transaction here */ + trans->sms.cp_state = GSM411_CPS_IDLE; + //trans_free(trans); + } + + return rc; +} + +static int gsm411_tx_cp_error(struct gsm_trans *trans, u_int8_t cause) +{ + struct msgb *msg = gsm411_msgb_alloc(); + u_int8_t *causep; + + LOGP(DSMS, LOGL_NOTICE, "TX CP-ERROR, cause %d (%s)\n", cause, + get_value_string(cp_cause_strs, cause)); + + causep = msgb_put(msg, 1); + *causep = cause; + + return gsm411_cp_sendmsg(msg, trans, GSM411_MT_CP_ERROR); +} + +/* 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); + u_int8_t msg_type = gh->msg_type; + u_int8_t transaction_id = ((gh->proto_discr >> 4) ^ 0x8); /* flip */ + struct gsm_trans *trans; + int rc = 0; + + if (!conn->subscr) + return -EIO; + /* FIXME: send some error message */ + + DEBUGP(DSMS, "trans_id=%x ", transaction_id); + trans = trans_find_by_id(conn->subscr, GSM48_PDISC_SMS, + transaction_id); + if (!trans) { + DEBUGPC(DSMS, "(new) "); + trans = trans_alloc(conn->subscr, GSM48_PDISC_SMS, + transaction_id, new_callref++); + if (!trans) { + DEBUGPC(DSMS, "No memory for trans\n"); + /* FIXME: send some error message */ + return -ENOMEM; + } + trans->sms.cp_state = GSM411_CPS_IDLE; + trans->sms.rp_state = GSM411_RPS_IDLE; + trans->sms.is_mt = 0; + trans->sms.link_id = UM_SAPI_SMS; + + trans->conn = conn; + } + + switch(msg_type) { + case GSM411_MT_CP_DATA: + DEBUGPC(DSMS, "RX SMS CP-DATA\n"); + + /* 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.cp_state == GSM411_CPS_IDLE) { + 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->subscr, + GSM48_PDISC_SMS, i); + if (!ptrans) + continue; + + DEBUGP(DSMS, "Implicit CP-ACK for trans_id=%x\n", i); + + /* Finish it for good */ + bsc_del_timer(&ptrans->sms.cp_timer); + ptrans->sms.cp_state = GSM411_CPS_IDLE; + trans_free(ptrans); + } + } + + /* 5.2.3.1.3: MO state exists when SMC has received + * CP-DATA, including sending of the assoc. CP-ACK */ + /* 5.2.3.2.4: MT state exists when SMC has received + * CP-DATA, including sending of the assoc. CP-ACK */ + trans->sms.cp_state = GSM411_CPS_MM_ESTABLISHED; + + /* SMC instance acknowledges the CP-DATA frame */ + gsm411_tx_cp_ack(trans); + + rc = gsm411_rx_cp_data(msg, gh, trans); +#if 0 + /* Send CP-ACK or CP-ERORR in response */ + if (rc < 0) { + rc = gsm411_tx_cp_error(trans, GSM411_CP_CAUSE_NET_FAIL); + } else + rc = gsm411_tx_cp_ack(trans); +#endif + break; + case GSM411_MT_CP_ACK: + /* previous CP-DATA in this transaction was confirmed */ + DEBUGPC(DSMS, "RX SMS CP-ACK\n"); + /* 5.2.3.1.3: MO state exists when SMC has received CP-ACK */ + /* 5.2.3.2.4: MT state exists when SMC has received CP-ACK */ + trans->sms.cp_state = GSM411_CPS_MM_ESTABLISHED; + /* Stop TC1* after CP-ACK has been received */ + bsc_del_timer(&trans->sms.cp_timer); + + if (!trans->sms.is_mt) { + /* FIXME: we have sent one CP-DATA, which was now + * acknowledged. Check if we want to transfer more, + * i.e. multi-part message */ + trans->sms.cp_state = GSM411_CPS_IDLE; + trans_free(trans); + } + break; + case GSM411_MT_CP_ERROR: + DEBUGPC(DSMS, "RX SMS CP-ERROR, cause %d (%s)\n", gh->data[0], + get_value_string(cp_cause_strs, gh->data[0])); + bsc_del_timer(&trans->sms.cp_timer); + trans->sms.cp_state = GSM411_CPS_IDLE; + trans_free(trans); + break; + default: + DEBUGPC(DSMS, "RX Unimplemented CP msg_type: 0x%02x\n", msg_type); + rc = gsm411_tx_cp_error(trans, GSM411_CP_CAUSE_MSGTYPE_NOTEXIST); + trans->sms.cp_state = GSM411_CPS_IDLE; + trans_free(trans); + break; + } + + 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; + u_int8_t *data, *rp_ud_len; + u_int8_t msg_ref = 42; + int transaction_id; + int rc; + + transaction_id = trans_assign_trans_id(conn->subscr, GSM48_PDISC_SMS, 0); + if (transaction_id == -1) { + LOGP(DSMS, LOGL_ERROR, "No available transaction ids\n"); + send_signal(S_SMS_UNKNOWN_ERROR, NULL, sms, 0); + sms_free(sms); + return -EBUSY; + } + + DEBUGP(DSMS, "send_sms_lchan()\n"); + + /* FIXME: allocate transaction with message reference */ + trans = trans_alloc(conn->subscr, GSM48_PDISC_SMS, + transaction_id, new_callref++); + if (!trans) { + LOGP(DSMS, LOGL_ERROR, "No memory for trans\n"); + send_signal(S_SMS_UNKNOWN_ERROR, NULL, sms, 0); + sms_free(sms); + /* FIXME: send some error message */ + return -ENOMEM; + } + trans->sms.cp_state = GSM411_CPS_IDLE; + trans->sms.rp_state = GSM411_RPS_IDLE; + trans->sms.is_mt = 1; + trans->sms.sms = sms; + trans->sms.link_id = UM_SAPI_SMS; /* FIXME: main or SACCH ? */ + + trans->conn = conn; + + /* Hardcode SMSC Originating Address for now */ + data = (u_int8_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 = (u_int8_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 = (u_int8_t *)msgb_put(msg, 1); + + /* generate the 03.40 TPDU */ + rc = gsm340_gen_tpdu(msg, sms); + if (rc < 0) { + send_signal(S_SMS_UNKNOWN_ERROR, trans, sms, 0); + trans_free(trans); + sms_free(sms); + msgb_free(msg); + return rc; + } + + *rp_ud_len = rc; + + DEBUGP(DSMS, "TX: SMS DELIVER\n"); + + counter_inc(conn->bts->network->stats.sms.delivered); + db_sms_inc_deliver_attempts(trans->sms.sms); + + return gsm411_rp_sendmsg(msg, trans, GSM411_MT_RP_DATA_MT, msg_ref); + /* FIXME: enter 'wait for RP-ACK' state, start TR1N */ +} + +/* 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(DSMS, "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(DSMS, 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; + + /* check if we already have an open lchan to the subscriber. + * if yes, send the SMS this way */ + conn = connection_for_subscr(subscr); + if (conn) { + return gsm411_send_sms(conn, sms); + } + + /* if not, we have to start paging */ + subscr_get_channel(subscr, RSL_CHANNEED_SDCCH, paging_cb_send_sms, sms); + return 0; +} + +void _gsm411_sms_trans_free(struct gsm_trans *trans) +{ + if (trans->sms.sms) { + LOGP(DSMS, 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; + } + + bsc_del_timer(&trans->sms.cp_timer); +} + +void gsm411_sapi_n_reject(struct gsm_subscriber_connection *conn) +{ + struct gsm_subscriber *subscr; + struct gsm_network *net; + struct gsm_trans *trans, *tmp; + + subscr = subscr_get(conn->subscr); + net = conn->bts->network; + + llist_for_each_entry_safe(trans, tmp, &net->trans_list, entry) + if (trans->conn == conn) { + struct gsm_sms *sms = trans->sms.sms; + if (!sms) { + LOGP(DSMS, 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); + } + + subscr_put_channel(subscr); + subscr_put(subscr); +} + diff --git a/openbsc/src/libmsc/gsm_04_80.c b/openbsc/src/libmsc/gsm_04_80.c new file mode 100644 index 000000000..494c319fe --- /dev/null +++ b/openbsc/src/libmsc/gsm_04_80.c @@ -0,0 +1,175 @@ +/* GSM Mobile Radio Interface Layer 3 messages on the A-bis interface + * 3GPP TS 04.08 version 7.21.0 Release 1998 / ETSI TS 100 940 V7.21.0 */ + +/* (C) 2008-2009 by Harald Welte + * (C) 2008, 2009, 2010 by Holger Hans Peter Freyther + * (C) 2009 by Mike Haben + * + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + * + */ + + +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +static inline unsigned char *msgb_wrap_with_TL(struct msgb *msgb, u_int8_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, u_int8_t tag, + u_int8_t value) +{ + uint8_t *data = msgb_push(msgb, 3); + + data[0] = tag; + data[1] = 1; + data[2] = value; + return data; +} + + +/* Send response to a mobile-originated ProcessUnstructuredSS-Request */ +int gsm0480_send_ussd_response(struct gsm_subscriber_connection *conn, + const struct msgb *in_msg, const char *response_text, + const struct ussd_request *req) +{ + struct msgb *msg = gsm48_msgb_alloc(); + struct gsm48_hdr *gh; + u_int8_t *ptr8; + int response_len; + + /* First put the payload text into the message */ + ptr8 = msgb_put(msg, 0); + response_len = gsm_7bit_encode(ptr8, response_text); + msgb_put(msg, response_len); + + /* Then wrap it as an Octet String */ + msgb_wrap_with_TL(msg, ASN1_OCTET_STRING_TAG); + + /* Pre-pend the DCS octet string */ + msgb_push_TLV1(msg, ASN1_OCTET_STRING_TAG, 0x0F); + + /* Then wrap these as a Sequence */ + msgb_wrap_with_TL(msg, GSM_0480_SEQUENCE_TAG); + + /* Pre-pend the operation code */ + msgb_push_TLV1(msg, GSM0480_OPERATION_CODE, + GSM0480_OP_CODE_PROCESS_USS_REQ); + + /* Wrap the operation code and IA5 string as a sequence */ + msgb_wrap_with_TL(msg, GSM_0480_SEQUENCE_TAG); + + /* Pre-pend the invoke ID */ + msgb_push_TLV1(msg, GSM0480_COMPIDTAG_INVOKE_ID, req->invoke_id); + + /* Wrap this up as a Return Result component */ + msgb_wrap_with_TL(msg, GSM0480_CTYPE_RETURN_RESULT); + + /* Wrap the component in a Facility message */ + msgb_wrap_with_TL(msg, GSM0480_IE_FACILITY); + + /* And finally pre-pend the L3 header */ + gh = (struct gsm48_hdr *) msgb_push(msg, sizeof(*gh)); + gh->proto_discr = GSM48_PDISC_NC_SS | req->transaction_id + | (1<<7); /* TI direction = 1 */ + gh->msg_type = GSM0480_MTYPE_RELEASE_COMPLETE; + + return gsm0808_submit_dtap(conn, msg, 0, 0); +} + +int gsm0480_send_ussd_reject(struct gsm_subscriber_connection *conn, + const struct msgb *in_msg, + const struct ussd_request *req) +{ + struct msgb *msg = gsm48_msgb_alloc(); + struct gsm48_hdr *gh; + + /* First insert the problem code */ + msgb_push_TLV1(msg, GSM_0480_PROBLEM_CODE_TAG_GENERAL, + GSM_0480_GEN_PROB_CODE_UNRECOGNISED); + + /* Before it insert the invoke ID */ + msgb_push_TLV1(msg, GSM0480_COMPIDTAG_INVOKE_ID, req->invoke_id); + + /* Wrap this up as a Reject component */ + msgb_wrap_with_TL(msg, GSM0480_CTYPE_REJECT); + + /* Wrap the component in a Facility message */ + msgb_wrap_with_TL(msg, GSM0480_IE_FACILITY); + + /* And finally pre-pend the L3 header */ + gh = (struct gsm48_hdr *) msgb_push(msg, sizeof(*gh)); + gh->proto_discr = GSM48_PDISC_NC_SS; + gh->proto_discr |= req->transaction_id | (1<<7); /* TI direction = 1 */ + gh->msg_type = GSM0480_MTYPE_RELEASE_COMPLETE; + + return gsm0808_submit_dtap(conn, msg, 0, 0); +} + +int gsm0480_send_ussdNotify(struct gsm_subscriber_connection *conn, int level, const char *text) +{ + struct gsm48_hdr *gh; + struct msgb *msg; + + msg = gsm0480_create_unstructuredSS_Notify(level, text); + if (!msg) + return -1; + + gsm0480_wrap_invoke(msg, GSM0480_OP_CODE_USS_NOTIFY, 0); + gsm0480_wrap_facility(msg); + + /* And finally pre-pend the L3 header */ + gh = (struct gsm48_hdr *) msgb_push(msg, sizeof(*gh)); + gh->proto_discr = GSM48_PDISC_NC_SS; + gh->msg_type = GSM0480_MTYPE_REGISTER; + + return gsm0808_submit_dtap(conn, msg, 0, 0); +} + +int gsm0480_send_releaseComplete(struct gsm_subscriber_connection *conn) +{ + struct gsm48_hdr *gh; + struct msgb *msg; + + msg = gsm48_msgb_alloc(); + if (!msg) + return -1; + + gh = (struct gsm48_hdr *) msgb_push(msg, sizeof(*gh)); + gh->proto_discr = GSM48_PDISC_NC_SS; + gh->msg_type = GSM0480_MTYPE_RELEASE_COMPLETE; + + return gsm0808_submit_dtap(conn, msg, 0, 0); +} diff --git a/openbsc/src/libmsc/gsm_subscriber.c b/openbsc/src/libmsc/gsm_subscriber.c new file mode 100644 index 000000000..db61f25aa --- /dev/null +++ b/openbsc/src/libmsc/gsm_subscriber.c @@ -0,0 +1,410 @@ +/* The concept of a subscriber for the MSC, roughly HLR/VLR functionality */ + +/* (C) 2008 by Harald Welte + * (C) 2009 by Holger Hans Peter Freyther + * + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + * + */ + +#include +#include +#include +#include +#include + +#include + +#include + +#include +#include +#include +#include +#include +#include + +void *tall_sub_req_ctx; + +extern struct llist_head *subscr_bsc_active_subscriber(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; + + /* back reference */ + struct gsm_subscriber *subscr; + + /* the requested channel type */ + int channel_type; + + /* what did we do */ + int state; + + /* the callback data */ + gsm_cbfn *cbfn; + void *param; +}; + +enum { + REQ_STATE_INITIAL, + REQ_STATE_QUEUED, + REQ_STATE_PAGED, + REQ_STATE_FAILED_START, + REQ_STATE_DISPATCHED, +}; + +/* + * 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; + struct gsm_subscriber_connection *conn = data; + struct gsm_subscriber *subscr = param; + struct paging_signal_data sig_data; + + /* There is no request anymore... */ + if (llist_empty(&subscr->requests)) + return -1; + + /* Dispatch signal */ + sig_data.subscr = subscr; + sig_data.bts = conn ? conn->bts : NULL; + sig_data.conn = conn; + sig_data.paging_result = event; + dispatch_signal( + SS_PAGING, + event == GSM_PAGING_SUCCEEDED ? + S_PAGING_SUCCEEDED : S_PAGING_EXPIRED, + &sig_data + ); + + /* + * FIXME: What to do with paging requests coming during + * this callback? We must be sure to not start paging when + * we have an active connection to a subscriber and to make + * the subscr_put_channel work as required... + */ + request = (struct subscr_request *)subscr->requests.next; + request->state = REQ_STATE_DISPATCHED; + llist_del(&request->entry); + subscr->in_callback = 1; + request->cbfn(hooknum, event, msg, data, request->param); + subscr->in_callback = 0; + + if (event != GSM_PAGING_SUCCEEDED) { + /* + * This is a workaround for a bigger issue. We have + * issued paging that might involve multiple BTSes + * and one of them have failed now. We will stop the + * other paging requests as well as the next timeout + * would work on the next paging request and the queue + * will do bad things. This should be fixed by counting + * the outstanding results. + */ + paging_request_stop(NULL, subscr, NULL, NULL); + subscr_put_channel(subscr); + } + + subscr_put(subscr); + talloc_free(request); + 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); +} + + +static void subscr_send_paging_request(struct gsm_subscriber *subscr) +{ + struct subscr_request *request; + int rc; + + assert(!llist_empty(&subscr->requests)); + + request = (struct subscr_request *)subscr->requests.next; + request->state = REQ_STATE_PAGED; + rc = paging_request(subscr->net, subscr, request->channel_type, + subscr_paging_cb, subscr); + + /* paging failed, quit now */ + if (rc <= 0) { + request->state = REQ_STATE_FAILED_START; + subscr_paging_cb(GSM_HOOK_RR_PAGING, GSM_PAGING_BUSY, + NULL, NULL, subscr); + } +} + +void subscr_get_channel(struct gsm_subscriber *subscr, + int type, gsm_cbfn *cbfn, void *param) +{ + struct subscr_request *request; + + request = talloc(tall_sub_req_ctx, struct subscr_request); + if (!request) { + if (cbfn) + cbfn(GSM_HOOK_RR_PAGING, GSM_PAGING_OOM, + NULL, NULL, param); + return; + } + + memset(request, 0, sizeof(*request)); + request->subscr = subscr_get(subscr); + request->channel_type = type; + request->cbfn = cbfn; + request->param = param; + request->state = REQ_STATE_INITIAL; + + /* + * FIXME: We might be able to assign more than one + * channel, e.g. voice and SMS submit at the same + * time. + */ + if (!subscr->in_callback && llist_empty(&subscr->requests)) { + /* add to the list, send a request */ + llist_add_tail(&request->entry, &subscr->requests); + subscr_send_paging_request(subscr); + } else { + /* this will be picked up later, from subscr_put_channel */ + llist_add_tail(&request->entry, &subscr->requests); + request->state = REQ_STATE_QUEUED; + } +} + +void subscr_put_channel(struct gsm_subscriber *subscr) +{ + /* + * FIXME: Continue with other requests now... by checking + * the gsm_subscriber inside the gsm_lchan. Drop the ref count + * of the lchan after having asked the next requestee to handle + * the channel. + */ + /* + * FIXME: is the lchan is of a different type we could still + * issue an immediate assignment for another channel and then + * close this one. + */ + /* + * Currently we will drop the last ref of the lchan which + * will result in a channel release on RSL and we will start + * the paging. This should work most of the time as the MS + * will listen to the paging requests before we timeout + */ + + if (subscr && !llist_empty(&subscr->requests)) + subscr_send_paging_request(subscr); +} + + +struct gsm_subscriber *subscr_get_by_tmsi(struct gsm_network *net, + u_int32_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_subscriber(), entry) { + if (tmsi == subscr->tmsi) + return subscr_get(subscr); + } + + sprintf(tmsi_string, "%u", tmsi); + return db_get_subscriber(net, GSM_SUBSCRIBER_TMSI, tmsi_string); +} + +struct gsm_subscriber *subscr_get_by_imsi(struct gsm_network *net, + const char *imsi) +{ + struct gsm_subscriber *subscr; + + llist_for_each_entry(subscr, subscr_bsc_active_subscriber(), entry) { + if (strcmp(subscr->imsi, imsi) == 0) + return subscr_get(subscr); + } + + return db_get_subscriber(net, GSM_SUBSCRIBER_IMSI, imsi); +} + +struct gsm_subscriber *subscr_get_by_extension(struct gsm_network *net, + const char *ext) +{ + struct gsm_subscriber *subscr; + + llist_for_each_entry(subscr, subscr_bsc_active_subscriber(), entry) { + if (strcmp(subscr->extension, ext) == 0) + return subscr_get(subscr); + } + + return db_get_subscriber(net, GSM_SUBSCRIBER_EXTENSION, ext); +} + +struct gsm_subscriber *subscr_get_by_id(struct gsm_network *net, + unsigned long long id) +{ + struct gsm_subscriber *subscr; + char buf[32]; + sprintf(buf, "%llu", id); + + llist_for_each_entry(subscr, subscr_bsc_active_subscriber(), entry) { + if (subscr->id == id) + return subscr_get(subscr); + } + + return db_get_subscriber(net, GSM_SUBSCRIBER_ID, buf); +} + + +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->net = bts->network; + /* 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); + rc = db_sync_subscriber(s); + db_subscriber_update(s); + dispatch_signal(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); + dispatch_signal(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); +} + +int subscr_pending_requests(struct gsm_subscriber *sub) +{ + struct subscr_request *req; + int pending = 0; + + llist_for_each_entry(req, &sub->requests, entry) + pending += 1; + + return pending; +} + +int subscr_pending_clear(struct gsm_subscriber *sub) +{ + int deleted = 0; + struct subscr_request *req, *tmp; + + llist_for_each_entry_safe(req, tmp, &sub->requests, entry) { + subscr_put(req->subscr); + llist_del(&req->entry); + talloc_free(req); + deleted += 1; + } + + return deleted; +} + +int subscr_pending_dump(struct gsm_subscriber *sub, struct vty *vty) +{ + struct subscr_request *req; + + vty_out(vty, "Pending Requests for Subscriber %llu.%s", sub->id, VTY_NEWLINE); + llist_for_each_entry(req, &sub->requests, entry) { + vty_out(vty, "Channel type: %d State: %d Sub: %llu.%s", + req->channel_type, req->state, req->subscr->id, VTY_NEWLINE); + } + + return 0; +} + +int subscr_pending_kick(struct gsm_subscriber *sub) +{ + subscr_put_channel(sub); + return 0; +} diff --git a/openbsc/src/libmsc/mncc.c b/openbsc/src/libmsc/mncc.c new file mode 100644 index 000000000..3630b91b4 --- /dev/null +++ b/openbsc/src/libmsc/mncc.c @@ -0,0 +1,110 @@ +/* mncc.c - utility routines for the MNCC API between the 04.08 + * message parsing and the actual Call Control logic */ + +/* (C) 2008-2009 by Harald Welte + * (C) 2009 by Andreas Eversberg + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + * + */ + + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +static struct mncc_names { + char *name; + int value; +} mncc_names[] = { + {"MNCC_SETUP_REQ", 0x0101}, + {"MNCC_SETUP_IND", 0x0102}, + {"MNCC_SETUP_RSP", 0x0103}, + {"MNCC_SETUP_CNF", 0x0104}, + {"MNCC_SETUP_COMPL_REQ",0x0105}, + {"MNCC_SETUP_COMPL_IND",0x0106}, + {"MNCC_CALL_CONF_IND", 0x0107}, + {"MNCC_CALL_PROC_REQ", 0x0108}, + {"MNCC_PROGRESS_REQ", 0x0109}, + {"MNCC_ALERT_REQ", 0x010a}, + {"MNCC_ALERT_IND", 0x010b}, + {"MNCC_NOTIFY_REQ", 0x010c}, + {"MNCC_NOTIFY_IND", 0x010d}, + {"MNCC_DISC_REQ", 0x010e}, + {"MNCC_DISC_IND", 0x010f}, + {"MNCC_REL_REQ", 0x0110}, + {"MNCC_REL_IND", 0x0111}, + {"MNCC_REL_CNF", 0x0112}, + {"MNCC_FACILITY_REQ", 0x0113}, + {"MNCC_FACILITY_IND", 0x0114}, + {"MNCC_START_DTMF_IND", 0x0115}, + {"MNCC_START_DTMF_RSP", 0x0116}, + {"MNCC_START_DTMF_REJ", 0x0117}, + {"MNCC_STOP_DTMF_IND", 0x0118}, + {"MNCC_STOP_DTMF_RSP", 0x0119}, + {"MNCC_MODIFY_REQ", 0x011a}, + {"MNCC_MODIFY_IND", 0x011b}, + {"MNCC_MODIFY_RSP", 0x011c}, + {"MNCC_MODIFY_CNF", 0x011d}, + {"MNCC_MODIFY_REJ", 0x011e}, + {"MNCC_HOLD_IND", 0x011f}, + {"MNCC_HOLD_CNF", 0x0120}, + {"MNCC_HOLD_REJ", 0x0121}, + {"MNCC_RETRIEVE_IND", 0x0122}, + {"MNCC_RETRIEVE_CNF", 0x0123}, + {"MNCC_RETRIEVE_REJ", 0x0124}, + {"MNCC_USERINFO_REQ", 0x0125}, + {"MNCC_USERINFO_IND", 0x0126}, + {"MNCC_REJ_REQ", 0x0127}, + {"MNCC_REJ_IND", 0x0128}, + + {"MNCC_BRIDGE", 0x0200}, + {"MNCC_FRAME_RECV", 0x0201}, + {"MNCC_FRAME_DROP", 0x0202}, + {"MNCC_LCHAN_MODIFY", 0x0203}, + + {"GSM_TCH_FRAME", 0x0300}, + + {NULL, 0} }; + +char *get_mncc_name(int value) +{ + int i; + + for (i = 0; mncc_names[i].name; i++) { + if (mncc_names[i].value == value) + return mncc_names[i].name; + } + + return "MNCC_Unknown"; +} + +void mncc_set_cause(struct gsm_mncc *data, int loc, int val) +{ + data->fields |= MNCC_F_CAUSE; + data->cause.location = loc; + data->cause.value = val; +} + diff --git a/openbsc/src/libmsc/mncc_builtin.c b/openbsc/src/libmsc/mncc_builtin.c new file mode 100644 index 000000000..0226b2748 --- /dev/null +++ b/openbsc/src/libmsc/mncc_builtin.c @@ -0,0 +1,411 @@ +/* mncc_builtin.c - default, minimal built-in MNCC Application for + * standalone bsc_hack (netowrk-in-the-box mode) */ + +/* (C) 2008-2010 by Harald Welte + * (C) 2009 by Andreas Eversberg + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + */ + + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +void *tall_call_ctx; + +static LLIST_HEAD(call_list); + +static u_int32_t new_callref = 0x00000001; + +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(u_int32_t callref) +{ + struct gsm_call *callt; + + llist_for_each_entry(callt, &call_list, entry) { + if (callt->callref == callref) + return callt; + } + return NULL; +} + + +/* on incoming call, look up database and send setup to remote subscr. */ +static int mncc_setup_ind(struct gsm_call *call, int msg_type, + struct gsm_mncc *setup) +{ + struct gsm_mncc mncc; + struct gsm_call *remote; + + memset(&mncc, 0, sizeof(struct gsm_mncc)); + mncc.callref = call->callref; + + /* already have remote call */ + if (call->remote_ref) + return 0; + + /* transfer mode 1 would be packet mode, which was never specified */ + if (setup->bearer_cap.mode != 0) { + LOGP(DMNCC, LOGL_NOTICE, "(call %x) We don't support " + "packet mode\n", call->callref); + mncc_set_cause(&mncc, GSM48_CAUSE_LOC_PRN_S_LU, + GSM48_CC_CAUSE_BEARER_CA_UNAVAIL); + goto out_reject; + } + + /* we currently only do speech */ + if (setup->bearer_cap.transfer != GSM_MNCC_BCAP_SPEECH) { + LOGP(DMNCC, LOGL_NOTICE, "(call %x) We only support " + "voice calls\n", call->callref); + mncc_set_cause(&mncc, GSM48_CAUSE_LOC_PRN_S_LU, + GSM48_CC_CAUSE_BEARER_CA_UNAVAIL); + goto out_reject; + } + + /* create remote call */ + if (!(remote = talloc(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; + + /* modify mode */ + memset(&mncc, 0, sizeof(struct gsm_mncc)); + mncc.callref = call->callref; + mncc.lchan_mode = GSM48_CMODE_SPEECH_EFR; + DEBUGP(DMNCC, "(call %x) Modify channel mode.\n", call->callref); + mncc_tx_to_cc(call->net, MNCC_LCHAN_MODIFY, &mncc); + + /* 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); + + /* 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; + u_int32_t refs[2]; + + /* acknowledge connect */ + memset(&connect_ack, 0, sizeof(struct gsm_mncc)); + connect_ack.callref = call->callref; + DEBUGP(DMNCC, "(call %x) Acknowledge SETUP.\n", call->callref); + mncc_tx_to_cc(call->net, MNCC_SETUP_COMPL_REQ, &connect_ack); + + /* send connect message to remote */ + if (!(remote = get_call_ref(call->remote_ref))) + return 0; + connect->callref = remote->callref; + DEBUGP(DMNCC, "(call %x) Sending CONNECT to remote.\n", call->callref); + mncc_tx_to_cc(remote->net, MNCC_SETUP_RSP, connect); + + /* bridge tch */ + refs[0] = call->callref; + refs[1] = call->remote_ref; + DEBUGP(DMNCC, "(call %x) Bridging with remote.\n", call->callref); + + /* in direct mode, we always have to bridge the channels */ + if (ipacc_rtp_direct) + return mncc_tx_to_cc(call->net, MNCC_BRIDGE, refs); + + /* proxy mode */ + if (!net->handover.active) { + /* in the no-handover case, we can bridge, i.e. use + * the old RTP proxy code */ + return mncc_tx_to_cc(call->net, MNCC_BRIDGE, refs); + } else { + /* in case of handover, we need to re-write the RTP + * SSRC, sequence and timestamp values and thus + * need to enable RTP receive for both directions */ + memset(&frame_recv, 0, sizeof(struct gsm_mncc)); + frame_recv.callref = call->callref; + mncc_tx_to_cc(call->net, MNCC_FRAME_RECV, &frame_recv); + frame_recv.callref = call->remote_ref; + return mncc_tx_to_cc(call->net, MNCC_FRAME_RECV, &frame_recv); + } +} + +static int mncc_disc_ind(struct gsm_call *call, int msg_type, + struct gsm_mncc *disc) +{ + struct gsm_call *remote; + + /* send release */ + DEBUGP(DMNCC, "(call %x) Releasing call with cause %d\n", + call->callref, disc->cause.value); + mncc_tx_to_cc(call->net, MNCC_REL_REQ, disc); + + /* send disc to remote */ + if (!(remote = get_call_ref(call->remote_ref))) { + return 0; + } + disc->callref = remote->callref; + DEBUGP(DMNCC, "(call %x) Disconnecting remote with cause %d\n", + remote->callref, disc->cause.value); + return mncc_tx_to_cc(remote->net, MNCC_DISC_REQ, disc); +} + +static int mncc_rel_ind(struct gsm_call *call, int msg_type, struct gsm_mncc *rel) +{ + struct gsm_call *remote; + + /* send release to remote */ + if (!(remote = get_call_ref(call->remote_ref))) { + free_call(call); + return 0; + } + + rel->callref = remote->callref; + DEBUGP(DMNCC, "(call %x) Releasing remote with cause %d\n", + call->callref, rel->cause.value); + + /* + * Release this side of the call right now. Otherwise we end up + * in this method for the other call and will also try to release + * it and then we will end up with a double free and a crash + */ + free_call(call); + mncc_tx_to_cc(remote->net, MNCC_REL_REQ, rel); + + return 0; +} + +static int mncc_rel_cnf(struct gsm_call *call, int msg_type, struct gsm_mncc *rel) +{ + free_call(call); + return 0; +} + +/* receiving a TCH/F frame from the BSC code */ +static int mncc_rcv_tchf(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); + } + + switch (msg_type) { + case GSM_TCHF_FRAME: + case GSM_TCHF_FRAME_EFR: + break; + default: + DEBUGP(DMNCC, "(call %x) Received message %s\n", call->callref, + get_mncc_name(msg_type)); + break; + } + + 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 = GSM48_CMODE_SPEECH_EFR; + mncc_tx_to_cc(call->net, MNCC_LCHAN_MODIFY, data); + break; + case MNCC_ALERT_IND: + rc = mncc_alert_ind(call, msg_type, arg); + break; + case MNCC_NOTIFY_IND: + rc = mncc_notify_ind(call, msg_type, arg); + break; + case MNCC_DISC_IND: + rc = mncc_disc_ind(call, msg_type, arg); + break; + case MNCC_REL_IND: + case MNCC_REJ_IND: + rc = mncc_rel_ind(call, msg_type, arg); + break; + case MNCC_REL_CNF: + rc = mncc_rel_cnf(call, msg_type, arg); + break; + case MNCC_FACILITY_IND: + break; + case MNCC_START_DTMF_IND: + break; + case MNCC_STOP_DTMF_IND: + break; + case MNCC_MODIFY_IND: + mncc_set_cause(data, GSM48_CAUSE_LOC_PRN_S_LU, + GSM48_CC_CAUSE_SERV_OPT_UNIMPL); + DEBUGP(DMNCC, "(call %x) Rejecting MODIFY with cause %d\n", + call->callref, data->cause.value); + rc = mncc_tx_to_cc(net, MNCC_MODIFY_REJ, data); + break; + case MNCC_MODIFY_CNF: + break; + case MNCC_HOLD_IND: + mncc_set_cause(data, GSM48_CAUSE_LOC_PRN_S_LU, + GSM48_CC_CAUSE_SERV_OPT_UNIMPL); + DEBUGP(DMNCC, "(call %x) Rejecting HOLD with cause %d\n", + call->callref, data->cause.value); + rc = mncc_tx_to_cc(net, MNCC_HOLD_REJ, data); + break; + case MNCC_RETRIEVE_IND: + mncc_set_cause(data, GSM48_CAUSE_LOC_PRN_S_LU, + GSM48_CC_CAUSE_SERV_OPT_UNIMPL); + DEBUGP(DMNCC, "(call %x) Rejecting RETRIEVE with cause %d\n", + call->callref, data->cause.value); + rc = mncc_tx_to_cc(net, MNCC_RETRIEVE_REJ, data); + break; + case GSM_TCHF_FRAME: + case GSM_TCHF_FRAME_EFR: + rc = mncc_rcv_tchf(call, msg_type, arg); + break; + default: + LOGP(DMNCC, LOGL_NOTICE, "(call %x) Message unhandled\n", callref); + break; + } + +out_free: + talloc_free(msg); + + return rc; +} diff --git a/openbsc/src/libmsc/mncc_sock.c b/openbsc/src/libmsc/mncc_sock.c new file mode 100644 index 000000000..2eef7c86e --- /dev/null +++ b/openbsc/src/libmsc/mncc_sock.c @@ -0,0 +1,337 @@ +/* mncc_sock.c: Tie the MNCC interface to a unix domain socket */ + +/* (C) 2008-2010 by Harald Welte + * (C) 2009 by Andreas Eversberg + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include + +struct mncc_sock_state { + struct gsm_network *net; + struct bsc_fd listen_bfd; /* fd for listen socket */ + struct bsc_fd conn_bfd; /* fd for connection to lcr */ +}; + +/* FIXME: avoid this */ +static struct mncc_sock_state *g_state; + +/* 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 (g_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 (msg_type != GSM_TCHF_FRAME && + msg_type != GSM_TCHF_FRAME_EFR) { + /* 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); + g_state->conn_bfd.when |= BSC_FD_WRITE; + return 0; +} + +void mncc_sock_write_pending(void) +{ + g_state->conn_bfd.when |= BSC_FD_WRITE; +} + +/* FIXME: move this to libosmocore */ +int osmo_unixsock_listen(struct bsc_fd *bfd, int type, const char *path); + +static void mncc_sock_close(struct mncc_sock_state *state) +{ + struct bsc_fd *bfd = &state->conn_bfd; + + LOGP(DMNCC, LOGL_NOTICE, "MNCC Socket has LOST connection\n"); + + close(bfd->fd); + bfd->fd = -1; + bsc_unregister_fd(bfd); + + /* re-enable the generation of ACCEPT for new connections */ + state->listen_bfd.when |= BSC_FD_READ; + + /* FIXME: make sure we don't enqueue anymore */ + + /* 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 bsc_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 bsc_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; + + /* 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; + } + /* _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 bsc_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; +} + +/* accept a new connection */ +static int mncc_sock_accept(struct bsc_fd *bfd, unsigned int flags) +{ + struct mncc_sock_state *state = (struct mncc_sock_state *)bfd->data; + struct bsc_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 (bsc_register_fd(conn_bfd) != 0) { + LOGP(DMNCC, LOGL_ERROR, "Failed to register new connection fd\n"); + close(conn_bfd->fd); + conn_bfd->fd = -1; + state->listen_bfd.when |= ~BSC_FD_READ; + return -1; + } + + LOGP(DMNCC, LOGL_NOTICE, "MNCC Socket has connection with external " + "call control application\n"); + + return 0; +} + + +int mncc_sock_init(struct gsm_network *net) +{ + struct mncc_sock_state *state; + struct bsc_fd *bfd; + int rc; + + state = talloc_zero(tall_bsc_ctx, struct mncc_sock_state); + if (!state) + return -ENOMEM; + + state->net = net; + state->conn_bfd.fd = -1; + + bfd = &state->listen_bfd; + + rc = osmo_unixsock_listen(bfd, SOCK_SEQPACKET, "/tmp/bsc_mncc"); + if (rc < 0) { + LOGP(DMNCC, LOGL_ERROR, "Could not create unix socket: %s\n", + strerror(errno)); + talloc_free(state); + return rc; + } + + bfd->when = BSC_FD_READ; + bfd->cb = mncc_sock_accept; + bfd->data = state; + + rc = bsc_register_fd(bfd); + if (rc < 0) { + LOGP(DMNCC, LOGL_ERROR, "Could not register listen fd: %d\n", rc); + close(bfd->fd); + talloc_free(state); + return rc; + } + + g_state = state; + + return 0; +} + +/* FIXME: move this to libosmocore */ +int osmo_unixsock_listen(struct bsc_fd *bfd, int type, const char *path) +{ + struct sockaddr_un local; + unsigned int namelen; + int rc; + + bfd->fd = socket(AF_UNIX, type, 0); + + if (bfd->fd < 0) { + fprintf(stderr, "Failed to create Unix Domain Socket.\n"); + return -1; + } + + local.sun_family = AF_UNIX; + strncpy(local.sun_path, path, sizeof(local.sun_path)); + local.sun_path[sizeof(local.sun_path) - 1] = '\0'; + unlink(local.sun_path); + + /* we use the same magic that X11 uses in Xtranssock.c for + * calculating the proper length of the sockaddr */ +#if defined(BSD44SOCKETS) || defined(__UNIXWARE__) + local.sun_len = strlen(local.sun_path); +#endif +#if defined(BSD44SOCKETS) || defined(SUN_LEN) + namelen = SUN_LEN(&local); +#else + namelen = strlen(local.sun_path) + + offsetof(struct sockaddr_un, sun_path); +#endif + + rc = bind(bfd->fd, (struct sockaddr *) &local, namelen); + if (rc != 0) { + fprintf(stderr, "Failed to bind the unix domain socket. '%s'\n", + local.sun_path); + return -1; + } + + if (listen(bfd->fd, 0) != 0) { + fprintf(stderr, "Failed to listen.\n"); + return -1; + } + + return 0; +} diff --git a/openbsc/src/libmsc/osmo_msc.c b/openbsc/src/libmsc/osmo_msc.c new file mode 100644 index 000000000..8c86dcc8e --- /dev/null +++ b/openbsc/src/libmsc/osmo_msc.c @@ -0,0 +1,104 @@ +/* main MSC management code... */ + +/* + * (C) 2010 by Holger Hans Peter Freyther + * (C) 2010 by On-Waves + * + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + * + */ + +#include +#include +#include + +#include + +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); + if (conn->put_channel) { + conn->put_channel = 0; + subscr_put_channel(conn->subscr); + } + return 1; +} + +static int msc_compl_l3(struct gsm_subscriber_connection *conn, struct msgb *msg, + uint16_t chosen_channel) +{ + gsm0408_new_conn(conn); + gsm0408_dispatch(conn, msg); + + /* TODO: do better */ + return BSC_API_CONN_POL_ACCEPT; +} + +static void msc_dtap(struct gsm_subscriber_connection *conn, uint8_t link_id, struct msgb *msg) +{ + gsm0408_dispatch(conn, msg); +} + +static struct bsc_api msc_handler = { + .sapi_n_reject = msc_sapi_n_reject, + .clear_request = msc_clear_request, + .compl_l3 = msc_compl_l3, + .dtap = msc_dtap, +}; + +struct bsc_api *msc_bsc_api() { + return &msc_handler; +} + +/* lchan release handling */ +void msc_release_connection(struct gsm_subscriber_connection *conn) +{ + struct gsm_trans *trans; + + /* 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; + + llist_for_each_entry(trans, &conn->bts->network->trans_list, entry) { + if (trans->conn == conn) + return; + } + + /* no more connections, asking to release the channel */ + conn->in_release = 1; + gsm0808_clear(conn); + if (conn->put_channel) { + conn->put_channel = 0; + subscr_put_channel(conn->subscr); + } + subscr_con_free(conn); +} diff --git a/openbsc/src/libmsc/rrlp.c b/openbsc/src/libmsc/rrlp.c new file mode 100644 index 000000000..ae5ca478e --- /dev/null +++ b/openbsc/src/libmsc/rrlp.c @@ -0,0 +1,105 @@ +/* Radio Resource LCS (Location) Protocol, GMS TS 04.31 */ + +/* (C) 2009 by Harald Welte + * + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + * + */ + + +#include + +#include +#include +#include +#include + +/* RRLP msPositionReq, nsBased, + * Accuracy=60, Method=gps, ResponseTime=2, oneSet */ +static const u_int8_t ms_based_pos_req[] = { 0x40, 0x01, 0x78, 0xa8 }; + +/* RRLP msPositionReq, msBasedPref, + Accuracy=60, Method=gpsOrEOTD, ResponseTime=5, multipleSets */ +static const u_int8_t ms_pref_pos_req[] = { 0x40, 0x02, 0x79, 0x50 }; + +/* RRLP msPositionReq, msAssistedPref, + Accuracy=60, Method=gpsOrEOTD, ResponseTime=5, multipleSets */ +static const u_int8_t ass_pref_pos_req[] = { 0x40, 0x03, 0x79, 0x50 }; + +static int send_rrlp_req(struct gsm_subscriber_connection *conn) +{ + struct gsm_network *net = conn->bts->network; + const u_int8_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) +{ + register_signal_handler(SS_SUBSCR, subscr_sig_cb, NULL); + register_signal_handler(SS_PAGING, paging_sig_cb, NULL); +} diff --git a/openbsc/src/libmsc/silent_call.c b/openbsc/src/libmsc/silent_call.c new file mode 100644 index 000000000..64ebdfdb9 --- /dev/null +++ b/openbsc/src/libmsc/silent_call.c @@ -0,0 +1,143 @@ +/* GSM silent call feature */ + +/* + * (C) 2009 by Harald Welte + * + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + * + */ + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* paging of the requested subscriber has completed */ +static int paging_cb_silent(unsigned int hooknum, unsigned int event, + struct msgb *msg, void *_conn, void *_data) +{ + struct gsm_subscriber_connection *conn = _conn; + struct scall_signal_data sigdata; + int rc = 0; + + if (hooknum != GSM_HOOK_RR_PAGING) + return -EINVAL; + + DEBUGP(DSMS, "paging_cb_silent: "); + + sigdata.conn = conn; + sigdata.data = _data; + + switch (event) { + case GSM_PAGING_SUCCEEDED: + DEBUGPC(DSMS, "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 */ + dispatch_signal(SS_SCALL, S_SCALL_SUCCESS, &sigdata); + break; + case GSM_PAGING_EXPIRED: + case GSM_PAGING_BUSY: + case GSM_PAGING_OOM: + DEBUGP(DSMS, "expired\n"); + dispatch_signal(SS_SCALL, S_SCALL_EXPIRED, &sigdata); + break; + default: + rc = -EINVAL; + break; + } + + return rc; +} + +/* receive a layer 3 message from a silent call */ +int silent_call_rx(struct gsm_subscriber_connection *conn, struct msgb *msg) +{ + /* FIXME: do something like sending it through a UDP port */ + return 0; +} + +struct msg_match { + u_int8_t pdisc; + u_int8_t msg_type; +}; + +/* list of messages that are handled inside OpenBSC, even in a silent call */ +static const struct msg_match silent_call_accept[] = { + { GSM48_PDISC_MM, GSM48_MT_MM_LOC_UPD_REQUEST }, + { GSM48_PDISC_MM, GSM48_MT_MM_CM_SERV_REQ }, +}; + +/* decide if we need to reroute a message as part of a silent call */ +int silent_call_reroute(struct gsm_subscriber_connection *conn, struct msgb *msg) +{ + struct gsm48_hdr *gh = msgb_l3(msg); + u_int8_t pdisc = gh->proto_discr & 0x0f; + int i; + + /* if we're not part of a silent call, never reroute */ + if (!conn->silent_call) + return 0; + + /* check if we are a special message that is handled in openbsc */ + for (i = 0; i < ARRAY_SIZE(silent_call_accept); i++) { + if (silent_call_accept[i].pdisc == pdisc && + silent_call_accept[i].msg_type == gh->msg_type) + return 0; + } + + /* otherwise, reroute */ + return 1; +} + + +/* initiate a silent call with a given subscriber */ +int gsm_silent_call_start(struct gsm_subscriber *subscr, void *data, int type) +{ + int rc; + + rc = paging_request(subscr->net, subscr, type, + paging_cb_silent, data); + return rc; +} + +/* 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; + + conn->silent_call = 0; + msc_release_connection(conn); + + return 0; +} diff --git a/openbsc/src/libmsc/sms_queue.c b/openbsc/src/libmsc/sms_queue.c new file mode 100644 index 000000000..079755d22 --- /dev/null +++ b/openbsc/src/libmsc/sms_queue.c @@ -0,0 +1,479 @@ +/* SMS queue to continously attempt to deliver SMS */ +/* + * (C) 2010 by Holger Hans Peter Freyther + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + * + */ + +/** + * The difficulty of such a queue is to send a lot of SMS without + * overloading the paging subsystem and the database and other users + * of the MSC. To make the best use we would need to know the number + * of pending paging requests, then throttle the number of SMS we + * want to send and such. + * We will start with a very simple SMS Queue and then try to speed + * things up by collecting data from other parts of the system. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include + +/* + * One pending SMS that we wait for. + */ +struct gsm_sms_pending { + struct llist_head entry; + + struct gsm_subscriber *subscr; + unsigned long long sms_id; + int failed_attempts; + int resend; +}; + +struct gsm_sms_queue { + struct timer_list resend_pending; + struct 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 int sms_subscriber_is_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 1; + } + + return 0; +} + +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(DSMS, LOGL_DEBUG, + "Scheduling resend of SMS %llu.\n", pending->sms_id); + + pending->resend = 1; + + smsq = pending->subscr->net->sms_queue; + if (bsc_timer_pending(&smsq->resend_pending)) + return; + + bsc_schedule_timer(&smsq->resend_pending, 1, 0); +} + +static void sms_pending_failed(struct gsm_sms_pending *pending, int paging_error) +{ + struct gsm_sms_queue *smsq; + + LOGP(DSMS, LOGL_NOTICE, "Sending SMS %llu failed %d times.\n", + pending->sms_id, pending->failed_attempts); + + smsq = pending->subscr->net->sms_queue; + if (++pending->failed_attempts < smsq->max_fail) + return sms_pending_resend(pending); + + if (paging_error) { + LOGP(DSMS, LOGL_NOTICE, + "Subscriber %llu is not reachable. Setting LAC=0.\n", pending->subscr->id); + pending->subscr->lac = GSM_LAC_RESERVED_DETACHED; + db_sync_subscriber(pending->subscr); + + /* Workaround a failing sync */ + db_subscriber_update(pending->subscr); + } + + 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(DSMS, LOGL_NOTICE, "Attempting to send %d SMS\n", attempts); + + do { + struct gsm_sms_pending *pending; + struct gsm_sms *sms; + + + sms = take_next_sms(smsq); + if (!sms) + break; + + rounds += 1; + + /* + * This code needs to detect a loop. It assumes that no SMS + * will vanish during the time this is executed. We will remember + * the id of the first GSM subscriber we see and then will + * compare this. The Database code should make sure that we will + * see all other subscribers first before seeing this one again. + * + * It is always scary to have an infinite loop like this. + */ + if (!initialized) { + first_sub = sms->receiver->id; + initialized = 1; + } else if (first_sub == sms->receiver->id) { + sms_free(sms); + break; + } + + /* no need to send a pending sms */ + if (sms_is_in_pending(smsq, sms)) { + LOGP(DSMS, 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(DSMS, 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(DSMS, 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(DSMS, LOGL_DEBUG, "SMSqueue added %d messages in %d rounds\n", attempted, rounds); +} + +/* + * Kick off the queue again. + */ +int sms_queue_trigger(struct gsm_sms_queue *smsq) +{ + if (bsc_timer_pending(&smsq->push_queue)) + return 0; + + bsc_schedule_timer(&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; + } + + register_signal_handler(SS_SUBSCR, sms_subscr_cb, network); + register_signal_handler(SS_SMS, sms_sms_cb, network); + + network->sms_queue = sms; + INIT_LLIST_HEAD(&sms->pending_sms); + sms->max_fail = 1; + sms->network = network; + sms->max_pending = max_pending; + sms->push_queue.data = sms; + sms->push_queue.cb = sms_submit_pending; + sms->resend_pending.data = sms; + sms->resend_pending.cb = sms_resend_pending; + + sms_submit_pending(sms); + + return 0; +} + +static int sub_ready_for_sm(struct gsm_subscriber *subscr) +{ + struct gsm_subscriber_connection *conn; + struct gsm_sms *sms; + + /* A subscriber has attached. Check if there are + * any pending SMS for him to be delivered */ + conn = connection_for_subscr(subscr); + if (!conn) + return -1; + 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(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; + + /* We got a new SMS and maybe should launch the queue again. */ + if (signal == S_SMS_SUBMITTED || signal == S_SMS_SMMA) { + 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: + /* + * Create place for a new SMS but keep the pending data + * so we will not attempt to send the SMS for this subscriber + * as we still have an open channel and will attempt to submit + * SMS to it anyway. + */ + network->sms_queue->pending -= 1; + sms_submit_pending(network->sms_queue); + sms_pending_free(pending); + 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(DSMS, LOGL_ERROR, "Unhandled result: %d\n", + sig_sms->paging_result); + } + break; + default: + LOGP(DSMS, 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(DSMS, 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(DSMS, 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(DSMS, 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 new file mode 100644 index 000000000..3404dd4ee --- /dev/null +++ b/openbsc/src/libmsc/token_auth.c @@ -0,0 +1,153 @@ +/* SMS based token authentication for ad-hoc GSM networks */ + +/* (C) 2009 by Harald Welte + * + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define TOKEN_SMS_TEXT "HAR 2009 GSM. Register at http://har2009.gnumonks.org/ " \ + "Your IMSI is %s, auth token is %08X, phone no is %s." + +static char *build_sms_string(struct gsm_subscriber *subscr, u_int32_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->net->auth_policy != GSM_AUTH_POLICY_TOKEN) + return 0; + + if (subscr->flags & GSM_SUBSCRIBER_FIRST_CONTACT) { + u_int32_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; + } + + sms = sms_from_text(subscr, 0, sms_str); + 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) { + u_int8_t auth_rand[16]; + /* kick the subscriber off the network */ + gsm48_tx_mm_auth_req(conn, auth_rand, 0); + gsm48_tx_mm_auth_rej(conn); + /* FIXME: close the channel early ?*/ + //gsm48_send_rr_Release(lchan); + } + } + } + + return rc; +} + +static int token_sms_cb(unsigned int subsys, unsigned int signal, + void *handler_data, void *signal_data) +{ + struct sms_signal_data *sig = signal_data; + struct gsm_sms *sms = sig->sms;; + struct gsm_subscriber_connection *conn; + u_int8_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->net->auth_policy != GSM_AUTH_POLICY_TOKEN) + return 0; + + + conn = connection_for_subscr(sms->receiver); + if (conn) { + /* kick the subscriber off the network */ + gsm48_tx_mm_auth_req(conn, auth_rand, 0); + gsm48_tx_mm_auth_rej(conn); + /* FIXME: close the channel early ?*/ + //gsm48_send_rr_Release(lchan); + } + + return 0; +} + +//static __attribute__((constructor)) void on_dso_load_token(void) +void on_dso_load_token(void) +{ + register_signal_handler(SS_SUBSCR, token_subscr_cb, NULL); + register_signal_handler(SS_SMS, token_sms_cb, NULL); +} diff --git a/openbsc/src/libmsc/ussd.c b/openbsc/src/libmsc/ussd.c new file mode 100644 index 000000000..72f26bd36 --- /dev/null +++ b/openbsc/src/libmsc/ussd.c @@ -0,0 +1,79 @@ +/* Network-specific handling of mobile-originated USSDs. */ + +/* (C) 2008-2009 by Harald Welte + * (C) 2008, 2009 by Holger Hans Peter Freyther + * (C) 2009 by Mike Haben + * + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + * + */ + +/* This module defines the network-specific handling of mobile-originated + USSD messages. */ + +#include +#include +#include +#include + +#include +#include +#include +#include + +/* Declarations of USSD strings to be recognised */ +const char USSD_TEXT_OWN_NUMBER[] = "*#100#"; + +/* Forward declarations of network-specific handler functions */ +static int send_own_number(struct gsm_subscriber_connection *conn, const struct msgb *msg, const struct ussd_request *req); + + +/* Entrypoint - handler function common to all mobile-originated USSDs */ +int handle_rcv_ussd(struct gsm_subscriber_connection *conn, struct msgb *msg) +{ + int rc; + struct ussd_request req; + struct gsm48_hdr *gh; + + memset(&req, 0, sizeof(req)); + gh = msgb_l3(msg); + rc = gsm0480_decode_ussd_request(gh, msgb_l3len(msg), &req); + if (req.text[0] == 0xFF) /* Release-Complete */ + return 0; + + if (strstr(USSD_TEXT_OWN_NUMBER, req.text) != NULL) { + DEBUGP(DMM, "USSD: Own number requested\n"); + rc = send_own_number(conn, msg, &req); + } else { + DEBUGP(DMM, "Unhandled USSD %s\n", req.text); + rc = gsm0480_send_ussd_reject(conn, msg, &req); + } + + /* check if we can release it */ + msc_release_connection(conn); + return rc; +} + +/* A network-specific handler function */ +static int send_own_number(struct gsm_subscriber_connection *conn, const struct msgb *msg, const struct ussd_request *req) +{ + char *own_number = conn->subscr->extension; + char response_string[GSM_EXTENSION_LENGTH + 20]; + + /* Need trailing CR as EOT character */ + snprintf(response_string, sizeof(response_string), "Your extension is %s\r", own_number); + return gsm0480_send_ussd_response(conn, msg, response_string, req); +} diff --git a/openbsc/src/libmsc/vty_interface_layer3.c b/openbsc/src/libmsc/vty_interface_layer3.c new file mode 100644 index 000000000..a38d15bbb --- /dev/null +++ b/openbsc/src/libmsc/vty_interface_layer3.c @@ -0,0 +1,790 @@ +/* OpenBSC interface to quagga VTY */ +/* (C) 2009 by Harald Welte + * (C) 2009 by Holger Hans Peter Freyther + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + * + */ + +#include +#include +#include +#include + +#include +#include +#include + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +extern struct gsm_network *gsmnet_from_vty(struct vty *v); + +static void subscr_dump_full_vty(struct vty *vty, struct gsm_subscriber *subscr, int pending) +{ + int rc; + struct gsm_auth_info ainfo; + struct gsm_auth_tuple atuple; + + vty_out(vty, " ID: %llu, Authorized: %d%s", subscr->id, + subscr->authorized, VTY_NEWLINE); + if (subscr->name) + vty_out(vty, " Name: '%s'%s", subscr->name, VTY_NEWLINE); + if (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); + if (subscr->imsi) + 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", + 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", + hexdump(atuple.rand, sizeof(atuple.rand)), + VTY_NEWLINE); + vty_out(vty, " SRES : %s%s", + hexdump(atuple.sres, sizeof(atuple.sres)), + VTY_NEWLINE); + vty_out(vty, " Kc : %s%s", + hexdump(atuple.kc, sizeof(atuple.kc)), + VTY_NEWLINE); + } + if (pending) + vty_out(vty, " Pending: %d%s", + subscr_pending_requests(subscr), 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 "Display contents of subscriber cache\n") +{ + struct gsm_subscriber *subscr; + + llist_for_each_entry(subscr, &active_subscribers, entry) { + vty_out(vty, " Subscriber:%s", VTY_NEWLINE); + subscr_dump_full_vty(vty, subscr, 0); + } + + return CMD_SUCCESS; +} + +DEFUN(sms_send_pend, + sms_send_pend_cmd, + "sms send pending", + "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, char *str, + u_int8_t tp_pid) +{ + struct gsm_sms *sms; + + sms = sms_from_text(receiver, 0, str); + sms->protocol_id = tp_pid; + + /* store in database for the queue */ + if (db_sms_store(sms) != 0) { + LOGP(DSMS, LOGL_ERROR, "Failed to store SMS in Database\n"); + sms_free(sms); + return CMD_WARNING; + } + + sms_free(sms); + sms_queue_trigger(receiver->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, id); + else if (!strcmp(type, "imsi")) + return subscr_get_by_imsi(gsmnet, id); + else if (!strcmp(type, "tmsi")) + return subscr_get_by_tmsi(gsmnet, atoi(id)); + else if (!strcmp(type, "id")) + return subscr_get_by_id(gsmnet, atoi(id)); + + return NULL; +} +#define SUBSCR_TYPES "(extension|imsi|tmsi|id)" +#define SUBSCR_HELP "Operations on a Subscriber\n" \ + "Identify subscriber by his extension (phone number)\n" \ + "Identify subscriber by his IMSI\n" \ + "Identify subscriber by his TMSI\n" \ + "Identify subscriber by his database ID\n" \ + "Identifier for the subscriber\n" + +DEFUN(show_subscr, + show_subscr_cmd, + "show subscriber " SUBSCR_TYPES " ID", + SHOW_STR SUBSCR_HELP) +{ + struct gsm_network *gsmnet = gsmnet_from_vty(vty); + struct gsm_subscriber *subscr = + get_subscr_by_argv(gsmnet, argv[0], argv[1]); + + if (!subscr) { + vty_out(vty, "%% No subscriber found for %s %s%s", + argv[0], argv[1], VTY_NEWLINE); + return CMD_WARNING; + } + + subscr_dump_full_vty(vty, subscr, 1); + + 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 = get_subscr_by_argv(gsmnet, argv[0], argv[1]); + struct gsm_sms *sms; + + 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 send .LINE", + SUBSCR_HELP "SMS Operations\n" "Send SMS\n" "Actual SMS Text") +{ + struct gsm_network *gsmnet = gsmnet_from_vty(vty); + struct gsm_subscriber *subscr = get_subscr_by_argv(gsmnet, argv[0], argv[1]); + char *str; + int rc; + + if (!subscr) { + vty_out(vty, "%% No subscriber found for %s %s%s", + argv[0], argv[1], VTY_NEWLINE); + return CMD_WARNING; + } + str = argv_concat(argv, argc, 2); + rc = _send_sms_str(subscr, str, 0); + talloc_free(str); + + subscr_put(subscr); + + return rc; +} + +DEFUN(subscriber_silent_sms, + subscriber_silent_sms_cmd, + "subscriber " SUBSCR_TYPES " ID silent-sms send .LINE", + SUBSCR_HELP + "Silent SMS Operation\n" "Send Silent 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]); + char *str; + int rc; + + if (!subscr) { + vty_out(vty, "%% No subscriber found for %s %s%s", + argv[0], argv[1], VTY_NEWLINE); + return CMD_WARNING; + } + + str = argv_concat(argv, argc, 2); + rc = _send_sms_str(subscr, str, 64); + talloc_free(str); + + 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 "USSD Notify\n" + "Subscriber ID\n" + "Alerting Level\n" + "Text Message to send\n") +{ + char *text; + struct gsm_subscriber_connection *conn; + struct gsm_network *gsmnet = gsmnet_from_vty(vty); + struct gsm_subscriber *subscr = get_subscr_by_argv(gsmnet, argv[0], argv[1]); + int level; + + if (!subscr) { + vty_out(vty, "%% No subscriber found for %s %s%s", + argv[0], argv[1], VTY_NEWLINE); + return CMD_WARNING; + } + + level = atoi(argv[2]); + text = argv_concat(argv, argc, 3); + if (!text) { + subscr_put(subscr); + return CMD_WARNING; + } + + conn = connection_for_subscr(subscr); + if (!conn) { + vty_out(vty, "%% An active connection is required for %s %s%s", + argv[0], argv[1], VTY_NEWLINE); + subscr_put(subscr); + talloc_free(text); + return CMD_WARNING; + } + + gsm0480_send_ussdNotify(conn, level, text); + gsm0480_send_releaseComplete(conn); + + subscr_put(subscr); + talloc_free(text); + return CMD_SUCCESS; +} + +DEFUN(ena_subscr_authorizde, + 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; + } + + strncpy(subscr->name, name, sizeof(subscr->name)); + talloc_free(name); + db_sync_subscriber(subscr); + + subscr_put(subscr); + + return CMD_SUCCESS; +} + +DEFUN(ena_subscr_extension, + ena_subscr_extension_cmd, + "subscriber " SUBSCR_TYPES " ID extension EXTENSION", + SUBSCR_HELP "Set the extension (phone number) of the subscriber\n" + "Extension (phone number)\n") +{ + struct gsm_network *gsmnet = gsmnet_from_vty(vty); + struct gsm_subscriber *subscr = + get_subscr_by_argv(gsmnet, argv[0], argv[1]); + const char *name = argv[2]; + + if (!subscr) { + vty_out(vty, "%% No subscriber found for %s %s%s", + argv[0], argv[1], VTY_NEWLINE); + return CMD_WARNING; + } + + strncpy(subscr->extension, name, sizeof(subscr->name)); + db_sync_subscriber(subscr); + + subscr_put(subscr); + + return CMD_SUCCESS; +} + +DEFUN(ena_subscr_clear, + ena_subscr_clear_cmd, + "subscriber " SUBSCR_TYPES " ID clear-requests", + SUBSCR_HELP "Clear the paging requests for this subscriber\n") +{ + int del; + 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; + } + + del = subscr_pending_clear(subscr); + vty_out(vty, "Cleared %d pending requests.%s", del, VTY_NEWLINE); + subscr_put(subscr); + + return CMD_SUCCESS; +} + +DEFUN(ena_subscr_pend, + ena_subscr_pend_cmd, + "subscriber " SUBSCR_TYPES " ID show-pending", + SUBSCR_HELP "Clear the paging requests for this subscriber\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_pending_dump(subscr, vty); + subscr_put(subscr); + + return CMD_SUCCESS; +} + +DEFUN(ena_subscr_kick, + ena_subscr_kick_cmd, + "subscriber " SUBSCR_TYPES " ID kick-pending", + SUBSCR_HELP "Clear the paging requests for this subscriber\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_pending_kick(subscr); + 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); + return CMD_WARNING; + } + + if (ki_str) { + rc = hexparse(ki_str, ainfo.a3a8_ki, sizeof(ainfo.a3a8_ki)); + if ((rc > maxlen) || (rc < minlen)) { + subscr_put(subscr); + return CMD_WARNING; + } + ainfo.a3a8_ki_len = rc; + } else { + ainfo.a3a8_ki_len = 0; + if (minlen) { + subscr_put(subscr); + 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); + + return rc ? CMD_WARNING : 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); + 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, "Channel Requests : %lu total, %lu no channel%s", + counter_get(net->stats.chreq.total), + counter_get(net->stats.chreq.no_channel), VTY_NEWLINE); + vty_out(vty, "Location Update : %lu attach, %lu normal, %lu periodic%s", + counter_get(net->stats.loc_upd_type.attach), + counter_get(net->stats.loc_upd_type.normal), + counter_get(net->stats.loc_upd_type.periodic), VTY_NEWLINE); + vty_out(vty, "IMSI Detach Indications : %lu%s", + counter_get(net->stats.loc_upd_type.detach), VTY_NEWLINE); + vty_out(vty, "Location Update Response: %lu accept, %lu reject%s", + counter_get(net->stats.loc_upd_resp.accept), + counter_get(net->stats.loc_upd_resp.reject), VTY_NEWLINE); + vty_out(vty, "Handover : %lu attempted, %lu no_channel, %lu timeout, " + "%lu completed, %lu failed%s", + counter_get(net->stats.handover.attempted), + counter_get(net->stats.handover.no_channel), + counter_get(net->stats.handover.timeout), + counter_get(net->stats.handover.completed), + counter_get(net->stats.handover.failed), VTY_NEWLINE); + vty_out(vty, "SMS MO : %lu submitted, %lu no receiver%s", + counter_get(net->stats.sms.submitted), + counter_get(net->stats.sms.no_receiver), VTY_NEWLINE); + vty_out(vty, "SMS MT : %lu delivered, %lu no memory, %lu other error%s", + counter_get(net->stats.sms.delivered), + counter_get(net->stats.sms.rp_err_mem), + counter_get(net->stats.sms.rp_err_other), VTY_NEWLINE); + vty_out(vty, "MO Calls : %lu setup, %lu connect ack%s", + counter_get(net->stats.call.mo_setup), + counter_get(net->stats.call.mo_connect_ack), VTY_NEWLINE); + vty_out(vty, "MT Calls : %lu setup, %lu connect%s", + counter_get(net->stats.call.mt_setup), + counter_get(net->stats.call.mt_connect), VTY_NEWLINE); + return CMD_SUCCESS; +} + +DEFUN(show_smsqueue, + show_smsqueue_cmd, + "show sms-queue", + SHOW_STR "Display SMSqueue statistics\n") +{ + struct gsm_network *net = gsmnet_from_vty(vty); + + sms_queue_stats(net->sms_queue, vty); + return CMD_SUCCESS; +} + +DEFUN(smsqueue_trigger, + smsqueue_trigger_cmd, + "sms-queue trigger", + "SMS Queue\n" "Trigger sending messages\n") +{ + struct gsm_network *net = gsmnet_from_vty(vty); + + sms_queue_trigger(net->sms_queue); + return CMD_SUCCESS; +} + +DEFUN(smsqueue_max, + smsqueue_max_cmd, + "sms-queue max-pending <1-500>", + "SMS Queue\n" "SMS to attempt to deliver at the same time\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" "Set maximum amount of failures\n") +{ + struct gsm_network *net = gsmnet_from_vty(vty); + + sms_queue_set_max_failure(net->sms_queue, atoi(argv[0])); + return CMD_SUCCESS; +} + +int bsc_vty_init_extra(void) +{ + register_signal_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_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(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_clear_cmd); + install_element(ENABLE_NODE, &ena_subscr_pend_cmd); + install_element(ENABLE_NODE, &ena_subscr_kick_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); + + return 0; +} diff --git a/openbsc/src/libtrau/Makefile.am b/openbsc/src/libtrau/Makefile.am new file mode 100644 index 000000000..01ed251d8 --- /dev/null +++ b/openbsc/src/libtrau/Makefile.am @@ -0,0 +1,7 @@ +INCLUDES = $(all_includes) -I$(top_srcdir)/include -I$(top_builddir) +AM_CFLAGS=-Wall $(LIBOSMOCORE_CFLAGS) $(LIBOSMOVTY_CFLAGS) $(COVERAGE_CFLAGS) +AM_LDFLAGS = $(LIBOSMOCORE_LIBS) $(COVERAGE_LDFLAGS) + +noinst_LIBRARIES = libtrau.a + +libtrau_a_SOURCES = rtp_proxy.c subchan_demux.c trau_frame.c trau_mux.c trau_upqueue.c diff --git a/openbsc/src/libtrau/rtp_proxy.c b/openbsc/src/libtrau/rtp_proxy.c new file mode 100644 index 000000000..eefc0e1d6 --- /dev/null +++ b/openbsc/src/libtrau/rtp_proxy.c @@ -0,0 +1,728 @@ +/* RTP proxy handling for ip.access nanoBTS */ + +/* (C) 2009 by Harald Welte + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + * + */ + +#include +#include +#include +#include +#include +#include /* gettimeofday() */ +#include /* get..() */ +#include /* clock() */ +#include /* uname() */ + +#include +#include +#include +#include +#include +#include + +/* attempt to determine byte order */ +#include +#include +#include + +#ifndef __BYTE_ORDER +#error "__BYTE_ORDER should be defined by someone" +#endif + +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 + +/* according to RFC 1889 */ +struct rtcp_hdr { + u_int8_t byte0; + u_int8_t type; + u_int16_t length; +} __attribute__((packed)); + +#define RTCP_TYPE_SDES 202 + +#define RTCP_IE_CNAME 1 + +/* according to RFC 3550 */ +struct rtp_hdr { +#if __BYTE_ORDER == __LITTLE_ENDIAN + u_int8_t csrc_count:4, + extension:1, + padding:1, + version:2; + u_int8_t payload_type:7, + marker:1; +#elif __BYTE_ORDER == __BIG_ENDIAN + u_int8_t version:2, + padding:1, + extension:1, + csrc_count:4; + u_int8_t marker:1, + payload_type:7; +#endif + u_int16_t sequence; + u_int32_t timestamp; + u_int32_t ssrc; +} __attribute__((packed)); + +struct rtp_x_hdr { + u_int16_t by_profile; + u_int16_t length; +} __attribute__((packed)); + +#define RTP_VERSION 2 + +/* decode an rtp frame and create a new buffer with payload */ +static int rtp_decode(struct msgb *msg, u_int32_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; + u_int8_t *payload; + int payload_len; + int msg_type; + int x_len; + + if (msg->len < 12) { + DEBUGPC(DMUX, "received RTP frame too short (len = %d)\n", + msg->len); + return -EINVAL; + } + if (rtph->version != RTP_VERSION) { + DEBUGPC(DMUX, "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(DMUX, "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(DMUX, "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(DMUX, "received RTP frame too short, " + "extension header exceeds frame length\n"); + return -EINVAL; + } + } + if (rtph->padding) { + if (payload_len < 0) { + DEBUGPC(DMUX, "received RTP frame too short for " + "padding length\n"); + return -EINVAL; + } + payload_len -= payload[payload_len - 1]; + if (payload_len < 0) { + DEBUGPC(DMUX, "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 != 33) { + DEBUGPC(DMUX, "received RTP full rate frame with " + "payload length != 32 (len = %d)\n", + payload_len); + return -EINVAL; + } + break; + case RTP_PT_GSM_EFR: + msg_type = GSM_TCHF_FRAME_EFR; + break; + default: + DEBUGPC(DMUX, "received RTP frame with unknown payload " + "type %d\n", rtph->payload_type); + return -EINVAL; + } + + new_msg = msgb_alloc(sizeof(struct gsm_data_frame) + payload_len, + "GSM-DATA"); + if (!new_msg) + return -ENOMEM; + frame = (struct gsm_data_frame *)(new_msg->data); + frame->msg_type = msg_type; + frame->callref = callref; + memcpy(frame->data, payload, payload_len); + msgb_put(new_msg, sizeof(struct gsm_data_frame) + payload_len); + + *data = new_msg; + return 0; +} + +/* "to - from" */ +static void tv_difference(struct timeval *diff, const struct timeval *from, + const struct timeval *__to) +{ + struct timeval _to = *__to, *to = &_to; + + if (to->tv_usec < from->tv_usec) { + to->tv_sec -= 1; + to->tv_usec += 1000000; + } + + diff->tv_usec = to->tv_usec - from->tv_usec; + diff->tv_sec = to->tv_sec - from->tv_sec; +} + +/* encode and send a rtp frame */ +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; + int payload_type; + int payload_len; + int duration; /* in samples */ + + 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 = 33; + duration = 160; + break; + case GSM_TCHF_FRAME_EFR: + payload_type = RTP_PT_GSM_EFR; + payload_len = 31; + duration = 160; + break; + default: + DEBUGPC(DMUX, "unsupported message type %d\n", + frame->msg_type); + return -EINVAL; + } + + { + struct timeval tv, tv_diff; + long int usec_diff, frame_diff; + + gettimeofday(&tv, NULL); + tv_difference(&tv_diff, &rs->transmit.last_tv, &tv); + rs->transmit.last_tv = tv; + + usec_diff = tv_diff.tv_sec * 1000000 + tv_diff.tv_usec; + frame_diff = (usec_diff / 20000); + + if (abs(frame_diff) > 1) { + long int frame_diff_excess = frame_diff - 1; + + LOGP(DMUX, LOGL_NOTICE, + "Correcting frame difference of %ld frames\n", frame_diff_excess); + rs->transmit.sequence += frame_diff_excess; + rs->transmit.timestamp += frame_diff_excess * duration; + } + } + + msg = msgb_alloc(sizeof(struct rtp_hdr) + payload_len, "RTP-GSM-FULL"); + if (!msg) + return -ENOMEM; + rtph = (struct rtp_hdr *)msg->data; + 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); + memcpy(msg->data + sizeof(struct rtp_hdr), frame->data, payload_len); + msgb_put(msg, sizeof(struct rtp_hdr) + 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, + u_int16_t *rtcp_len, const char *new_cname) +{ + u_int8_t *rtcp_end; + u_int8_t *cur = (u_int8_t *) rh; + u_int8_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) { } + 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; + u_int16_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(DMUX, "received RTCP packet too short for " + "length element\n"); + return -EINVAL; + } + if (rtph->type == RTCP_TYPE_SDES) { + char new_cname[255]; + strncpy(new_cname, inet_ntoa(rss->sin_local.sin_addr), + sizeof(new_cname)); + new_cname[sizeof(new_cname)-1] = '\0'; + rc = rtcp_sdes_cname_mangle(msg, rtph, &old_len, + new_cname); + if (rc < 0) + return rc; + } + rtph = (void *)rtph + old_len; + } + + return 0; +} + +/* read from incoming RTP/RTCP socket */ +static int rtp_socket_read(struct rtp_socket *rs, struct rtp_sub_socket *rss) +{ + int rc; + struct msgb *msg = msgb_alloc(RTP_ALLOC_SIZE, "RTP/RTCP"); + struct msgb *new_msg; + struct rtp_sub_socket *other_rss; + + if (!msg) + return -ENOMEM; + + rc = read(rss->bfd.fd, msg->data, RTP_ALLOC_SIZE); + if (rc <= 0) { + rss->bfd.when &= ~BSC_FD_READ; + return rc; + } + + 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; +} + +/* 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(DMIB, LOGL_ERROR, "short write"); + msgb_free(msg); + return -EIO; + } + + msgb_free(msg); + + return 0; +} + + +/* callback for the select.c:bfd_* layer */ +static int rtp_bfd_cb(struct bsc_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; +} + +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; +} + +struct rtp_socket *rtp_socket_create(void) +{ + int rc; + struct rtp_socket *rs; + + DEBUGP(DMUX, "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 = bsc_register_fd(&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 = bsc_register_fd(&rs->rtcp.bfd); + if (rc < 0) + goto out_rtcp_socket; + + DEBUGPC(DMUX, "success\n"); + + rc = rtp_socket_bind(rs, INADDR_ANY); + if (rc < 0) + goto out_rtcp_bfd; + + return rs; + +out_rtcp_bfd: + bsc_unregister_fd(&rs->rtcp.bfd); +out_rtcp_socket: + close(rs->rtcp.bfd.fd); +out_rtp_bfd: + bsc_unregister_fd(&rs->rtp.bfd); +out_rtp_socket: + close(rs->rtp.bfd.fd); +out_free: + talloc_free(rs); + DEBUGPC(DMUX, "failed\n"); + return NULL; +} + +static int rtp_sub_socket_bind(struct rtp_sub_socket *rss, u_int32_t ip, + u_int16_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; + +/* bind a RTP socket to a local address */ +int rtp_socket_bind(struct rtp_socket *rs, u_int32_t ip) +{ + int rc = -EIO; + struct in_addr ia; + + ia.s_addr = htonl(ip); + DEBUGP(DMUX, "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(DMUX, "failed\n"); + return rc; + } + + ia.s_addr = rs->rtp.sin_local.sin_addr.s_addr; + DEBUGPC(DMUX, "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, + u_int32_t ip, u_int16_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); +} + +/* 'connect' a RTP socket to a remote peer */ +int rtp_socket_connect(struct rtp_socket *rs, u_int32_t ip, u_int16_t port) +{ + int rc; + struct in_addr ia; + + ia.s_addr = htonl(ip); + DEBUGP(DMUX, "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); +} + +/* bind two RTP/RTCP sockets together */ +int rtp_socket_proxy(struct rtp_socket *this, struct rtp_socket *other) +{ + DEBUGP(DMUX, "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; +} + +/* bind RTP/RTCP socket to application */ +int rtp_socket_upstream(struct rtp_socket *this, struct gsm_network *net, + u_int32_t callref) +{ + DEBUGP(DMUX, "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); +} + +int rtp_socket_free(struct rtp_socket *rs) +{ + DEBUGP(DMUX, "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; + + bsc_unregister_fd(&rs->rtp.bfd); + close(rs->rtp.bfd.fd); + free_tx_queue(&rs->rtp); + + bsc_unregister_fd(&rs->rtcp.bfd); + close(rs->rtcp.bfd.fd); + free_tx_queue(&rs->rtcp); + + talloc_free(rs); + + return 0; +} diff --git a/openbsc/src/libtrau/subchan_demux.c b/openbsc/src/libtrau/subchan_demux.c new file mode 100644 index 000000000..6bcf279fe --- /dev/null +++ b/openbsc/src/libtrau/subchan_demux.c @@ -0,0 +1,321 @@ +/* A E1 sub-channel (de)multiplexer with TRAU frame sync */ + +/* (C) 2009 by Harald Welte + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + * + */ + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +void *tall_tqe_ctx; + +static inline void append_bit(struct demux_subch *sch, u_int8_t bit) +{ + sch->out_bitbuf[sch->out_idx++] = bit; +} + +#define SYNC_HDR_BITS 16 +static const u_int8_t nullbytes[SYNC_HDR_BITS]; + +/* check if we have just completed the 16 bit zero sync header, + * in accordance with GSM TS 08.60 Chapter 4.8.1 */ +static int sync_hdr_complete(struct demux_subch *sch, u_int8_t bit) +{ + if (bit == 0) + sch->consecutive_zeros++; + else + sch->consecutive_zeros = 0; + + if (sch->consecutive_zeros >= SYNC_HDR_BITS) { + sch->consecutive_zeros = 0; + return 1; + } + + return 0; +} + +/* resynchronize to current location */ +static void resync_to_here(struct demux_subch *sch) +{ + memset(sch->out_bitbuf, 0, SYNC_HDR_BITS); + + /* set index in a way that we can continue receiving bits after + * the end of the SYNC header */ + sch->out_idx = SYNC_HDR_BITS; + sch->in_sync = 1; +} + +int subch_demux_init(struct subch_demux *dmx) +{ + int i; + + dmx->chan_activ = 0; + for (i = 0; i < NR_SUBCH; i++) { + struct demux_subch *sch = &dmx->subch[i]; + sch->out_idx = 0; + memset(sch->out_bitbuf, 0xff, sizeof(sch->out_bitbuf)); + } + return 0; +} + +/* input some arbitrary (modulo 4) number of bytes of a 64k E1 channel, + * split it into the 16k subchannels */ +int subch_demux_in(struct subch_demux *dmx, u_int8_t *data, int len) +{ + int i, c; + + /* we avoid partially filled bytes in outbuf */ + if (len % 4) + return -EINVAL; + + for (i = 0; i < len; i++) { + u_int8_t inbyte = data[i]; + + for (c = 0; c < NR_SUBCH; c++) { + struct demux_subch *sch = &dmx->subch[c]; + u_int8_t inbits; + u_int8_t bit; + + /* ignore inactive subchannels */ + if (!(dmx->chan_activ & (1 << c))) + continue; + + inbits = inbyte >> (c << 1); + + /* two bits for each subchannel */ + if (inbits & 0x01) + bit = 1; + else + bit = 0; + append_bit(sch, bit); + + if (sync_hdr_complete(sch, bit)) + resync_to_here(sch); + + if (inbits & 0x02) + bit = 1; + else + bit = 0; + append_bit(sch, bit); + + if (sync_hdr_complete(sch, bit)) + resync_to_here(sch); + + /* FIXME: verify the first bit in octet 2, 4, 6, ... + * according to TS 08.60 4.8.1 */ + + /* once we have reached TRAU_FRAME_BITS, call + * the TRAU frame handler callback function */ + if (sch->out_idx >= TRAU_FRAME_BITS) { + if (sch->in_sync) { + dmx->out_cb(dmx, c, sch->out_bitbuf, + sch->out_idx, dmx->data); + sch->in_sync = 0; + } + sch->out_idx = 0; + } + } + } + return i; +} + +int subch_demux_activate(struct subch_demux *dmx, int subch) +{ + if (subch >= NR_SUBCH) + return -EINVAL; + + dmx->chan_activ |= (1 << subch); + return 0; +} + +int subch_demux_deactivate(struct subch_demux *dmx, int subch) +{ + if (subch >= NR_SUBCH) + return -EINVAL; + + dmx->chan_activ &= ~(1 << subch); + return 0; +} + +/* MULTIPLEXER */ + +static int alloc_add_idle_frame(struct subch_mux *mx, int sch_nr) +{ + /* allocate and initialize with idle pattern */ + return subchan_mux_enqueue(mx, sch_nr, trau_idle_frame(), + TRAU_FRAME_BITS); +} + +/* return the requested number of bits from the specified subchannel */ +static int get_subch_bits(struct subch_mux *mx, int subch, + u_int8_t *bits, int num_requested) +{ + struct mux_subch *sch = &mx->subch[subch]; + int num_bits = 0; + + while (num_bits < num_requested) { + struct subch_txq_entry *txe; + int num_bits_left; + int num_bits_thistime; + + /* make sure we have a valid entry at top of tx queue. + * if not, add an idle frame */ + if (llist_empty(&sch->tx_queue)) + alloc_add_idle_frame(mx, subch); + + if (llist_empty(&sch->tx_queue)) + return -EIO; + + txe = llist_entry(sch->tx_queue.next, struct subch_txq_entry, list); + num_bits_left = txe->bit_len - txe->next_bit; + + if (num_bits_left < num_requested) + num_bits_thistime = num_bits_left; + else + num_bits_thistime = num_requested; + + /* pull the bits from the txe */ + memcpy(bits + num_bits, txe->bits + txe->next_bit, num_bits_thistime); + txe->next_bit += num_bits_thistime; + + /* free the tx_queue entry if it is fully consumed */ + if (txe->next_bit >= txe->bit_len) { + llist_del(&txe->list); + talloc_free(txe); + } + + /* increment global number of bits dequeued */ + num_bits += num_bits_thistime; + } + + return num_requested; +} + +/* compact an array of 8 single-bit bytes into one byte of 8 bits */ +static u_int8_t compact_bits(const u_int8_t *bits) +{ + u_int8_t ret = 0; + int i; + + for (i = 0; i < 8; i++) + ret |= (bits[i] ? 1 : 0) << i; + + return ret; +} + +/* obtain a single output byte from the subchannel muxer */ +static int mux_output_byte(struct subch_mux *mx, u_int8_t *byte) +{ + u_int8_t bits[8]; + int rc; + + /* combine two bits of every subchan */ + rc = get_subch_bits(mx, 0, &bits[0], 2); + rc = get_subch_bits(mx, 1, &bits[2], 2); + rc = get_subch_bits(mx, 2, &bits[4], 2); + rc = get_subch_bits(mx, 3, &bits[6], 2); + + *byte = compact_bits(bits); + + return rc; +} + +/* Request the output of some muxed bytes from the subchan muxer */ +int subchan_mux_out(struct subch_mux *mx, u_int8_t *data, int len) +{ + int i; + + for (i = 0; i < len; i++) { + int rc; + rc = mux_output_byte(mx, &data[i]); + if (rc < 0) + break; + } + return i; +} + +static int llist_len(struct llist_head *head) +{ + struct llist_head *entry; + int i = 0; + + llist_for_each(entry, head) + i++; + + return i; +} + +/* evict the 'num_evict' number of oldest entries in the queue */ +static void tx_queue_evict(struct mux_subch *sch, int num_evict) +{ + struct subch_txq_entry *tqe; + int i; + + for (i = 0; i < num_evict; i++) { + if (llist_empty(&sch->tx_queue)) + return; + + tqe = llist_entry(sch->tx_queue.next, struct subch_txq_entry, list); + llist_del(&tqe->list); + talloc_free(tqe); + } +} + +/* enqueue some data into the tx_queue of a given subchannel */ +int subchan_mux_enqueue(struct subch_mux *mx, int s_nr, const u_int8_t *data, + int len) +{ + struct mux_subch *sch = &mx->subch[s_nr]; + int list_len = llist_len(&sch->tx_queue); + struct subch_txq_entry *tqe = talloc_zero_size(tall_tqe_ctx, + sizeof(*tqe) + len); + if (!tqe) + return -ENOMEM; + + tqe->bit_len = len; + memcpy(tqe->bits, data, len); + + if (list_len > 2) + tx_queue_evict(sch, list_len-2); + + llist_add_tail(&tqe->list, &sch->tx_queue); + + return 0; +} + +/* initialize one subchannel muxer instance */ +int subchan_mux_init(struct subch_mux *mx) +{ + int i; + + memset(mx, 0, sizeof(*mx)); + for (i = 0; i < NR_SUBCH; i++) { + struct mux_subch *sch = &mx->subch[i]; + INIT_LLIST_HEAD(&sch->tx_queue); + } + + return 0; +} diff --git a/openbsc/src/libtrau/trau_frame.c b/openbsc/src/libtrau/trau_frame.c new file mode 100644 index 000000000..d4d6447cc --- /dev/null +++ b/openbsc/src/libtrau/trau_frame.c @@ -0,0 +1,260 @@ +/* TRAU frame handling according to GSM TS 08.60 */ + +/* (C) 2009 by Harald Welte + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + * + */ + +#include +#include +#include +#include +#include + +#include +#include +#include + +static u_int32_t get_bits(const u_int8_t *bitbuf, int offset, int num) +{ + int i; + u_int32_t ret = 0; + + for (i = offset; i < offset + num; i++) { + ret = ret << 1; + if (bitbuf[i]) + ret |= 1; + } + return ret; +} + +/* Decode according to 3.1.1 */ +static void decode_fr(struct decoded_trau_frame *fr, const u_int8_t *trau_bits) +{ + int i; + int d_idx = 0; + + /* C1 .. C15 */ + memcpy(fr->c_bits+0, trau_bits+17, 15); + /* C16 .. C21 */ + memcpy(fr->c_bits+15, trau_bits+310, 6); + /* T1 .. T4 */ + memcpy(fr->t_bits+0, trau_bits+316, 4); + /* D1 .. D255 */ + for (i = 32; i < 304; i+= 16) { + memcpy(fr->d_bits + d_idx, trau_bits+i+1, 15); + d_idx += 15; + } + /* D256 .. D260 */ + memcpy(fr->d_bits + d_idx, trau_bits + 305, 5); +} + +/* Decode according to 3.1.2 */ +static void decode_amr(struct decoded_trau_frame *fr, const u_int8_t *trau_bits) +{ + int i; + int d_idx = 0; + + /* C1 .. C15 */ + memcpy(fr->c_bits+0, trau_bits+17, 15); + /* C16 .. C25 */ + memcpy(fr->c_bits+15, trau_bits+33, 10); + /* T1 .. T4 */ + memcpy(fr->t_bits+0, trau_bits+316, 4); + /* D1 .. D5 */ + memcpy(fr->d_bits, trau_bits+43, 5); + /* D6 .. D245 */ + for (i = 48; i < 304; i += 16) { + memcpy(fr->d_bits + d_idx, trau_bits+i+1, 15); + d_idx += 15; + } + /* D246 .. D256 */ + memcpy(fr->d_bits + d_idx, trau_bits + 305, 11); +} + +int decode_trau_frame(struct decoded_trau_frame *fr, const u_int8_t *trau_bits) +{ + u_int8_t cbits5 = get_bits(trau_bits, 17, 5); + + switch (cbits5) { + case TRAU_FT_FR_UP: + case TRAU_FT_FR_DOWN: + case TRAU_FT_IDLE_UP: + case TRAU_FT_IDLE_DOWN: + case TRAU_FT_EFR: + decode_fr(fr, trau_bits); + break; + case TRAU_FT_AMR: + decode_amr(fr, trau_bits); + break; + case TRAU_FT_OM_UP: + case TRAU_FT_OM_DOWN: + case TRAU_FT_DATA_UP: + case TRAU_FT_DATA_DOWN: + case TRAU_FT_D145_SYNC: + case TRAU_FT_EDATA: + LOGP(DMUX, LOGL_NOTICE, "can't decode unimplemented TRAU " + "Frame Type 0x%02x\n", cbits5); + return -1; + break; + default: + LOGP(DMUX, LOGL_NOTICE, "can't decode unknown TRAU " + "Frame Type 0x%02x\n", cbits5); + return -1; + break; + } + + return 0; +} + +const u_int8_t ft_fr_down_bits[] = { 1, 1, 1, 0, 0 }; +const u_int8_t ft_idle_down_bits[] = { 0, 1, 1, 1, 0 }; + +/* modify an uplink TRAU frame so we can send it downlink */ +int trau_frame_up2down(struct decoded_trau_frame *fr) +{ + u_int8_t cbits5 = get_bits(fr->c_bits, 0, 5); + + switch (cbits5) { + case TRAU_FT_FR_UP: + memcpy(fr->c_bits, ft_fr_down_bits, 5); + /* clear time alignment */ + memset(fr->c_bits+5, 0, 6); + /* FIXME: SP / BFI in case of DTx */ + /* C12 .. C21 are spare and coded as '1' */ + memset(fr->c_bits+11, 0x01, 10); + break; + case TRAU_FT_EFR: + /* clear time alignment */ + memset(fr->c_bits+5, 0, 6); + /* FIXME: set UFE appropriately */ + /* FIXME: SP / BFI in case of DTx */ + break; + case TRAU_FT_IDLE_UP: + memcpy(fr->c_bits, ft_idle_down_bits, 5); + /* clear time alignment */ + memset(fr->c_bits+5, 0, 6); + /* FIXME: SP / BFI in case of DTx */ + /* C12 .. C21 are spare and coded as '1' */ + memset(fr->c_bits+11, 0x01, 10); + break; + case TRAU_FT_FR_DOWN: + case TRAU_FT_IDLE_DOWN: + case TRAU_FT_OM_DOWN: + case TRAU_FT_DATA_DOWN: + /* we cannot convert a downlink to a downlink frame */ + return -EINVAL; + break; + case TRAU_FT_AMR: + case TRAU_FT_OM_UP: + case TRAU_FT_DATA_UP: + case TRAU_FT_D145_SYNC: + case TRAU_FT_EDATA: + LOGP(DMUX, LOGL_NOTICE, "unimplemented TRAU Frame Type " + "0x%02x\n", cbits5); + return -1; + break; + default: + LOGP(DMUX, LOGL_NOTICE, "unknown TRAU Frame Type " + "0x%02x\n", cbits5); + return -1; + break; + } + + return 0; + +} + +static void encode_fr(u_int8_t *trau_bits, const struct decoded_trau_frame *fr) +{ + int i; + int d_idx = 0; + + trau_bits[16] = 1; + /* C1 .. C15 */ + memcpy(trau_bits+17, fr->c_bits+0, 15); + /* D1 .. D255 */ + for (i = 32; i < 304; i+= 16) { + trau_bits[i] = 1; + memcpy(trau_bits+i+1, fr->d_bits + d_idx, 15); + d_idx += 15; + } + /* D256 .. D260 */ + trau_bits[304] = 1; + memcpy(trau_bits + 305, fr->d_bits + d_idx, 5); + /* C16 .. C21 */ + memcpy(trau_bits+310, fr->c_bits+15, 6); + + /* FIXME: handle timing adjustment */ + + /* T1 .. T4 */ + memcpy(trau_bits+316, fr->t_bits+0, 4); +} + + +int encode_trau_frame(u_int8_t *trau_bits, const struct decoded_trau_frame *fr) +{ + u_int8_t cbits5 = get_bits(fr->c_bits, 0, 5); + + /* 16 bits of sync header */ + memset(trau_bits, 0, 16); + + switch (cbits5) { + case TRAU_FT_FR_UP: + case TRAU_FT_FR_DOWN: + case TRAU_FT_IDLE_UP: + case TRAU_FT_IDLE_DOWN: + case TRAU_FT_EFR: + encode_fr(trau_bits, fr); + break; + case TRAU_FT_AMR: + case TRAU_FT_OM_UP: + case TRAU_FT_OM_DOWN: + case TRAU_FT_DATA_UP: + case TRAU_FT_DATA_DOWN: + case TRAU_FT_D145_SYNC: + case TRAU_FT_EDATA: + LOGP(DMUX, LOGL_NOTICE, "unimplemented TRAU Frame Type " + "0x%02x\n", cbits5); + return -1; + break; + default: + LOGP(DMUX, LOGL_NOTICE, "unknown TRAU Frame Type " + "0x%02x\n", cbits5); + return -1; + break; + } + + return 0; +} + +static struct decoded_trau_frame fr_idle_frame = { + .c_bits = { 0, 1, 1, 1, 0 }, /* IDLE DOWNLINK 3.5.5 */ + .t_bits = { 1, 1, 1, 1 }, +}; +static u_int8_t encoded_idle_frame[TRAU_FRAME_BITS]; +static int dbits_initted; + +u_int8_t *trau_idle_frame(void) +{ + /* only initialize during the first call */ + if (!dbits_initted) { + /* set all D-bits to 1 */ + memset(&fr_idle_frame.d_bits, 0x01, 260); + encode_fr(encoded_idle_frame, &fr_idle_frame); + } + return encoded_idle_frame; +} diff --git a/openbsc/src/libtrau/trau_mux.c b/openbsc/src/libtrau/trau_mux.c new file mode 100644 index 000000000..712e22d85 --- /dev/null +++ b/openbsc/src/libtrau/trau_mux.c @@ -0,0 +1,312 @@ +/* Simple TRAU frame reflector to route voice calls */ + +/* (C) 2009 by Harald Welte + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + * + */ + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +u_int8_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 +}; + +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; + u_int32_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(DMIB, 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, u_int32_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 u_int8_t c_bits_check[] = { 0, 0, 0, 1, 0 }; + +/* we get called by subchan_demux */ +int trau_mux_input(struct gsm_e1_subslot *src_e1_ss, + const u_int8_t *trau_bits, int num_bits) +{ + struct decoded_trau_frame tf; + u_int8_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; + struct gsm_data_frame *frame; + unsigned char *data; + int i, j, k, l, o; + /* 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, sizeof(c_bits_check))) + DEBUGPC(DMUX, "illegal trau (C1-C5) %s\n", + hexdump(tf.c_bits, sizeof(c_bits_check))); + msg = msgb_alloc(sizeof(struct gsm_data_frame) + 33, + "GSM-DATA"); + if (!msg) + return -ENOMEM; + + 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))); + if (--k < 0) { + o += gsm_fr_map[l]; + k = gsm_fr_map[++l]-1; + } + i++; + j++; + } + frame->msg_type = GSM_TCHF_FRAME; + frame->callref = ue->callref; + 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); +} + +/* add receiver instance for lchan and callref */ +int trau_recv_lchan(struct gsm_lchan *lchan, u_int32_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; +} + +int trau_send_frame(struct gsm_lchan *lchan, struct gsm_data_frame *frame) +{ + u_int8_t trau_bits_out[TRAU_FRAME_BITS]; + struct gsm_e1_subslot *dst_e1_ss = &lchan->ts->e1_link; + struct subch_mux *mx; + int i, j, k, l, o; + unsigned char *data = frame->data; + 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: + /* 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; + if (--k < 0) { + o += gsm_fr_map[l]; + k = gsm_fr_map[++l]-1; + } + i++; + j++; + } + break; + default: + DEBUGPC(DMUX, "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); +} diff --git a/openbsc/src/libtrau/trau_upqueue.c b/openbsc/src/libtrau/trau_upqueue.c new file mode 100644 index 000000000..f8edaf0ff --- /dev/null +++ b/openbsc/src/libtrau/trau_upqueue.c @@ -0,0 +1,27 @@ +/* trau_upqueue.c - Pass msgb's up the chain */ + +/* (C) 2010 by Harald Welte + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + * + */ + +#include +#include + +void trau_tx_to_mncc(struct gsm_network *net, struct msgb *msg) +{ + net->mncc_recv(net, msg); +} diff --git a/openbsc/src/mgcp/Makefile.am b/openbsc/src/mgcp/Makefile.am deleted file mode 100644 index b1d1d158a..000000000 --- a/openbsc/src/mgcp/Makefile.am +++ /dev/null @@ -1,7 +0,0 @@ -INCLUDES = $(all_includes) -I$(top_srcdir)/include -I$(top_builddir) -AM_CFLAGS=-Wall $(LIBOSMOCORE_CFLAGS) $(LIBOSMOVTY_CFLAGS) $(COVERAGE_CFLAGS) -AM_LDFLAGS = $(LIBOSMOCORE_LIBS) $(COVERAGE_LDFLAGS) - -noinst_LIBRARIES = libmgcp.a - -libmgcp_a_SOURCES = mgcp_protocol.c mgcp_network.c mgcp_vty.c diff --git a/openbsc/src/mgcp/mgcp_network.c b/openbsc/src/mgcp/mgcp_network.c deleted file mode 100644 index 51c08d151..000000000 --- a/openbsc/src/mgcp/mgcp_network.c +++ /dev/null @@ -1,579 +0,0 @@ -/* A Media Gateway Control Protocol Media Gateway: RFC 3435 */ -/* The protocol implementation */ - -/* - * (C) 2009-2011 by Holger Hans Peter Freyther - * (C) 2009-2011 by On-Waves - * All Rights Reserved - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation; either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - * - */ - -#include -#include -#include -#include - -#include -#include - -#include -#include - -#include -#include -#include - -#warning "Make use of the rtp proxy code" - -/* attempt to determine byte order */ -#include -#include -#include - -#ifndef __BYTE_ORDER -#error "__BYTE_ORDER should be defined by someone" -#endif - -/* according to rtp_proxy.c RFC 3550 */ -struct rtp_hdr { -#if __BYTE_ORDER == __LITTLE_ENDIAN - uint8_t csrc_count:4, - extension:1, - padding:1, - version:2; - uint8_t payload_type:7, - marker:1; -#elif __BYTE_ORDER == __BIG_ENDIAN - uint8_t version:2, - padding:1, - extension:1, - csrc_count:4; - uint8_t marker:1, - payload_type:7; -#endif - uint16_t sequence; - uint32_t timestamp; - uint32_t ssrc; -} __attribute__((packed)); - - -enum { - DEST_NETWORK = 0, - DEST_BTS = 1, -}; - -enum { - PROTO_RTP, - PROTO_RTCP, -}; - -#define DUMMY_LOAD 0x23 - - -static int 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[] = { DUMMY_LOAD }; - - return udp_send(endp->net_end.rtp.fd, &endp->net_end.addr, - endp->net_end.rtp_port, buf, 1); -} - -static void patch_and_count(struct mgcp_endpoint *endp, struct mgcp_rtp_state *state, - int payload, struct sockaddr_in *addr, char *data, int len) -{ - uint16_t seq; - uint32_t timestamp; - struct rtp_hdr *rtp_hdr; - - if (len < sizeof(*rtp_hdr)) - return; - - rtp_hdr = (struct rtp_hdr *) data; - seq = ntohs(rtp_hdr->sequence); - timestamp = ntohl(rtp_hdr->timestamp); - - if (!state->initialized) { - state->seq_no = seq - 1; - state->ssrc = state->orig_ssrc = rtp_hdr->ssrc; - state->initialized = 1; - state->last_timestamp = timestamp; - } else if (state->ssrc != rtp_hdr->ssrc) { - state->ssrc = rtp_hdr->ssrc; - state->seq_offset = (state->seq_no + 1) - seq; - state->timestamp_offset = state->last_timestamp - timestamp; - state->patch = endp->allow_patch; - LOGP(DMGCP, LOGL_NOTICE, - "The SSRC changed on 0x%x SSRC: %u offset: %d from %s:%d in %d\n", - ENDPOINT_NUMBER(endp), state->ssrc, state->seq_offset, - inet_ntoa(addr->sin_addr), ntohs(addr->sin_port), endp->conn_mode); - } - - /* apply the offset and store it back to the packet */ - if (state->patch) { - seq += state->seq_offset; - rtp_hdr->sequence = htons(seq); - rtp_hdr->ssrc = state->orig_ssrc; - - timestamp += state->timestamp_offset; - rtp_hdr->timestamp = htonl(timestamp); - } - - /* seq changed, now compare if we have lost something */ - if (state->seq_no + 1u != seq) - state->lost_no = abs(seq - (state->seq_no + 1)); - state->seq_no = seq; - - state->last_timestamp = timestamp; - - 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 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; -} - -static int send_to(struct mgcp_endpoint *endp, int dest, int is_rtp, - struct sockaddr_in *addr, char *buf, int rc) -{ - struct mgcp_trunk_config *tcfg = endp->tcfg; - /* 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 == DEST_NETWORK) { - if (is_rtp) { - patch_and_count(endp, &endp->bts_state, - endp->net_end.payload_type, - addr, buf, rc); - forward_data(endp->net_end.rtp.fd, - &endp->taps[MGCP_TAP_NET_OUT], buf, rc); - return udp_send(endp->net_end.rtp.fd, &endp->net_end.addr, - endp->net_end.rtp_port, buf, rc); - } else { - return udp_send(endp->net_end.rtcp.fd, &endp->net_end.addr, - endp->net_end.rtcp_port, buf, rc); - } - } else { - if (is_rtp) { - patch_and_count(endp, &endp->net_state, - endp->bts_end.payload_type, - addr, buf, rc); - forward_data(endp->bts_end.rtp.fd, - &endp->taps[MGCP_TAP_BTS_OUT], buf, rc); - return udp_send(endp->bts_end.rtp.fd, &endp->bts_end.addr, - endp->bts_end.rtp_port, buf, rc); - } else { - return udp_send(endp->bts_end.rtcp.fd, &endp->bts_end.addr, - endp->bts_end.rtcp_port, buf, rc); - } - } -} - -static int recevice_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 bsc_fd *fd, unsigned int what) -{ - char buf[4096]; - struct sockaddr_in addr; - struct mgcp_endpoint *endp; - int rc, proto; - - endp = (struct mgcp_endpoint *) fd->data; - - rc = recevice_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, - "Data from wrong address %s on 0x%x\n", - inet_ntoa(addr.sin_addr), ENDPOINT_NUMBER(endp)); - return -1; - } - - 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; - } - - /* throw away the dummy message */ - if (rc == 1 && buf[0] == 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 ? PROTO_RTP : PROTO_RTCP; - endp->net_end.packets += 1; - - forward_data(fd->fd, &endp->taps[MGCP_TAP_NET_IN], buf, rc); - if (endp->is_transcoded) - return send_transcoder(&endp->trans_net, endp->cfg, proto == PROTO_RTP, &buf[0], rc); - else - return send_to(endp, DEST_BTS, proto == PROTO_RTP, &addr, &buf[0], rc); -} - -static void discover_bts(struct mgcp_endpoint *endp, int proto, struct sockaddr_in *addr) -{ - struct mgcp_config *cfg = endp->cfg; - - if (proto == 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 == 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 bsc_fd *fd, unsigned int what) -{ - char buf[4096]; - struct sockaddr_in addr; - struct mgcp_endpoint *endp; - struct mgcp_config *cfg; - int rc, proto; - - endp = (struct mgcp_endpoint *) fd->data; - cfg = endp->cfg; - - rc = recevice_from(endp, fd->fd, &addr, buf, sizeof(buf)); - if (rc <= 0) - return -1; - - proto = fd == &endp->bts_end.rtp ? PROTO_RTP : 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] == 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; - - forward_data(fd->fd, &endp->taps[MGCP_TAP_BTS_IN], buf, rc); - if (endp->is_transcoded) - return send_transcoder(&endp->trans_bts, endp->cfg, proto == PROTO_RTP, &buf[0], rc); - else - return send_to(endp, DEST_NETWORK, proto == PROTO_RTP, &addr, &buf[0], rc); -} - -static int rtp_data_transcoder(struct mgcp_rtp_end *end, struct mgcp_endpoint *_endp, - int dest, struct bsc_fd *fd) -{ - char buf[4096]; - struct sockaddr_in addr; - struct mgcp_config *cfg; - int rc, proto; - - cfg = _endp->cfg; - rc = recevice_from(_endp, fd->fd, &addr, buf, sizeof(buf)); - if (rc <= 0) - return -1; - - proto = fd == &end->rtp ? PROTO_RTP : 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] == 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 send_to(_endp, dest, proto == PROTO_RTP, &addr, &buf[0], rc); -} - -static int rtp_data_trans_net(struct bsc_fd *fd, unsigned int what) -{ - struct mgcp_endpoint *endp; - endp = (struct mgcp_endpoint *) fd->data; - - return rtp_data_transcoder(&endp->trans_net, endp, DEST_NETWORK, fd); -} - -static int rtp_data_trans_bts(struct bsc_fd *fd, unsigned int what) -{ - struct mgcp_endpoint *endp; - endp = (struct mgcp_endpoint *) fd->data; - - return rtp_data_transcoder(&endp->trans_bts, endp, DEST_BTS, fd); -} - -static int create_bind(const char *source_addr, struct bsc_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; -} - -static int 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, struct mgcp_rtp_end *rtp_end, int endpno) -{ - if (create_bind(cfg->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", - cfg->source_addr, rtp_end->local_port, endpno); - goto cleanup0; - } - - if (create_bind(cfg->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", - cfg->source_addr, rtp_end->local_port + 1, endpno); - goto cleanup1; - } - - set_ip_tos(rtp_end->rtp.fd, cfg->endp_dscp); - set_ip_tos(rtp_end->rtcp.fd, cfg->endp_dscp); - - rtp_end->rtp.when = BSC_FD_READ; - if (bsc_register_fd(&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 (bsc_register_fd(&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: - bsc_unregister_fd(&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 bsc_fd *, unsigned), - struct mgcp_endpoint *_endp, 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, 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, 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, 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, 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, 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; - bsc_unregister_fd(&end->rtp); - } - - if (end->rtcp.fd != -1) { - close(end->rtcp.fd); - end->rtcp.fd = -1; - bsc_unregister_fd(&end->rtcp); - } - - return 0; -} diff --git a/openbsc/src/mgcp/mgcp_protocol.c b/openbsc/src/mgcp/mgcp_protocol.c deleted file mode 100644 index ba290dd90..000000000 --- a/openbsc/src/mgcp/mgcp_protocol.c +++ /dev/null @@ -1,1102 +0,0 @@ -/* A Media Gateway Control Protocol Media Gateway: RFC 3435 */ -/* The protocol implementation */ - -/* - * (C) 2009-2011 by Holger Hans Peter Freyther - * (C) 2009-2011 by On-Waves - * All Rights Reserved - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation; either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - * - */ - -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include - -/** - * Macro for tokenizing MGCP messages and SDP in one go. - * - */ -#define MSG_TOKENIZE_START \ - line_start = 0; \ - for (i = 0; i < msgb_l3len(msg); ++i) { \ - /* we have a line end */ \ - if (msg->l3h[i] == '\n') { \ - /* skip the first line */ \ - if (line_start == 0) { \ - line_start = i + 1; \ - continue; \ - } \ - \ - /* check if we have a proper param */ \ - if (i - line_start == 1 && msg->l3h[line_start] == '\r') { \ - } else if (i - line_start > 2 \ - && islower(msg->l3h[line_start]) \ - && msg->l3h[line_start + 1] == '=') { \ - } else if (i - line_start < 3 \ - || msg->l3h[line_start + 1] != ':' \ - || msg->l3h[line_start + 2] != ' ') \ - goto error; \ - \ - msg->l3h[i] = '\0'; \ - if (msg->l3h[i-1] == '\r') \ - msg->l3h[i-1] = '\0'; - -#define MSG_TOKENIZE_END \ - line_start = i + 1; \ - } \ - } - -static void mgcp_rtp_end_reset(struct mgcp_rtp_end *end); - -struct mgcp_request { - char *name; - struct msgb *(*handle_request) (struct mgcp_config *cfg, struct msgb *msg); - 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_config *cfg, struct msgb *msg); -static struct msgb *handle_create_con(struct mgcp_config *cfg, struct msgb *msg); -static struct msgb *handle_delete_con(struct mgcp_config *cfg, struct msgb *msg); -static struct msgb *handle_modify_con(struct mgcp_config *cfg, struct msgb *msg); -static struct msgb *handle_rsip(struct mgcp_config *cfg, struct msgb *msg); -static struct msgb *handle_noti_req(struct mgcp_config *cfg, struct msgb *msg); - -static void create_transcoder(struct mgcp_endpoint *endp); -static void delete_transcoder(struct mgcp_endpoint *endp); - -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; -} - -struct msgb *mgcp_create_response_with_data(int code, const char *txt, - const char *msg, const char *trans, - const char *data) -{ - int len; - struct msgb *res; - - res = mgcp_msgb_alloc(); - if (!res) - return NULL; - - if (data) { - len = snprintf((char *) res->data, 2048, "%d %s%s\r\n%s", code, trans, txt, data); - } else { - len = snprintf((char *) res->data, 2048, "%d %s%s\r\n", code, trans, txt); - } - - res->l2h = msgb_put(res, len); - LOGP(DMGCP, LOGL_DEBUG, "Sending response: code: %d for '%s'\n", code, res->l2h); - return res; -} - -static struct msgb *create_ok_response(int code, const char *msg, const char *trans) -{ - return mgcp_create_response_with_data(code, " OK", msg, trans, NULL); -} - -static struct msgb *create_err_response(int code, const char *msg, const char *trans) -{ - return mgcp_create_response_with_data(code, " FAIL", msg, trans, NULL); -} - -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]; - - if (!addr) - addr = endp->cfg->source_addr; - - snprintf(sdp_record, sizeof(sdp_record) - 1, - "I: %u\n\n" - "v=0\r\n" - "c=IN IP4 %s\r\n" - "m=audio %d RTP/AVP %d\r\n" - "a=rtpmap:%d %s\r\n", - endp->ci, addr, endp->net_end.local_port, - endp->bts_end.payload_type, endp->bts_end.payload_type, - endp->tcfg->audio_name); - return mgcp_create_response_with_data(200, " OK", msg, trans_id, sdp_record); -} - -/* - * 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) -{ - int code; - struct msgb *resp = NULL; - - if (msgb_l2len(msg) < 4) { - LOGP(DMGCP, LOGL_ERROR, "mgs too short: %d\n", msg->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); - } else { - int i, handled = 0; - msg->l3h = &msg->l2h[4]; - 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(cfg, msg); - break; - } - if (!handled) { - LOGP(DMGCP, LOGL_NOTICE, "MSG with type: '%.4s' not handled\n", &msg->l2h[0]); - } - } - - return resp; -} - -/* string tokenizer for the poor */ -static int find_msg_pointers(struct msgb *msg, struct mgcp_msg_ptr *ptrs, int ptrs_length) -{ - int i, found = 0; - - int whitespace = 1; - for (i = 0; i < msgb_l3len(msg) && ptrs_length > 0; ++i) { - /* if we have a space we found an end */ - if (msg->l3h[i] == ' ' || msg->l3h[i] == '\r' || msg->l3h[i] == '\n') { - if (!whitespace) { - ++found; - whitespace = 1; - ptrs->length = i - ptrs->start - 1; - ++ptrs; - --ptrs_length; - } else { - /* skip any number of whitespace */ - } - - /* line end... stop */ - if (msg->l3h[i] == '\r' || msg->l3h[i] == '\n') - break; - } else if (msg->l3h[i] == '\r' || msg->l3h[i] == '\n') { - /* line end, be done */ - break; - } else if (whitespace) { - whitespace = 0; - ptrs->start = i; - } - } - - if (ptrs_length == 0) - return -1; - return found; -} - -/** - * 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); - } else { - gw = strtoul(mgcp, &endptr, 16); - if (gw > 0 && gw < cfg->trunk.number_endpoints && strcmp(endptr, "@mgw") == 0) - return &cfg->trunk.endpoints[gw]; - } - - LOGP(DMGCP, LOGL_ERROR, "Not able to find endpoint: '%s'\n", mgcp); - return NULL; -} - -int mgcp_analyze_header(struct mgcp_config *cfg, struct msgb *msg, - struct mgcp_msg_ptr *ptr, int size, - const char **transaction_id, struct mgcp_endpoint **endp) -{ - int found; - - *transaction_id = "000000"; - - if (size < 3) { - LOGP(DMGCP, LOGL_ERROR, "Not enough space in ptr\n"); - return -1; - } - - found = find_msg_pointers(msg, ptr, size); - - if (found <= 3) { - LOGP(DMGCP, LOGL_ERROR, "Gateway: Not enough params. Found: %d\n", found); - return -1; - } - - /* - * replace the space with \0. the main method gurantess that - * we still have + 1 for null termination - */ - msg->l3h[ptr[3].start + ptr[3].length + 1] = '\0'; - msg->l3h[ptr[2].start + ptr[2].length + 1] = '\0'; - msg->l3h[ptr[1].start + ptr[1].length + 1] = '\0'; - msg->l3h[ptr[0].start + ptr[0].length + 1] = '\0'; - - if (strncmp("1.0", (const char *)&msg->l3h[ptr[3].start], 3) != 0 - || strncmp("MGCP", (const char *)&msg->l3h[ptr[2].start], 4) != 0) { - LOGP(DMGCP, LOGL_ERROR, "Wrong MGCP version. Not handling: '%s' '%s'\n", - (const char *)&msg->l3h[ptr[3].start], - (const char *)&msg->l3h[ptr[2].start]); - return -1; - } - - *transaction_id = (const char *)&msg->l3h[ptr[0].start]; - if (endp) { - *endp = find_endpoint(cfg, (const char *)&msg->l3h[ptr[1].start]); - return *endp == NULL; - } - 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_config *cfg, struct msgb *msg) -{ - struct mgcp_msg_ptr data_ptrs[6]; - int found; - const char *trans_id; - struct mgcp_endpoint *endp; - - found = mgcp_analyze_header(cfg, msg, data_ptrs, ARRAY_SIZE(data_ptrs), &trans_id, &endp); - if (found != 0) - return create_err_response(500, "AUEP", trans_id); - else - return create_ok_response(200, "AUEP", trans_id); -} - -static int parse_conn_mode(const char *msg, int *conn_mode) -{ - int ret = 0; - if (strcmp(msg, "recvonly") == 0) - *conn_mode = MGCP_CONN_RECV_ONLY; - else if (strcmp(msg, "sendrecv") == 0) - *conn_mode = MGCP_CONN_RECV_SEND; - else if (strcmp(msg, "sendonly") == 0) - *conn_mode = MGCP_CONN_SEND_ONLY; - else if (strcmp(msg, "loopback") == 0) - *conn_mode = MGCP_CONN_LOOPBACK; - else { - LOGP(DMGCP, LOGL_ERROR, "Unknown connection mode: '%s'\n", msg); - ret = -1; - } - - 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->is_transcoded = 1; - } - - return 0; -} - -static struct msgb *handle_create_con(struct mgcp_config *cfg, struct msgb *msg) -{ - struct mgcp_msg_ptr data_ptrs[6]; - int found, i, line_start; - const char *trans_id; - struct mgcp_trunk_config *tcfg; - struct mgcp_endpoint *endp; - int error_code = 400; - - found = mgcp_analyze_header(cfg, msg, data_ptrs, ARRAY_SIZE(data_ptrs), &trans_id, &endp); - if (found != 0) - return create_err_response(510, "CRCX", trans_id); - - tcfg = endp->tcfg; - - if (endp->allocated) { - if (tcfg->force_realloc) { - LOGP(DMGCP, LOGL_NOTICE, "Endpoint 0x%x already allocated. Forcing realloc.\n", - ENDPOINT_NUMBER(endp)); - mgcp_free_endp(endp); - if (cfg->realloc_cb) - 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(400, "CRCX", trans_id); - } - } - - /* parse CallID C: and LocalParameters L: */ - MSG_TOKENIZE_START - switch (msg->l3h[line_start]) { - case 'L': - endp->local_options = talloc_strdup(tcfg->endpoints, - (const char *)&msg->l3h[line_start + 3]); - break; - case 'C': - endp->callid = talloc_strdup(tcfg->endpoints, - (const char *)&msg->l3h[line_start + 3]); - break; - case 'M': - if (parse_conn_mode((const char *)&msg->l3h[line_start + 3], - &endp->conn_mode) != 0) { - error_code = 517; - goto error2; - } - - endp->orig_mode = endp->conn_mode; - break; - default: - LOGP(DMGCP, LOGL_NOTICE, "Unhandled option: '%c'/%d on 0x%x\n", - msg->l3h[line_start], msg->l3h[line_start], - ENDPOINT_NUMBER(endp)); - break; - } - MSG_TOKENIZE_END - - /* initialize */ - endp->net_end.rtp_port = endp->net_end.rtcp_port = endp->bts_end.rtp_port = endp->bts_end.rtcp_port = 0; - - /* 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(cfg); - if (endp->ci == CI_UNUSED) - goto error2; - - endp->allocated = 1; - endp->bts_end.payload_type = tcfg->audio_payload; - - /* policy CB */ - if (cfg->policy_cb) { - switch (cfg->policy_cb(tcfg, ENDPOINT_NUMBER(endp), MGCP_ENDP_CRCX, trans_id)) { - case MGCP_POLICY_REJECT: - LOGP(DMGCP, LOGL_NOTICE, "CRCX rejected by policy on 0x%x\n", - ENDPOINT_NUMBER(endp)); - mgcp_free_endp(endp); - return create_err_response(400, "CRCX", trans_id); - 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 (cfg->change_cb) - cfg->change_cb(tcfg, ENDPOINT_NUMBER(endp), MGCP_ENDP_CRCX); - - create_transcoder(endp); - return create_response_with_sdp(endp, "CRCX", trans_id); -error: - LOGP(DMGCP, LOGL_ERROR, "Malformed line: %s on 0x%x with: line_start: %d %d\n", - hexdump(msg->l3h, msgb_l3len(msg)), - ENDPOINT_NUMBER(endp), line_start, i); - return create_err_response(error_code, "CRCX", trans_id); - -error2: - mgcp_free_endp(endp); - LOGP(DMGCP, LOGL_NOTICE, "Resource error on 0x%x\n", ENDPOINT_NUMBER(endp)); - return create_err_response(error_code, "CRCX", trans_id); -} - -static struct msgb *handle_modify_con(struct mgcp_config *cfg, struct msgb *msg) -{ - struct mgcp_msg_ptr data_ptrs[6]; - int found, i, line_start; - const char *trans_id; - struct mgcp_endpoint *endp; - int error_code = 500; - int silent = 0; - - found = mgcp_analyze_header(cfg, msg, data_ptrs, ARRAY_SIZE(data_ptrs), &trans_id, &endp); - if (found != 0) - return create_err_response(510, "MDCX", trans_id); - - 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(400, "MDCX", trans_id); - } - - MSG_TOKENIZE_START - switch (msg->l3h[line_start]) { - case 'C': { - if (verify_call_id(endp, (const char *)&msg->l3h[line_start + 3]) != 0) - goto error3; - break; - } - case 'I': { - if (verify_ci(endp, (const char *)&msg->l3h[line_start + 3]) != 0) - goto error3; - break; - } - case 'L': - /* skip */ - break; - case 'M': - if (parse_conn_mode((const char *)&msg->l3h[line_start + 3], - &endp->conn_mode) != 0) { - error_code = 517; - goto error3; - } - endp->orig_mode = endp->conn_mode; - break; - case 'Z': - silent = strcmp("noanswer", (const char *)&msg->l3h[line_start + 3]) == 0; - break; - case '\0': - /* SDP file begins */ - break; - case 'a': - case 'o': - case 's': - case 't': - case 'v': - /* skip these SDP attributes */ - break; - case 'm': { - int port; - int payload; - const char *param = (const char *)&msg->l3h[line_start]; - - if (sscanf(param, "m=audio %d RTP/AVP %d", &port, &payload) == 2) { - endp->net_end.rtp_port = htons(port); - endp->net_end.rtcp_port = htons(port + 1); - endp->net_end.payload_type = payload; - } - break; - } - case 'c': { - char ipv4[16]; - const char *param = (const char *)&msg->l3h[line_start]; - - if (sscanf(param, "c=IN IP4 %15s", ipv4) == 1) { - inet_aton(ipv4, &endp->net_end.addr); - } - break; - } - default: - LOGP(DMGCP, LOGL_NOTICE, "Unhandled option: '%c'/%d on 0x%x\n", - msg->l3h[line_start], msg->l3h[line_start], - ENDPOINT_NUMBER(endp)); - break; - } - MSG_TOKENIZE_END - - /* policy CB */ - if (cfg->policy_cb) { - switch (cfg->policy_cb(endp->tcfg, ENDPOINT_NUMBER(endp), MGCP_ENDP_MDCX, trans_id)) { - 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(400, "MDCX", trans_id); - break; - case MGCP_POLICY_DEFER: - /* stop processing */ - return NULL; - break; - case MGCP_POLICY_CONT: - /* just continue */ - break; - } - } - - /* 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 (cfg->change_cb) - cfg->change_cb(endp->tcfg, ENDPOINT_NUMBER(endp), MGCP_ENDP_MDCX); - if (silent) - goto out_silent; - - return create_response_with_sdp(endp, "MDCX", trans_id); - -error: - LOGP(DMGCP, LOGL_ERROR, "Malformed line: %s on 0x%x with: line_start: %d %d %d\n", - hexdump(msg->l3h, msgb_l3len(msg)), - ENDPOINT_NUMBER(endp), line_start, i, msg->l3h[line_start]); - return create_err_response(error_code, "MDCX", trans_id); - -error3: - return create_err_response(error_code, "MDCX", trans_id); - - -out_silent: - return NULL; -} - -static struct msgb *handle_delete_con(struct mgcp_config *cfg, struct msgb *msg) -{ - struct mgcp_msg_ptr data_ptrs[6]; - int found, i, line_start; - const char *trans_id; - struct mgcp_endpoint *endp; - int error_code = 400; - int silent = 0; - - found = mgcp_analyze_header(cfg, msg, data_ptrs, ARRAY_SIZE(data_ptrs), &trans_id, &endp); - if (found != 0) - return create_err_response(error_code, "DLCX", trans_id); - - if (!endp->allocated) { - LOGP(DMGCP, LOGL_ERROR, "Endpoint is not used. 0x%x\n", ENDPOINT_NUMBER(endp)); - return create_err_response(400, "DLCX", trans_id); - } - - MSG_TOKENIZE_START - switch (msg->l3h[line_start]) { - case 'C': { - if (verify_call_id(endp, (const char *)&msg->l3h[line_start + 3]) != 0) - goto error3; - break; - } - case 'I': { - if (verify_ci(endp, (const char *)&msg->l3h[line_start + 3]) != 0) - goto error3; - break; - case 'Z': - silent = strcmp("noanswer", (const char *)&msg->l3h[line_start + 3]) == 0; - break; - } - default: - LOGP(DMGCP, LOGL_NOTICE, "Unhandled option: '%c'/%d on 0x%x\n", - msg->l3h[line_start], msg->l3h[line_start], - ENDPOINT_NUMBER(endp)); - break; - } - MSG_TOKENIZE_END - - /* policy CB */ - if (cfg->policy_cb) { - switch (cfg->policy_cb(endp->tcfg, ENDPOINT_NUMBER(endp), MGCP_ENDP_DLCX, trans_id)) { - 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(400, "DLCX", trans_id); - 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)); - - delete_transcoder(endp); - mgcp_free_endp(endp); - if (cfg->change_cb) - cfg->change_cb(endp->tcfg, ENDPOINT_NUMBER(endp), MGCP_ENDP_DLCX); - - if (silent) - goto out_silent; - return create_ok_response(250, "DLCX", trans_id); - -error: - LOGP(DMGCP, LOGL_ERROR, "Malformed line: %s on 0x%x with: line_start: %d %d\n", - hexdump(msg->l3h, msgb_l3len(msg)), - ENDPOINT_NUMBER(endp), line_start, i); - return create_err_response(error_code, "DLCX", trans_id); - -error3: - return create_err_response(error_code, "DLCX", trans_id); - -out_silent: - return NULL; -} - -static struct msgb *handle_rsip(struct mgcp_config *cfg, struct msgb *msg) -{ - if (cfg->reset_cb) - cfg->reset_cb(cfg); - return NULL; -} - -/* - * 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_config *cfg, struct msgb *msg) -{ - struct mgcp_msg_ptr data_ptrs[6]; - const char *trans_id; - struct mgcp_endpoint *endp; - int found; - - found = mgcp_analyze_header(cfg, msg, data_ptrs, ARRAY_SIZE(data_ptrs), &trans_id, &endp); - if (found != 0) - return create_err_response(400, "RQNT", trans_id); - - if (!endp->allocated) { - LOGP(DMGCP, LOGL_ERROR, "Endpoint is not used. 0x%x\n", ENDPOINT_NUMBER(endp)); - return create_err_response(400, "RQNT", trans_id); - } - return create_ok_response(200, "RQNT", trans_id); -} - -struct mgcp_config *mgcp_config_alloc(void) -{ - struct mgcp_config *cfg; - - cfg = talloc_zero(NULL, struct mgcp_config); - if (!cfg) { - LOGP(DMGCP, LOGL_FATAL, "Failed to allocate config.\n"); - return NULL; - } - - cfg->source_port = 2427; - cfg->source_addr = talloc_strdup(cfg, "0.0.0.0"); - - cfg->transcoder_remote_base = 4000; - - cfg->bts_ports.base_port = RTP_PORT_DEFAULT; - cfg->net_ports.base_port = RTP_PORT_NET_DEFAULT; - - /* 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; - - 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->number_endpoints = 33; - 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_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; - memset(&end->addr, 0, sizeof(end->addr)); - end->rtp_port = end->rtcp_port = 0; - end->payload_type = -1; - end->local_alloc = -1; -} - -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].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_free_endp(struct mgcp_endpoint *endp) -{ - LOGP(DMGCP, LOGL_DEBUG, "Deleting endpoint on: 0x%x\n", ENDPOINT_NUMBER(endp)); - endp->ci = CI_UNUSED; - endp->allocated = 0; - - if (endp->callid) { - talloc_free(endp->callid); - endp->callid = NULL; - } - - if (endp->local_options) { - talloc_free(endp->local_options); - endp->local_options = 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->is_transcoded = 0; - - 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; - endp->allow_patch = 0; - - memset(&endp->taps, 0, sizeof(endp->taps)); -} - -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; - - /* 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" - "c=IN IP4 %s\r\n" - "m=audio %d RTP/AVP %d\r\n" - "a=rtpmap:%d %s\r\n", - msg, endpoint, mode, endp->cfg->source_addr, - port, endp->tcfg->audio_payload, - endp->tcfg->audio_payload, endp->tcfg->audio_name); - - if (len < 0) - return; - - 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 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->is_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->is_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); -} diff --git a/openbsc/src/mgcp/mgcp_vty.c b/openbsc/src/mgcp/mgcp_vty.c deleted file mode 100644 index c299a98cc..000000000 --- a/openbsc/src/mgcp/mgcp_vty.c +++ /dev/null @@ -1,742 +0,0 @@ -/* A Media Gateway Control Protocol Media Gateway: RFC 3435 */ -/* The protocol implementation */ - -/* - * (C) 2009-2011 by Holger Hans Peter Freyther - * (C) 2009-2011 by On-Waves - * All Rights Reserved - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation; either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - * - */ - -#include - -#include - -#include -#include -#include -#include - -#include -#include - -#include - -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(mgcp)#", - 1, -}; - -struct cmd_node trunk_node = { - TRUNK_NODE, - "%s(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->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); - - vty_out(vty, " rtp ip-dscp %d%s", g_cfg->endp_dscp, 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); - 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); - 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); - vty_out(vty, " transcoder-remote-base %u%s", g_cfg->transcoder_remote_base, VTY_NEWLINE); - - return CMD_SUCCESS; -} - -static void dump_trunk(struct vty *vty, struct mgcp_trunk_config *cfg) -{ - 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/%u remote: %u/%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->bts_state.lost_no, - endp->net_end.packets, endp->net_state.lost_no, - endp->trans_net.packets, endp->trans_bts.packets, - VTY_NEWLINE); - } -} - -DEFUN(show_mcgp, show_mgcp_cmd, "show mgcp", - SHOW_STR "Display information about the MGCP Media Gateway") -{ - struct mgcp_trunk_config *trunk; - - dump_trunk(vty, &g_cfg->trunk); - - llist_for_each_entry(trunk, &g_cfg->trunks, entry) - dump_trunk(vty, trunk); - - 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", - "Set the IP to be used in SDP records") -{ - bsc_replace_string(g_cfg, &g_cfg->local_ip, argv[0]); - return CMD_SUCCESS; -} - -DEFUN(cfg_mgcp_bts_ip, - cfg_mgcp_bts_ip_cmd, - "bts ip A.B.C.D", - "Set the IP of the BTS for RTP forwarding") -{ - bsc_replace_string(g_cfg, &g_cfg->bts_ip, argv[0]); - inet_aton(g_cfg->bts_ip, &g_cfg->bts_in); - return CMD_SUCCESS; -} - -DEFUN(cfg_mgcp_bind_ip, - cfg_mgcp_bind_ip_cmd, - "bind ip A.B.C.D", - "Bind the MGCP to this local addr") -{ - bsc_replace_string(g_cfg, &g_cfg->source_addr, argv[0]); - return CMD_SUCCESS; -} - -DEFUN(cfg_mgcp_bind_port, - cfg_mgcp_bind_port_cmd, - "bind port <0-65534>", - "Bind the MGCP to this port") -{ - 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 all RTP ports early") -{ - 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; -} - - -DEFUN(cfg_mgcp_rtp_bts_base_port, - cfg_mgcp_rtp_bts_base_port_cmd, - "rtp bts-base <0-65534>", - "Base port to use") -{ - parse_base(&g_cfg->bts_ports, argv); - return CMD_SUCCESS; -} - -DEFUN(cfg_mgcp_rtp_bts_range, - cfg_mgcp_rtp_bts_range_cmd, - "rtp bts-range <0-65534> <0-65534>", - "Range of ports to allocate for endpoints\n" - "Start of the range of ports\n" "End of the range of ports\n") -{ - 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>", - "Range of ports to allocate for endpoints\n" - "Start of the range of ports\n" "End of the range of ports\n") -{ - 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>", - "Base port to use for network port\n" "Port\n") -{ - 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>", "Base port to use") - -DEFUN(cfg_mgcp_rtp_transcoder_range, - cfg_mgcp_rtp_transcoder_range_cmd, - "rtp transcoder-range <0-65534> <0-65534>", - "Range of ports to allocate for the transcoder\n" - "Start of the range of ports\n" "End of the range of ports\n") -{ - 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>", - "Base port for the transcoder range\n" "Port\n") -{ - parse_base(&g_cfg->transcoder_ports, argv); - return CMD_SUCCESS; -} - -DEFUN(cfg_mgcp_rtp_ip_dscp, - cfg_mgcp_rtp_ip_dscp_cmd, - "rtp ip-dscp <0-255>", - "Set the IP_TOS socket attribute on the RTP/RTCP sockets.\n" "The DSCP value.") -{ - 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>", - "Set the IP_TOS socket attribute on the RTP/RTCP sockets.\n" "The DSCP value.") - - -DEFUN(cfg_mgcp_sdp_payload_number, - cfg_mgcp_sdp_payload_number_cmd, - "sdp audio payload number <1-255>", - "Set the audio codec to use") -{ - unsigned int payload = atoi(argv[0]); - g_cfg->trunk.audio_payload = payload; - return CMD_SUCCESS; -} - -DEFUN(cfg_mgcp_sdp_payload_name, - cfg_mgcp_sdp_payload_name_cmd, - "sdp audio payload name NAME", - "Set the audio name to use") -{ - bsc_replace_string(g_cfg, &g_cfg->trunk.audio_name, argv[0]); - return CMD_SUCCESS; -} - -DEFUN(cfg_mgcp_loop, - cfg_mgcp_loop_cmd, - "loop (0|1)", - "Loop the audio") -{ - 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>", - "The number of endpoints to allocate. This is not dynamic.") -{ - /* + 1 as we start counting at one */ - g_cfg->trunk.number_endpoints = atoi(argv[0]) + 1; - return CMD_SUCCESS; -} - -DEFUN(cfg_mgcp_agent_addr, - cfg_mgcp_agent_addr_cmd, - "call agent ip IP", - "Set the address of the call agent.") -{ - bsc_replace_string(g_cfg, &g_cfg->call_agent_addr, argv[0]); - return CMD_SUCCESS; -} - -DEFUN(cfg_mgcp_transcoder, - cfg_mgcp_transcoder_cmd, - "transcoder-mgw A.B.C.D", - "Use a MGW to detranscoder RTP\n" - "The IP address of the MGW") -{ - bsc_replace_string(g_cfg, &g_cfg->transcoder_ip, argv[0]); - inet_aton(g_cfg->transcoder_ip, &g_cfg->transcoder_in); - - return CMD_SUCCESS; -} - -DEFUN(cfg_mgcp_no_transcoder, - cfg_mgcp_no_transcoder_cmd, - NO_STR "transcoder-mgw", - "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, " loop %d%s", - trunk->audio_loop, VTY_NEWLINE); - } - - return CMD_SUCCESS; -} - -DEFUN(cfg_trunk_payload_number, - cfg_trunk_payload_number_cmd, - "sdp audio payload number <1-255>", - "SDP related\n" "Audio\n" "Payload\n" "Payload Number\n") -{ - struct mgcp_trunk_config *trunk = vty->index; - unsigned int payload = atoi(argv[0]); - - trunk->audio_payload = payload; - return CMD_SUCCESS; -} - -DEFUN(cfg_trunk_payload_name, - cfg_trunk_payload_name_cmd, - "sdp audio payload name NAME", - "SDP related\n" "Audio\n" "Payload\n" "Payload Name\n") -{ - struct mgcp_trunk_config *trunk = vty->index; - - bsc_replace_string(g_cfg, &trunk->audio_name, argv[0]); - return CMD_SUCCESS; -} - -DEFUN(cfg_trunk_loop, - cfg_trunk_loop_cmd, - "loop (0|1)", - "Loop the audio") -{ - struct mgcp_trunk_config *trunk = vty->index; - - trunk->audio_loop = atoi(argv[0]); - 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; - endp->allow_patch = 1; - - 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_free_endp(endp); - 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(CONFIG_NODE, &cfg_mgcp_cmd); - install_node(&mgcp_node, config_write_mgcp); - - install_default(MGCP_NODE); - install_element(MGCP_NODE, &ournode_exit_cmd); - install_element(MGCP_NODE, &ournode_end_cmd); - 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_net_range_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_agent_addr_cmd); - 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_loop_cmd); - install_element(MGCP_NODE, &cfg_mgcp_number_endp_cmd); - - install_element(MGCP_NODE, &cfg_mgcp_trunk_cmd); - install_node(&trunk_node, config_write_trunk); - install_default(TRUNK_NODE); - install_element(TRUNK_NODE, &ournode_exit_cmd); - install_element(TRUNK_NODE, &ournode_end_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_loop_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) -{ - int rc; - struct mgcp_trunk_config *trunk; - - 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; - } - } - - return 0; -} - diff --git a/openbsc/src/msc/Makefile.am b/openbsc/src/msc/Makefile.am deleted file mode 100644 index 7d895c394..000000000 --- a/openbsc/src/msc/Makefile.am +++ /dev/null @@ -1,19 +0,0 @@ -INCLUDES = $(all_includes) -I$(top_srcdir)/include -I$(top_builddir) -AM_CFLAGS=-Wall $(LIBOSMOCORE_CFLAGS) $(LIBOSMOVTY_CFLAGS) $(COVERAGE_CFLAGS) -AM_LDFLAGS = $(LIBOSMOCORE_LIBS) $(COVERAGE_LDFLAGS) - -noinst_LIBRARIES = libmsc.a - -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 \ - osmo_msc.c - diff --git a/openbsc/src/msc/auth.c b/openbsc/src/msc/auth.c deleted file mode 100644 index e09bde5f3..000000000 --- a/openbsc/src/msc/auth.c +++ /dev/null @@ -1,132 +0,0 @@ -/* Authentication related functions */ - -/* - * (C) 2010 by Sylvain Munaut - * - * All Rights Reserved - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation; either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - * - */ - -#include -#include -#include -#include - -#include - -#include - - -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, - hexdump(ainfo->a3a8_ki, ainfo->a3a8_ki_len)); - return -1; - } - - for (i=0; i<4; i++) - atuple->sres[i] = atuple->rand[i] ^ ainfo->a3a8_ki[i]; - for (i=4; i<12; i++) - atuple->kc[i-4] = atuple->rand[i] ^ ainfo->a3a8_ki[i]; - - return 0; -} - -static int -_use_comp128_v1(struct gsm_auth_info *ainfo, struct gsm_auth_tuple *atuple) -{ - if (ainfo->a3a8_ki_len != A38_COMP128_KEY_LEN) { - LOGP(DMM, LOGL_ERROR, "Invalid COMP128v1 key (len=%d) %s\n", - ainfo->a3a8_ki_len, - hexdump(ainfo->a3a8_ki, ainfo->a3a8_ki_len)); - return -1; - } - - comp128(ainfo->a3a8_ki, atuple->rand, atuple->sres, atuple->kc); - - return 0; -} - -/* Return values - * -1 -> Internal error - * 0 -> Not available - * 1 -> Tuple returned, need to do auth, then enable cipher - * 2 -> Tuple returned, need to enable cipher - */ -int auth_get_tuple_for_subscr(struct gsm_auth_tuple *atuple, - struct gsm_subscriber *subscr, int key_seq) -{ - struct gsm_auth_info ainfo; - int i, rc; - - /* Get subscriber info (if any) */ - rc = db_get_authinfo_for_subscr(&ainfo, subscr); - if (rc < 0) { - LOGP(DMM, LOGL_NOTICE, - "No retrievable Ki for subscriber, skipping auth\n"); - return rc == -ENOENT ? AUTH_NOT_AVAIL : -1; - } - - /* If possible, re-use the last tuple and skip auth */ - rc = db_get_lastauthtuple_for_subscr(atuple, subscr); - if ((rc == 0) && - (key_seq != GSM_KEY_SEQ_INVAL) && - (atuple->use_count < 3)) - { - atuple->use_count++; - db_sync_lastauthtuple_for_subscr(atuple, subscr); - DEBUGP(DMM, "Auth tuple use < 3, just doing ciphering\n"); - return AUTH_DO_CIPH; - } - - /* Generate a new one */ - atuple->use_count = 1; - atuple->key_seq = (atuple->key_seq + 1) % 7; - for (i=0; irand); i++) - atuple->rand[i] = random() & 0xff; - - switch (ainfo.auth_algo) { - case AUTH_ALGO_NONE: - DEBUGP(DMM, "No authentication for subscriber\n"); - return 0; - - case AUTH_ALGO_XOR: - if (_use_xor(&ainfo, atuple)) - return 0; - break; - - case AUTH_ALGO_COMP128v1: - if (_use_comp128_v1(&ainfo, atuple)) - return 0; - break; - - default: - DEBUGP(DMM, "Unsupported auth type algo_id=%d\n", - ainfo.auth_algo); - return 0; - } - - db_sync_lastauthtuple_for_subscr(atuple, subscr); - - DEBUGP(DMM, "Need to do authentication and ciphering\n"); - return AUTH_DO_AUTH_THAN_CIPH; -} - diff --git a/openbsc/src/msc/db.c b/openbsc/src/msc/db.c deleted file mode 100644 index 95a7d36fb..000000000 --- a/openbsc/src/msc/db.c +++ /dev/null @@ -1,1303 +0,0 @@ -/* Simple HLR/VLR database backend using dbi */ -/* (C) 2008 by Jan Luebbe - * (C) 2009 by Holger Hans Peter Freyther - * (C) 2009 by Harald Welte - * All Rights Reserved - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation; either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - * - */ - -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include - -#include -#include -#include - -static char *db_basename = NULL; -static char *db_dirname = NULL; -static dbi_conn conn; - -static char *create_stmts[] = { - "CREATE TABLE IF NOT EXISTS Meta (" - "id INTEGER PRIMARY KEY AUTOINCREMENT, " - "key TEXT UNIQUE NOT NULL, " - "value TEXT NOT NULL" - ")", - "INSERT OR IGNORE INTO Meta " - "(key, value) " - "VALUES " - "('revision', '2')", - "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" - ")", - "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" - ")", - "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" - ")", - "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) " - ")", - "CREATE TABLE IF NOT EXISTS SMS (" - /* metadata, not part of sms */ - "id INTEGER PRIMARY KEY AUTOINCREMENT, " - "created TIMESTAMP NOT NULL, " - "sent TIMESTAMP, " - "sender_id INTEGER NOT NULL, " - "receiver_id INTEGER NOT NULL, " - "deliver_attempts INTEGER NOT NULL DEFAULT 0, " - /* 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, " - "dest_addr TEXT, " - "user_data BLOB, " /* TP-UD */ - /* additional data, interpreted from SMS */ - "header BLOB, " /* UD Header */ - "text TEXT " /* decoded UD after UDH */ - ")", - "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 " - ")", - "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 " - ")", - "CREATE TABLE IF NOT EXISTS Counters (" - "id INTEGER PRIMARY KEY AUTOINCREMENT, " - "timestamp TIMESTAMP NOT NULL, " - "value INTEGER NOT NULL, " - "name TEXT NOT NULL " - ")", - "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 " - ")", - "CREATE TABLE IF NOT EXISTS AuthKeys (" - "subscriber_id INTEGER PRIMARY KEY, " - "algorithm_id INTEGER NOT NULL, " - "a3a8_ki BLOB " - ")", - "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); -} - -static int check_db_revision(void) -{ - dbi_result result; - const char *rev; - - result = dbi_conn_query(conn, - "SELECT value FROM Meta WHERE key='revision'"); - if (!result) - return -EINVAL; - - if (!dbi_result_next_row(result)) { - dbi_result_free(result); - return -EINVAL; - } - rev = dbi_result_get_string(result, "value"); - if (!rev || atoi(rev) != 2) { - dbi_result_free(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() -{ - 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; - } - - return 0; -} - -int db_fini() -{ - dbi_conn_close(conn); - dbi_shutdown(); - - if (db_dirname) - free(db_dirname); - if (db_basename) - free(db_basename); - return 0; -} - -struct gsm_subscriber *db_create_subscriber(struct gsm_network *net, char *imsi) -{ - dbi_result result; - struct gsm_subscriber *subscr; - - /* Is this subscriber known in the db? */ - subscr = db_get_subscriber(net, GSM_SUBSCRIBER_IMSI, imsi); - if (subscr) { - result = dbi_conn_queryf(conn, - "UPDATE Subscriber set updated = datetime('now') " - "WHERE imsi = %s " , imsi); - if (!result) - LOGP(DDB, LOGL_ERROR, "failed to update timestamp\n"); - else - dbi_result_free(result); - return subscr; - } - - subscr = subscr_alloc(); - subscr->flags |= GSM_SUBSCRIBER_FIRST_CONTACT; - if (!subscr) - return NULL; - 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->net = net; - subscr->id = dbi_conn_sequence_last(conn, NULL); - strncpy(subscr->imsi, imsi, GSM_IMSI_LENGTH-1); - dbi_result_free(result); - LOGP(DDB, LOGL_INFO, "New Subscriber: ID %llu, IMSI %s\n", subscr->id, subscr->imsi); - db_subscriber_alloc_exten(subscr); - return subscr; -} - -static_assert(sizeof(unsigned char) == sizeof(struct gsm48_classmark1), classmark1_size); - -static int get_equipment_by_subscr(struct gsm_subscriber *subscr) -{ - dbi_result result; - const char *string; - unsigned char cm1; - const unsigned char *cm2, *cm3; - struct gsm_equipment *equip = &subscr->equipment; - - result = dbi_conn_queryf(conn, - "SELECT Equipment.* " - "FROM Equipment JOIN EquipmentWatch ON " - "EquipmentWatch.equipment_id=Equipment.id " - "WHERE EquipmentWatch.subscriber_id = %llu " - "ORDER BY EquipmentWatch.updated DESC", subscr->id); - if (!result) - return -EIO; - - if (!dbi_result_next_row(result)) { - dbi_result_free(result); - return -ENOENT; - } - - equip->id = dbi_result_get_ulonglong(result, "id"); - - string = dbi_result_get_string(result, "imei"); - if (string) - strncpy(equip->imei, string, sizeof(equip->imei)); - - string = dbi_result_get_string(result, "classmark1"); - if (string) { - cm1 = atoi(string) & 0xff; - memcpy(&equip->classmark1, &cm1, sizeof(equip->classmark1)); - } - - equip->classmark2_len = dbi_result_get_field_length(result, "classmark2"); - cm2 = dbi_result_get_binary(result, "classmark2"); - if (equip->classmark2_len > sizeof(equip->classmark2)) - equip->classmark2_len = sizeof(equip->classmark2); - memcpy(equip->classmark2, cm2, equip->classmark2_len); - - equip->classmark3_len = dbi_result_get_field_length(result, "classmark3"); - cm3 = dbi_result_get_binary(result, "classmark3"); - if (equip->classmark3_len > sizeof(equip->classmark3)) - equip->classmark3_len = sizeof(equip->classmark3); - memcpy(equip->classmark3, cm3, equip->classmark3_len); - - dbi_result_free(result); - - return 0; -} - -int db_get_authinfo_for_subscr(struct gsm_auth_info *ainfo, - struct gsm_subscriber *subscr) -{ - dbi_result result; - const unsigned char *a3a8_ki; - - result = dbi_conn_queryf(conn, - "SELECT * FROM AuthKeys WHERE subscriber_id=%llu", - subscr->id); - if (!result) - return -EIO; - - if (!dbi_result_next_row(result)) { - dbi_result_free(result); - return -ENOENT; - } - - ainfo->auth_algo = dbi_result_get_ulonglong(result, "algorithm_id"); - ainfo->a3a8_ki_len = dbi_result_get_field_length(result, "a3a8_ki"); - a3a8_ki = dbi_result_get_binary(result, "a3a8_ki"); - if (ainfo->a3a8_ki_len > sizeof(ainfo->a3a8_ki)) - ainfo->a3a8_ki_len = sizeof(ainfo->a3a8_ki_len); - memcpy(ainfo->a3a8_ki, a3a8_ki, ainfo->a3a8_ki_len); - - dbi_result_free(result); - - return 0; -} - -int db_sync_authinfo_for_subscr(struct gsm_auth_info *ainfo, - struct gsm_subscriber *subscr) -{ - dbi_result result; - struct gsm_auth_info ainfo_old; - int rc, upd; - unsigned char *ki_str; - - /* Deletion ? */ - if (ainfo == NULL) { - result = dbi_conn_queryf(conn, - "DELETE FROM AuthKeys WHERE subscriber_id=%llu", - subscr->id); - - if (!result) - return -EIO; - - dbi_result_free(result); - - return 0; - } - - /* Check if already existing */ - rc = db_get_authinfo_for_subscr(&ainfo_old, subscr); - if (rc && rc != -ENOENT) - return rc; - upd = rc ? 0 : 1; - - /* Update / Insert */ - dbi_conn_quote_binary_copy(conn, - ainfo->a3a8_ki, ainfo->a3a8_ki_len, &ki_str); - - if (!upd) { - result = dbi_conn_queryf(conn, - "INSERT INTO AuthKeys " - "(subscriber_id, algorithm_id, a3a8_ki) " - "VALUES (%llu, %u, %s)", - subscr->id, ainfo->auth_algo, ki_str); - } else { - result = dbi_conn_queryf(conn, - "UPDATE AuthKeys " - "SET algorithm_id=%u, a3a8_ki=%s " - "WHERE subscriber_id=%llu", - ainfo->auth_algo, ki_str, subscr->id); - } - - free(ki_str); - - if (!result) - return -EIO; - - dbi_result_free(result); - - return 0; -} - -int db_get_lastauthtuple_for_subscr(struct gsm_auth_tuple *atuple, - struct gsm_subscriber *subscr) -{ - dbi_result result; - int len; - const unsigned char *blob; - - result = dbi_conn_queryf(conn, - "SELECT * FROM AuthLastTuples WHERE subscriber_id=%llu", - subscr->id); - if (!result) - return -EIO; - - if (!dbi_result_next_row(result)) { - dbi_result_free(result); - return -ENOENT; - } - - memset(atuple, 0, sizeof(atuple)); - - atuple->use_count = dbi_result_get_ulonglong(result, "use_count"); - atuple->key_seq = dbi_result_get_ulonglong(result, "key_seq"); - - len = dbi_result_get_field_length(result, "rand"); - if (len != sizeof(atuple->rand)) - goto err_size; - - blob = dbi_result_get_binary(result, "rand"); - memcpy(atuple->rand, blob, len); - - len = dbi_result_get_field_length(result, "sres"); - if (len != sizeof(atuple->sres)) - goto err_size; - - blob = dbi_result_get_binary(result, "sres"); - memcpy(atuple->sres, blob, len); - - len = dbi_result_get_field_length(result, "kc"); - if (len != sizeof(atuple->kc)) - goto err_size; - - blob = dbi_result_get_binary(result, "kc"); - memcpy(atuple->kc, blob, len); - - dbi_result_free(result); - - return 0; - -err_size: - dbi_result_free(result); - return -EIO; -} - -int db_sync_lastauthtuple_for_subscr(struct gsm_auth_tuple *atuple, - struct gsm_subscriber *subscr) -{ - dbi_result result; - int rc, upd; - struct gsm_auth_tuple atuple_old; - unsigned char *rand_str, *sres_str, *kc_str; - - /* Deletion ? */ - if (atuple == NULL) { - result = dbi_conn_queryf(conn, - "DELETE FROM AuthLastTuples WHERE subscriber_id=%llu", - subscr->id); - - if (!result) - return -EIO; - - dbi_result_free(result); - - return 0; - } - - /* Check if already existing */ - rc = db_get_lastauthtuple_for_subscr(&atuple_old, subscr); - if (rc && rc != -ENOENT) - return rc; - upd = rc ? 0 : 1; - - /* Update / Insert */ - dbi_conn_quote_binary_copy(conn, - atuple->rand, sizeof(atuple->rand), &rand_str); - dbi_conn_quote_binary_copy(conn, - atuple->sres, sizeof(atuple->sres), &sres_str); - dbi_conn_quote_binary_copy(conn, - atuple->kc, sizeof(atuple->kc), &kc_str); - - if (!upd) { - result = dbi_conn_queryf(conn, - "INSERT INTO AuthLastTuples " - "(subscriber_id, issued, use_count, " - "key_seq, rand, sres, kc) " - "VALUES (%llu, datetime('now'), %u, " - "%u, %s, %s, %s ) ", - subscr->id, atuple->use_count, atuple->key_seq, - rand_str, sres_str, kc_str); - } else { - char *issued = atuple->key_seq == atuple_old.key_seq ? - "issued" : "datetime('now')"; - result = dbi_conn_queryf(conn, - "UPDATE AuthLastTuples " - "SET issued=%s, use_count=%u, " - "key_seq=%u, rand=%s, sres=%s, kc=%s " - "WHERE subscriber_id = %llu", - issued, atuple->use_count, atuple->key_seq, - rand_str, sres_str, kc_str, subscr->id); - } - - free(rand_str); - free(sres_str); - free(kc_str); - - if (!result) - return -EIO; - - dbi_result_free(result); - - return 0; -} - -static void db_set_from_query(struct gsm_subscriber *subscr, dbi_conn result) -{ - const char *string; - string = dbi_result_get_string(result, "imsi"); - if (string) - strncpy(subscr->imsi, string, GSM_IMSI_LENGTH); - - string = dbi_result_get_string(result, "tmsi"); - if (string) - subscr->tmsi = tmsi_from_string(string); - - string = dbi_result_get_string(result, "name"); - if (string) - strncpy(subscr->name, string, GSM_NAME_LENGTH); - - string = dbi_result_get_string(result, "extension"); - if (string) - strncpy(subscr->extension, string, GSM_EXTENSION_LENGTH); - - subscr->lac = dbi_result_get_uint(result, "lac"); - subscr->authorized = dbi_result_get_uint(result, "authorized"); -} - -#define BASE_QUERY "SELECT * FROM Subscriber " -struct gsm_subscriber *db_get_subscriber(struct gsm_network *net, - enum gsm_subscriber_field field, - const char *id) -{ - dbi_result result; - char *quoted; - struct gsm_subscriber *subscr; - - switch (field) { - case GSM_SUBSCRIBER_IMSI: - dbi_conn_quote_string_copy(conn, id, "ed); - result = dbi_conn_queryf(conn, - BASE_QUERY - "WHERE imsi = %s ", - quoted - ); - free(quoted); - break; - case GSM_SUBSCRIBER_TMSI: - dbi_conn_quote_string_copy(conn, id, "ed); - result = dbi_conn_queryf(conn, - BASE_QUERY - "WHERE tmsi = %s ", - quoted - ); - free(quoted); - break; - case GSM_SUBSCRIBER_EXTENSION: - dbi_conn_quote_string_copy(conn, id, "ed); - result = dbi_conn_queryf(conn, - BASE_QUERY - "WHERE extension = %s ", - quoted - ); - free(quoted); - break; - case GSM_SUBSCRIBER_ID: - dbi_conn_quote_string_copy(conn, id, "ed); - result = dbi_conn_queryf(conn, - BASE_QUERY - "WHERE id = %s ", quoted); - free(quoted); - break; - default: - LOGP(DDB, LOGL_NOTICE, "Unknown query selector for Subscriber.\n"); - return NULL; - } - if (!result) { - LOGP(DDB, LOGL_ERROR, "Failed to query Subscriber.\n"); - return NULL; - } - if (!dbi_result_next_row(result)) { - DEBUGP(DDB, "Failed to find the Subscriber. '%u' '%s'\n", - field, id); - dbi_result_free(result); - return NULL; - } - - subscr = subscr_alloc(); - subscr->net = net; - subscr->id = dbi_result_get_ulonglong(result, "id"); - - db_set_from_query(subscr, result); - DEBUGP(DDB, "Found Subscriber: ID %llu, IMSI %s, NAME '%s', TMSI %u, EXTEN '%s', LAC %hu, AUTH %u\n", - subscr->id, subscr->imsi, subscr->name, subscr->tmsi, subscr->extension, - subscr->lac, subscr->authorized); - dbi_result_free(result); - - get_equipment_by_subscr(subscr); - - return subscr; -} - -int db_subscriber_update(struct gsm_subscriber *subscr) -{ - char buf[32]; - dbi_result result; - - /* Copy the id to a string as queryf with %llu is failing */ - sprintf(buf, "%llu", subscr->id); - result = dbi_conn_queryf(conn, - BASE_QUERY - "WHERE id = %s", buf); - - if (!result) { - LOGP(DDB, LOGL_ERROR, "Failed to query Subscriber: %llu\n", subscr->id); - return -EIO; - } - if (!dbi_result_next_row(result)) { - DEBUGP(DDB, "Failed to find the Subscriber. %llu\n", - subscr->id); - dbi_result_free(result); - return -EIO; - } - - db_set_from_query(subscr, result); - dbi_result_free(result); - get_equipment_by_subscr(subscr); - - return 0; -} - -int db_sync_subscriber(struct gsm_subscriber *subscriber) -{ - dbi_result result; - char tmsi[14]; - char *q_tmsi, *q_name, *q_extension; - - dbi_conn_quote_string_copy(conn, - subscriber->name, &q_name); - dbi_conn_quote_string_copy(conn, - subscriber->extension, &q_extension); - - if (subscriber->tmsi != GSM_RESERVED_TMSI) { - sprintf(tmsi, "%u", subscriber->tmsi); - dbi_conn_quote_string_copy(conn, - tmsi, - &q_tmsi); - } else - q_tmsi = strdup("NULL"); - - result = dbi_conn_queryf(conn, - "UPDATE Subscriber " - "SET updated = datetime('now'), " - "name = %s, " - "extension = %s, " - "authorized = %i, " - "tmsi = %s, " - "lac = %i " - "WHERE imsi = %s ", - q_name, - q_extension, - subscriber->authorized, - q_tmsi, - subscriber->lac, - 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_sync_equipment(struct gsm_equipment *equip) -{ - dbi_result result; - unsigned char *cm2, *cm3; - char *q_imei; - u_int8_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", - hexdump(equip->classmark2, equip->classmark2_len)); - if (equip->classmark3_len) - DEBUGPC(DDB, ", classmark3=%s", - 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_alloc_tmsi(struct gsm_subscriber *subscriber) -{ - dbi_result result = NULL; - char tmsi[14]; - char *tmsi_quoted; - - for (;;) { - subscriber->tmsi = rand(); - if (subscriber->tmsi == GSM_RESERVED_TMSI) - continue; - - sprintf(tmsi, "%u", subscriber->tmsi); - dbi_conn_quote_string_copy(conn, tmsi, &tmsi_quoted); - result = dbi_conn_queryf(conn, - "SELECT * FROM Subscriber " - "WHERE tmsi = %s ", - tmsi_quoted); - - free(tmsi_quoted); - - if (!result) { - LOGP(DDB, LOGL_ERROR, "Failed to query Subscriber " - "while allocating new TMSI.\n"); - return 1; - } - if (dbi_result_get_numrows(result)) { - dbi_result_free(result); - continue; - } - if (!dbi_result_next_row(result)) { - dbi_result_free(result); - DEBUGP(DDB, "Allocated TMSI %u for IMSI %s.\n", - subscriber->tmsi, subscriber->imsi); - return db_sync_subscriber(subscriber); - } - dbi_result_free(result); - } - return 0; -} - -int db_subscriber_alloc_exten(struct gsm_subscriber *subscriber) -{ - dbi_result result = NULL; - u_int32_t try; - - for (;;) { - try = (rand()%(GSM_MAX_EXTEN-GSM_MIN_EXTEN+1)+GSM_MIN_EXTEN); - result = dbi_conn_queryf(conn, - "SELECT * FROM Subscriber " - "WHERE extension = %i", - try - ); - if (!result) { - LOGP(DDB, LOGL_ERROR, "Failed to query Subscriber " - "while allocating new extension.\n"); - return 1; - } - if (dbi_result_get_numrows(result)){ - dbi_result_free(result); - continue; - } - if (!dbi_result_next_row(result)) { - dbi_result_free(result); - break; - } - dbi_result_free(result); - } - sprintf(subscriber->extension, "%i", try); - DEBUGP(DDB, "Allocated extension %i for IMSI %s.\n", try, subscriber->imsi); - return db_sync_subscriber(subscriber); -} -/* - * try to allocate a new unique token for this subscriber and return it - * via a parameter. if the subscriber already has a token, return - * an error. - */ - -int db_subscriber_alloc_token(struct gsm_subscriber *subscriber, u_int32_t *token) -{ - dbi_result result; - u_int32_t try; - - for (;;) { - try = rand(); - if (!try) /* 0 is an invalid token */ - continue; - result = dbi_conn_queryf(conn, - "SELECT * FROM AuthToken " - "WHERE subscriber_id = %llu OR token = \"%08X\" ", - subscriber->id, try); - if (!result) { - LOGP(DDB, LOGL_ERROR, "Failed to query AuthToken " - "while allocating new token.\n"); - return 1; - } - if (dbi_result_get_numrows(result)) { - dbi_result_free(result); - continue; - } - if (!dbi_result_next_row(result)) { - dbi_result_free(result); - break; - } - dbi_result_free(result); - } - result = dbi_conn_queryf(conn, - "INSERT INTO AuthToken " - "(subscriber_id, created, token) " - "VALUES " - "(%llu, datetime('now'), \"%08X\") ", - subscriber->id, try); - if (!result) { - LOGP(DDB, LOGL_ERROR, "Failed to create token %08X for " - "IMSI %s.\n", try, subscriber->imsi); - return 1; - } - dbi_result_free(result); - *token = try; - DEBUGP(DDB, "Allocated token %08X for IMSI %s.\n", try, subscriber->imsi); - - return 0; -} - -int db_subscriber_assoc_imei(struct gsm_subscriber *subscriber, char imei[GSM_IMEI_LENGTH]) -{ - unsigned long long equipment_id, watch_id; - dbi_result result; - - strncpy(subscriber->equipment.imei, imei, - sizeof(subscriber->equipment.imei)-1), - - result = dbi_conn_queryf(conn, - "INSERT OR IGNORE INTO Equipment " - "(imei, created, updated) " - "VALUES " - "(%s, datetime('now'), datetime('now')) ", - imei); - if (!result) { - LOGP(DDB, LOGL_ERROR, "Failed to create Equipment by IMEI.\n"); - return 1; - } - - equipment_id = 0; - if (dbi_result_get_numrows_affected(result)) { - equipment_id = dbi_conn_sequence_last(conn, NULL); - } - dbi_result_free(result); - - if (equipment_id) - DEBUGP(DDB, "New Equipment: ID %llu, IMEI %s\n", equipment_id, imei); - else { - result = dbi_conn_queryf(conn, - "SELECT id FROM Equipment " - "WHERE imei = %s ", - imei - ); - if (!result) { - LOGP(DDB, LOGL_ERROR, "Failed to query Equipment by IMEI.\n"); - return 1; - } - if (!dbi_result_next_row(result)) { - LOGP(DDB, LOGL_ERROR, "Failed to find the Equipment.\n"); - dbi_result_free(result); - return 1; - } - equipment_id = dbi_result_get_ulonglong(result, "id"); - dbi_result_free(result); - } - - result = dbi_conn_queryf(conn, - "INSERT OR IGNORE INTO EquipmentWatch " - "(subscriber_id, equipment_id, created, updated) " - "VALUES " - "(%llu, %llu, datetime('now'), datetime('now')) ", - subscriber->id, equipment_id); - if (!result) { - LOGP(DDB, LOGL_ERROR, "Failed to create EquipmentWatch.\n"); - return 1; - } - - watch_id = 0; - if (dbi_result_get_numrows_affected(result)) - watch_id = dbi_conn_sequence_last(conn, NULL); - - dbi_result_free(result); - if (watch_id) - DEBUGP(DDB, "New EquipmentWatch: ID %llu, IMSI %s, IMEI %s\n", - equipment_id, subscriber->imsi, imei); - else { - result = dbi_conn_queryf(conn, - "UPDATE EquipmentWatch " - "SET updated = datetime('now') " - "WHERE subscriber_id = %llu AND equipment_id = %llu ", - subscriber->id, equipment_id); - if (!result) { - LOGP(DDB, LOGL_ERROR, "Failed to update EquipmentWatch.\n"); - return 1; - } - dbi_result_free(result); - DEBUGP(DDB, "Updated EquipmentWatch: ID %llu, IMSI %s, IMEI %s\n", - equipment_id, subscriber->imsi, imei); - } - - return 0; -} - -/* store an [unsent] SMS to the database */ -int db_sms_store(struct gsm_sms *sms) -{ - dbi_result result; - char *q_text, *q_daddr; - 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->dest_addr, &q_daddr); - 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, sender_id, receiver_id, valid_until, " - "reply_path_req, status_rep_req, protocol_id, " - "data_coding_scheme, ud_hdr_ind, dest_addr, " - "user_data, text) VALUES " - "(datetime('now'), %llu, %llu, %u, " - "%u, %u, %u, %u, %u, %s, %s, %s)", - sms->sender->id, - sms->receiver ? sms->receiver->id : 0, validity_timestamp, - sms->reply_path_req, sms->status_rep_req, sms->protocol_id, - sms->data_coding_scheme, sms->ud_hdr_ind, - q_daddr, q_udata, q_text); - free(q_text); - free(q_daddr); - free(q_udata); - - 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(); - long long unsigned int sender_id, receiver_id; - const char *text, *daddr; - const unsigned char *user_data; - - if (!sms) - return NULL; - - sms->id = dbi_result_get_ulonglong(result, "id"); - - sender_id = dbi_result_get_ulonglong(result, "sender_id"); - sms->sender = subscr_get_by_id(net, sender_id); - - receiver_id = dbi_result_get_ulonglong(result, "receiver_id"); - sms->receiver = subscr_get_by_id(net, receiver_id); - - /* FIXME: validity */ - /* FIXME: those should all be get_uchar, but sqlite3 is braindead */ - sms->reply_path_req = dbi_result_get_uint(result, "reply_path_req"); - sms->status_rep_req = dbi_result_get_uint(result, "status_rep_req"); - sms->ud_hdr_ind = dbi_result_get_uint(result, "ud_hdr_ind"); - sms->protocol_id = dbi_result_get_uint(result, "protocol_id"); - sms->data_coding_scheme = dbi_result_get_uint(result, - "data_coding_scheme"); - /* sms->msg_ref is temporary and not stored in DB */ - - daddr = dbi_result_get_string(result, "dest_addr"); - if (daddr) { - strncpy(sms->dest_addr, daddr, sizeof(sms->dest_addr)); - sms->dest_addr[sizeof(sms->dest_addr)-1] = '\0'; - } - - sms->user_data_len = dbi_result_get_field_length(result, "user_data"); - user_data = dbi_result_get_binary(result, "user_data"); - if (sms->user_data_len > sizeof(sms->user_data)) - sms->user_data_len = (u_int8_t) sizeof(sms->user_data); - memcpy(sms->user_data, user_data, sms->user_data_len); - - text = dbi_result_get_string(result, "text"); - if (text) { - strncpy(sms->text, text, sizeof(sms->text)); - sms->text[sizeof(sms->text)-1] = '\0'; - } - return sms; -} - -struct gsm_sms *db_sms_get(struct gsm_network *net, unsigned long long id) -{ - dbi_result result; - struct gsm_sms *sms; - - result = dbi_conn_queryf(conn, - "SELECT * FROM SMS WHERE SMS.id = %llu", id); - if (!result) - return NULL; - - if (!dbi_result_next_row(result)) { - dbi_result_free(result); - return NULL; - } - - sms = sms_from_result(net, result); - - dbi_result_free(result); - - return sms; -} - -/* retrieve the next unsent SMS with ID >= min_id */ -struct gsm_sms *db_sms_get_unsent(struct gsm_network *net, unsigned long long min_id) -{ - dbi_result result; - struct gsm_sms *sms; - - result = dbi_conn_queryf(conn, - "SELECT SMS.* " - "FROM SMS JOIN Subscriber ON " - "SMS.receiver_id = Subscriber.id " - "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.receiver_id = Subscriber.id " - "WHERE SMS.receiver_id >= %llu AND SMS.sent IS NULL " - "AND Subscriber.lac > 0 AND SMS.deliver_attempts < %u " - "ORDER BY SMS.receiver_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.receiver_id = Subscriber.id " - "WHERE SMS.receiver_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->net, result); - - dbi_result_free(result); - - return sms; -} - -/* mark a given SMS as read */ -int db_sms_mark_sent(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, - u_int8_t apdu_id_flags, u_int8_t len, - u_int8_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 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/msc/gsm_04_08.c b/openbsc/src/msc/gsm_04_08.c deleted file mode 100644 index 2b61aa9b0..000000000 --- a/openbsc/src/msc/gsm_04_08.c +++ /dev/null @@ -1,3345 +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 - * (C) 2008-2010 by Holger Hans Peter Freyther - * - * All Rights Reserved - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation; either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - * - */ - - -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include - -void *tall_locop_ctx; -void *tall_authciphop_ctx; - -int gsm0408_loc_upd_acc(struct gsm_subscriber_connection *conn, u_int32_t tmsi); -static int gsm48_tx_simple(struct gsm_subscriber_connection *conn, - u_int8_t pdisc, u_int8_t msg_type); -static void schedule_reject(struct gsm_subscriber_connection *conn); -static void release_anchor(struct gsm_subscriber_connection *conn); - -struct gsm_lai { - u_int16_t mcc; - u_int16_t mnc; - u_int16_t lac; -}; - -static u_int32_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) { - msg->trx = msg->lchan->ts->trx; - if ((gh->proto_discr & GSM48_PDISC_MASK) == GSM48_PDISC_CC) - DEBUGP(DCC, "(bts %d trx %d ts %d ti %02x) " - "Sending '%s' to MS.\n", msg->trx->bts->nr, - msg->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", msg->trx->bts->nr, - msg->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); -} - -static 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); -} - -static void allocate_security_operation(struct gsm_subscriber_connection *conn) -{ - conn->sec_operation = talloc_zero(tall_authciphop_ctx, - struct gsm_security_operation); -} - -int gsm48_secure_channel(struct gsm_subscriber_connection *conn, int key_seq, - gsm_cbfn *cb, void *cb_data) -{ - struct gsm_network *net = conn->bts->network; - struct gsm_subscriber *subscr = conn->subscr; - struct gsm_security_operation *op; - struct gsm_auth_tuple atuple; - int status = -1, rc; - - /* Check if we _can_ enable encryption. Cases where we can't: - * - Encryption disabled in config - * - Channel already secured (nothing to do) - * - Subscriber equipment doesn't support configured encryption - */ - if (!net->a5_encryption) { - status = GSM_SECURITY_NOAVAIL; - } else if (conn->lchan->encr.alg_id > RSL_ENC_ALG_A5(0)) { - DEBUGP(DMM, "Requesting to secure an already secure channel"); - status = GSM_SECURITY_SUCCEEDED; - } else if (!ms_cm2_a5n_support(subscr->equipment.classmark2, - net->a5_encryption)) { - DEBUGP(DMM, "Subscriber equipment doesn't support requested encryption"); - status = GSM_SECURITY_NOAVAIL; - } - - /* If not done yet, try to get info for this user */ - if (status < 0) { - rc = auth_get_tuple_for_subscr(&atuple, subscr, key_seq); - if (rc <= 0) - status = GSM_SECURITY_NOAVAIL; - } - - /* Are we done yet ? */ - if (status >= 0) - return cb ? - cb(GSM_HOOK_RR_SECURITY, status, NULL, conn, cb_data) : - 0; - - /* Start an operation (can't have more than one pending !!!) */ - if (conn->sec_operation) - return -EBUSY; - - allocate_security_operation(conn); - op = conn->sec_operation; - op->cb = cb; - op->cb_data = cb_data; - memcpy(&op->atuple, &atuple, sizeof(struct gsm_auth_tuple)); - - /* FIXME: Should start a timer for completion ... */ - - /* Then do whatever is needed ... */ - if (rc == AUTH_DO_AUTH_THAN_CIPH) { - /* Start authentication */ - return gsm48_tx_mm_auth_req(conn, op->atuple.rand, op->atuple.key_seq); - } else if (rc == AUTH_DO_CIPH) { - /* Start ciphering directly */ - return gsm0808_cipher_mode(conn, net->a5_encryption, - op->atuple.kc, 8, 0); - } - - return -EINVAL; /* not reached */ -} - -static int authorize_subscriber(struct gsm_loc_updating_operation *loc, - struct gsm_subscriber *subscriber) -{ - if (!subscriber) - return 0; - - /* - * Do not send accept yet as more information should arrive. Some - * phones will not send us the information and we will have to check - * what we want to do with that. - */ - if (loc && (loc->waiting_for_imsi || loc->waiting_for_imei)) - return 0; - - switch (subscriber->net->auth_policy) { - case GSM_AUTH_POLICY_CLOSED: - return subscriber->authorized; - case GSM_AUTH_POLICY_TOKEN: - if (subscriber->authorized) - return subscriber->authorized; - return (subscriber->flags & GSM_SUBSCRIBER_FIRST_CONTACT); - case GSM_AUTH_POLICY_ACCEPT_ALL: - return 1; - default: - return 0; - } -} - -static void release_loc_updating_req(struct gsm_subscriber_connection *conn) -{ - if (!conn->loc_operation) - return; - - /* No need to keep the connection up */ - release_anchor(conn); - - bsc_del_timer(&conn->loc_operation->updating_timer); - talloc_free(conn->loc_operation); - conn->loc_operation = NULL; - msc_release_connection(conn); -} - -static void allocate_loc_updating_req(struct gsm_subscriber_connection *conn) -{ - if (conn->loc_operation) - LOGP(DMM, LOGL_ERROR, "Connection already had operation.\n"); - release_loc_updating_req(conn); - - conn->loc_operation = talloc_zero(tall_locop_ctx, - struct gsm_loc_updating_operation); -} - -static int _gsm0408_authorize_sec_cb(unsigned int hooknum, unsigned int event, - struct msgb *msg, void *data, void *param) -{ - struct gsm_subscriber_connection *conn = data; - int rc = 0; - - switch (event) { - case GSM_SECURITY_AUTH_FAILED: - release_loc_updating_req(conn); - break; - - case GSM_SECURITY_NOAVAIL: - case GSM_SECURITY_SUCCEEDED: - /* We're all good */ - db_subscriber_alloc_tmsi(conn->subscr); - rc = gsm0408_loc_upd_acc(conn, conn->subscr->tmsi); - if (conn->bts->network->send_mm_info) { - /* send MM INFO with network name */ - rc = gsm48_tx_mm_info(conn); - } - - /* call subscr_update after putting the loc_upd_acc - * in the transmit queue, since S_SUBSCR_ATTACHED might - * trigger further action like SMS delivery */ - subscr_update(conn->subscr, conn->bts, - GSM_SUBSCRIBER_UPDATE_ATTACHED); - - /* - * The gsm0408_loc_upd_acc sends a MI with the TMSI. The - * MS needs to respond with a TMSI REALLOCATION COMPLETE - * (even if the TMSI is the same). - */ - break; - - default: - rc = -EINVAL; - }; - - return rc; -} - -static int gsm0408_authorize(struct gsm_subscriber_connection *conn, struct msgb *msg) -{ - if (authorize_subscriber(conn->loc_operation, conn->subscr)) - return gsm48_secure_channel(conn, - conn->loc_operation->key_seq, - _gsm0408_authorize_sec_cb, NULL); - return 0; -} - -void gsm0408_clear_request(struct gsm_subscriber_connection *conn, uint32_t cause) -{ - struct gsm_trans *trans, *temp; - - /* avoid someone issuing a clear */ - conn->in_release = 1; - - /* - * Cancel any outstanding location updating request - * operation taking place on the subscriber connection. - */ - release_loc_updating_req(conn); - - /* 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 lchan */ - /* FIXME: this is not neccessarily the right thing to do, we should - * only set trans->lchan to NULL and wait for another lchan to be - * established to the same MM entity (phone/subscriber) */ - llist_for_each_entry_safe(trans, temp, &conn->bts->network->trans_list, entry) { - if (trans->conn == conn) - trans_free(trans); - } -} - -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, u_int8_t cause) -{ - struct gsm_bts *bts = conn->bts; - struct msgb *msg; - - counter_inc(bts->network->stats.loc_upd_resp.reject); - - msg = gsm48_create_loc_upd_rej(cause); - if (!msg) { - LOGP(DMM, LOGL_ERROR, "Failed to create msg for LOCATION UPDATING REJECT.\n"); - return -1; - } - - msg->lchan = conn->lchan; - - LOGP(DMM, LOGL_INFO, "Subscriber %s: LOCATION UPDATING REJECT " - "LAC=%u BTS=%u\n", conn->subscr ? - subscr_name(conn->subscr) : "unknown", - bts->location_area_code, bts->nr); - - return gsm48_conn_sendmsg(msg, conn, NULL); -} - -/* Chapter 9.2.13 : Send LOCATION UPDATE ACCEPT */ -int gsm0408_loc_upd_acc(struct gsm_subscriber_connection *conn, u_int32_t tmsi) -{ - struct gsm_bts *bts = conn->bts; - struct msgb *msg = gsm48_msgb_alloc(); - struct gsm48_hdr *gh; - struct gsm48_loc_area_id *lai; - u_int8_t *mid; - - msg->lchan = conn->lchan; - - gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh)); - gh->proto_discr = GSM48_PDISC_MM; - gh->msg_type = GSM48_MT_MM_LOC_UPD_ACCEPT; - - lai = (struct gsm48_loc_area_id *) msgb_put(msg, sizeof(*lai)); - gsm48_generate_lai(lai, bts->network->country_code, - bts->network->network_code, bts->location_area_code); - - mid = msgb_put(msg, GSM48_MID_TMSI_LEN); - gsm48_generate_mid_from_tmsi(mid, tmsi); - - DEBUGP(DMM, "-> LOCATION UPDATE ACCEPT\n"); - - counter_inc(bts->network->stats.loc_upd_resp.accept); - - return gsm48_conn_sendmsg(msg, conn, NULL); -} - -/* Transmit Chapter 9.2.10 Identity Request */ -static int mm_tx_identity_req(struct gsm_subscriber_connection *conn, u_int8_t id_type) -{ - struct msgb *msg = gsm48_msgb_alloc(); - struct gsm48_hdr *gh; - - msg->lchan = conn->lchan; - - gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh) + 1); - gh->proto_discr = GSM48_PDISC_MM; - gh->msg_type = GSM48_MT_MM_ID_REQ; - gh->data[0] = id_type; - - return gsm48_conn_sendmsg(msg, conn, NULL); -} - - -/* Parse Chapter 9.2.11 Identity Response */ -static int mm_rx_id_resp(struct gsm_subscriber_connection *conn, struct msgb *msg) -{ - struct gsm48_hdr *gh = msgb_l3(msg); - struct gsm_lchan *lchan = msg->lchan; - struct gsm_bts *bts = lchan->ts->trx->bts; - struct gsm_network *net = bts->network; - u_int8_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_type=0x%02x MI(%s)\n", - mi_type, mi_string); - - dispatch_signal(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, mi_string); - if (!conn->subscr) - conn->subscr = db_create_subscriber(net, mi_string); - } - if (conn->loc_operation) - conn->loc_operation->waiting_for_imsi = 0; - break; - case GSM_MI_TYPE_IMEI: - case GSM_MI_TYPE_IMEISV: - /* update subscribe <-> IMEI mapping */ - if (conn->subscr) { - db_subscriber_assoc_imei(conn->subscr, mi_string); - db_sync_equipment(&conn->subscr->equipment); - } - if (conn->loc_operation) - conn->loc_operation->waiting_for_imei = 0; - break; - } - - /* Check if we can let the mobile station enter */ - return gsm0408_authorize(conn, msg); -} - - -static void loc_upd_rej_cb(void *data) -{ - struct gsm_subscriber_connection *conn = data; - struct gsm_lchan *lchan = conn->lchan; - struct gsm_bts *bts = lchan->ts->trx->bts; - - gsm0408_loc_upd_rej(conn, bts->network->reject_cause); - release_loc_updating_req(conn); -} - -static void schedule_reject(struct gsm_subscriber_connection *conn) -{ - conn->loc_operation->updating_timer.cb = loc_upd_rej_cb; - conn->loc_operation->updating_timer.data = conn; - bsc_schedule_timer(&conn->loc_operation->updating_timer, 5, 0); -} - -static const char *lupd_name(u_int8_t type) -{ - switch (type) { - case GSM48_LUPD_NORMAL: - return "NORMAL"; - case GSM48_LUPD_PERIODIC: - return "PEROIDOC"; - case GSM48_LUPD_IMSI_ATT: - return "IMSI ATTACH"; - default: - return "UNKNOWN"; - } -} - -/* Chapter 9.2.15: Receive Location Updating Request */ -static int mm_rx_loc_upd_req(struct gsm_subscriber_connection *conn, struct msgb *msg) -{ - struct gsm48_hdr *gh = msgb_l3(msg); - struct gsm48_loc_upd_req *lu; - struct gsm_subscriber *subscr = NULL; - struct gsm_bts *bts = conn->bts; - u_int8_t mi_type; - char mi_string[GSM48_MI_SIZE]; - int rc; - - lu = (struct gsm48_loc_upd_req *) gh->data; - - mi_type = lu->mi[0] & GSM_MI_TYPE_MASK; - - gsm48_mi_to_string(mi_string, sizeof(mi_string), lu->mi, lu->mi_len); - - DEBUGPC(DMM, "mi_type=0x%02x MI(%s) type=%s ", mi_type, mi_string, - lupd_name(lu->type)); - - dispatch_signal(SS_SUBSCR, S_SUBSCR_IDENTITY, &lu->mi_len); - - switch (lu->type) { - case GSM48_LUPD_NORMAL: - counter_inc(bts->network->stats.loc_upd_type.normal); - break; - case GSM48_LUPD_IMSI_ATT: - counter_inc(bts->network->stats.loc_upd_type.attach); - break; - case GSM48_LUPD_PERIODIC: - counter_inc(bts->network->stats.loc_upd_type.periodic); - break; - } - - /* - * Pseudo Spoof detection: Just drop a second/concurrent - * location updating request. - */ - if (conn->loc_operation) { - DEBUGPC(DMM, "ignoring request due an existing one: %p.\n", - conn->loc_operation); - gsm0408_loc_upd_rej(conn, GSM48_REJECT_PROTOCOL_ERROR); - return 0; - } - - allocate_loc_updating_req(conn); - - conn->loc_operation->key_seq = lu->key_seq; - - switch (mi_type) { - case GSM_MI_TYPE_IMSI: - DEBUGPC(DMM, "\n"); - /* we always want the IMEI, too */ - rc = mm_tx_identity_req(conn, GSM_MI_TYPE_IMEI); - conn->loc_operation->waiting_for_imei = 1; - - /* look up subscriber based on IMSI, create if not found */ - subscr = subscr_get_by_imsi(bts->network, mi_string); - if (!subscr) { - subscr = db_create_subscriber(bts->network, mi_string); - } - break; - case GSM_MI_TYPE_TMSI: - DEBUGPC(DMM, "\n"); - /* look up the subscriber based on TMSI, request IMSI if it fails */ - subscr = subscr_get_by_tmsi(bts->network, - tmsi_from_string(mi_string)); - if (!subscr) { - /* send IDENTITY REQUEST message to get IMSI */ - rc = mm_tx_identity_req(conn, GSM_MI_TYPE_IMSI); - conn->loc_operation->waiting_for_imsi = 1; - } - /* we always want the IMEI, too */ - rc = 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); -} - -#if 0 -static u_int8_t to_bcd8(u_int8_t val) -{ - return ((val / 10) << 4) | (val % 10); -} -#endif - -/* Section 9.2.15a */ -int gsm48_tx_mm_info(struct gsm_subscriber_connection *conn) -{ - struct msgb *msg = gsm48_msgb_alloc(); - struct gsm48_hdr *gh; - struct gsm_network *net = conn->bts->network; - u_int8_t *ptr8; - int name_len, name_pad; -#if 0 - time_t cur_t; - struct tm* cur_time; - int tz15min; -#endif - - 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 = (u_int16_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(ptr8, net->name_long); - - } - - if (net->name_short) { -#if 0 - name_len = strlen(net->name_short); - /* 10.5.3.5a */ - ptr8 = (u_int8_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 = (u_int16_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 = (u_int8_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(ptr8, net->name_short); - - } - -#if 0 - /* Section 10.5.3.9 */ - cur_t = time(NULL); - cur_time = gmtime(&cur_t); - ptr8 = msgb_put(msg, 8); - ptr8[0] = GSM48_IE_NET_TIME_TZ; - ptr8[1] = to_bcd8(cur_time->tm_year % 100); - ptr8[2] = to_bcd8(cur_time->tm_mon); - ptr8[3] = to_bcd8(cur_time->tm_mday); - ptr8[4] = to_bcd8(cur_time->tm_hour); - ptr8[5] = to_bcd8(cur_time->tm_min); - ptr8[6] = to_bcd8(cur_time->tm_sec); - /* 02.42: coded as BCD encoded signed value in units of 15 minutes */ - tz15min = (cur_time->tm_gmtoff)/(60*15); - ptr8[7] = to_bcd8(tz15min); - if (tz15min < 0) - ptr8[7] |= 0x80; -#endif - - DEBUGP(DMM, "-> MM INFO\n"); - - return gsm48_conn_sendmsg(msg, conn, NULL); -} - -/* Section 9.2.2 */ -int gsm48_tx_mm_auth_req(struct gsm_subscriber_connection *conn, u_int8_t *rand, int key_seq) -{ - struct msgb *msg = gsm48_msgb_alloc(); - struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh)); - struct gsm48_auth_req *ar = (struct gsm48_auth_req *) msgb_put(msg, sizeof(*ar)); - - DEBUGP(DMM, "-> AUTH REQ (rand = %s)\n", hexdump(rand, 16)); - - msg->lchan = conn->lchan; - gh->proto_discr = GSM48_PDISC_MM; - gh->msg_type = GSM48_MT_MM_AUTH_REQ; - - ar->key_seq = key_seq; - - /* 16 bytes RAND parameters */ - if (rand) - memcpy(ar->rand, rand, 16); - - return gsm48_conn_sendmsg(msg, conn, NULL); -} - -/* Section 9.2.1 */ -int gsm48_tx_mm_auth_rej(struct gsm_subscriber_connection *conn) -{ - DEBUGP(DMM, "-> AUTH REJECT\n"); - return gsm48_tx_simple(conn, GSM48_PDISC_MM, GSM48_MT_MM_AUTH_REJ); -} - -static int gsm48_tx_mm_serv_ack(struct gsm_subscriber_connection *conn) -{ - DEBUGP(DMM, "-> CM SERVICE ACK\n"); - return gsm48_tx_simple(conn, GSM48_PDISC_MM, GSM48_MT_MM_CM_SERV_ACC); -} - -/* 9.2.6 CM service reject */ -static 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); - msg->lchan = conn->lchan; - return gsm48_conn_sendmsg(msg, conn, NULL); -} - -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; - - switch (event) { - case GSM_SECURITY_AUTH_FAILED: - /* Nothing to do */ - break; - - case GSM_SECURITY_NOAVAIL: - rc = gsm48_tx_mm_serv_ack(conn); - break; - - case GSM_SECURITY_SUCCEEDED: - /* nothing to do. CIPHER MODE COMMAND is - * implicit CM SERV ACK */ - 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) -{ - u_int8_t mi_type; - char mi_string[GSM48_MI_SIZE]; - - struct gsm_bts *bts = conn->bts; - struct gsm_subscriber *subscr; - struct gsm48_hdr *gh = msgb_l3(msg); - struct gsm48_service_request *req = - (struct gsm48_service_request *)gh->data; - /* unfortunately in Phase1 the classmark2 length is variable */ - u_int8_t classmark2_len = gh->data[1]; - u_int8_t *classmark2 = gh->data+2; - u_int8_t mi_len = *(classmark2 + classmark2_len); - u_int8_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); - } - - mi_type = mi[0] & GSM_MI_TYPE_MASK; - if (mi_type != GSM_MI_TYPE_TMSI) { - DEBUGPC(DMM, "mi_type is not TMSI: %d\n", mi_type); - return gsm48_tx_mm_serv_rej(conn, - GSM48_REJECT_INCORRECT_MESSAGE); - } - - gsm48_mi_to_string(mi_string, sizeof(mi_string), mi, mi_len); - DEBUGPC(DMM, "serv_type=0x%02x mi_type=0x%02x M(%s)\n", - req->cm_service_type, mi_type, mi_string); - - dispatch_signal(SS_SUBSCR, S_SUBSCR_IDENTITY, (classmark2 + classmark2_len)); - - if (is_siemens_bts(bts)) - send_siemens_mrpci(msg->lchan, classmark2-1); - - subscr = subscr_get_by_tmsi(bts->network, - tmsi_from_string(mi_string)); - - /* 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_HLR); - - 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); - - 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 msgb *msg) -{ - struct gsm_bts *bts = msg->lchan->ts->trx->bts; - struct gsm48_hdr *gh = msgb_l3(msg); - struct gsm48_imsi_detach_ind *idi = - (struct gsm48_imsi_detach_ind *) gh->data; - u_int8_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_type=0x%02x MI(%s): ", - mi_type, mi_string); - - counter_inc(bts->network->stats.loc_upd_type.detach); - - switch (mi_type) { - case GSM_MI_TYPE_TMSI: - subscr = subscr_get_by_tmsi(bts->network, - tmsi_from_string(mi_string)); - break; - case GSM_MI_TYPE_IMSI: - subscr = subscr_get_by_imsi(bts->network, 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, msg->trx->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! */ - - /* subscriber is detached: should we release lchan? */ - return 0; -} - -static int gsm48_rx_mm_status(struct msgb *msg) -{ - struct gsm48_hdr *gh = msgb_l3(msg); - - DEBUGP(DMM, "MM STATUS (reject cause 0x%02x)\n", gh->data[0]); - - return 0; -} - -/* Chapter 9.2.3: Authentication Response */ -static int gsm48_rx_mm_auth_resp(struct gsm_subscriber_connection *conn, struct msgb *msg) -{ - struct gsm48_hdr *gh = msgb_l3(msg); - struct gsm48_auth_resp *ar = (struct gsm48_auth_resp*) gh->data; - struct gsm_network *net = conn->bts->network; - - DEBUGP(DMM, "MM AUTHENTICATION RESPONSE (sres = %s): ", - hexdump(ar->sres, 4)); - - /* Safety check */ - if (!conn->sec_operation) { - DEBUGP(DMM, "No authentication/cipher operation in progress !!!\n"); - return -EIO; - } - - /* Validate SRES */ - if (memcmp(conn->sec_operation->atuple.sres, ar->sres,4)) { - int rc; - gsm_cbfn *cb = conn->sec_operation->cb; - - DEBUGPC(DMM, "Invalid (expected %s)\n", - hexdump(conn->sec_operation->atuple.sres, 4)); - - if (cb) - cb(GSM_HOOK_RR_SECURITY, GSM_SECURITY_AUTH_FAILED, - NULL, conn, conn->sec_operation->cb_data); - - rc = gsm48_tx_mm_auth_rej(conn); - release_security_operation(conn); - return rc; - } - - DEBUGPC(DMM, "OK\n"); - - /* Start ciphering */ - return gsm0808_cipher_mode(conn, net->a5_encryption, - conn->sec_operation->atuple.kc, 8, 0); -} - -/* Receive a GSM 04.08 Mobility Management (MM) message */ -static int gsm0408_rcv_mm(struct gsm_subscriber_connection *conn, struct msgb *msg) -{ - struct gsm48_hdr *gh = msgb_l3(msg); - int rc = 0; - - switch (gh->msg_type & 0xbf) { - case GSM48_MT_MM_LOC_UPD_REQUEST: - DEBUGP(DMM, "LOCATION UPDATING REQUEST: "); - rc = mm_rx_loc_upd_req(conn, msg); - break; - case GSM48_MT_MM_ID_RESP: - rc = mm_rx_id_resp(conn, msg); - break; - case GSM48_MT_MM_CM_SERV_REQ: - rc = gsm48_rx_mm_serv_req(conn, msg); - break; - case GSM48_MT_MM_STATUS: - rc = gsm48_rx_mm_status(msg); - break; - case GSM48_MT_MM_TMSI_REALL_COMPL: - DEBUGP(DMM, "TMSI Reallocation Completed. Subscriber: %s\n", - conn->subscr ? - subscr_name(conn->subscr) : - "unknown subscriber"); - release_loc_updating_req(conn); - break; - case GSM48_MT_MM_IMSI_DETACH_IND: - rc = gsm48_rx_mm_imsi_detach_ind(msg); - break; - case GSM48_MT_MM_CM_REEST_REQ: - DEBUGP(DMM, "CM REESTABLISH REQUEST: Not implemented\n"); - break; - case GSM48_MT_MM_AUTH_RESP: - rc = gsm48_rx_mm_auth_resp(conn, msg); - break; - default: - LOGP(DMM, LOGL_NOTICE, "Unknown GSM 04.08 MM msg type 0x%02x\n", - gh->msg_type); - break; - } - - return rc; -} - -/* Receive a PAGING RESPONSE message from the MS */ -static int gsm48_rx_rr_pag_resp(struct gsm_subscriber_connection *conn, struct msgb *msg) -{ - struct gsm_bts *bts = conn->bts; - struct gsm48_hdr *gh = msgb_l3(msg); - struct gsm48_pag_resp *resp; - u_int8_t *classmark2_lv = gh->data + 1; - u_int8_t mi_type; - char mi_string[GSM48_MI_SIZE]; - struct gsm_subscriber *subscr = NULL; - int rc = 0; - - resp = (struct gsm48_pag_resp *) &gh->data[0]; - gsm48_paging_extract_mi(resp, msgb_l3len(msg) - sizeof(*gh), - mi_string, &mi_type); - DEBUGP(DRR, "PAGING RESPONSE: mi_type=0x%02x MI(%s)\n", - mi_type, mi_string); - - switch (mi_type) { - case GSM_MI_TYPE_TMSI: - subscr = subscr_get_by_tmsi(bts->network, - tmsi_from_string(mi_string)); - break; - case GSM_MI_TYPE_IMSI: - subscr = subscr_get_by_imsi(bts->network, mi_string); - break; - } - - if (!subscr) { - DEBUGP(DRR, "<- Can't find any subscriber for this ID\n"); - /* FIXME: request id? close channel? */ - return -EINVAL; - } - 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); - - rc = gsm48_handle_paging_resp(conn, msg, subscr); - return rc; -} - -static int gsm48_rx_rr_classmark(struct gsm_subscriber_connection *conn, struct msgb *msg) -{ - struct gsm48_hdr *gh = msgb_l3(msg); - struct gsm_subscriber *subscr = conn->subscr; - unsigned int payload_len = msgb_l3len(msg) - sizeof(*gh); - u_int8_t cm2_len, cm3_len = 0; - u_int8_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 -EINVAL; - } - if (cm2_len > 3) { - DEBUGPC(DRR, "CM2 too long!\n"); - return -EINVAL; - } - - 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 -EINVAL; - } - DEBUGPC(DRR, "CM3(len=%u)\n", cm3_len); - } - 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); - } - - return 0; -} - -static int gsm48_rx_rr_status(struct msgb *msg) -{ - struct gsm48_hdr *gh = msgb_l3(msg); - - DEBUGP(DRR, "STATUS rr_cause = %s\n", - rr_cause_name(gh->data[0])); - - return 0; -} - -static int gsm48_rx_rr_meas_rep(struct msgb *msg) -{ - struct gsm_meas_rep *meas_rep = lchan_next_meas_rep(msg->lchan); - - /* 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 */ - DEBUGP(DMEAS, "DIRECT GSM48 MEASUREMENT REPORT ?!? "); - gsm48_parse_meas_rep(meas_rep, msg); - - return 0; -} - -static int gsm48_rx_rr_app_info(struct gsm_subscriber_connection *conn, struct msgb *msg) -{ - struct gsm48_hdr *gh = msgb_l3(msg); - u_int8_t apdu_id_flags; - u_int8_t apdu_len; - u_int8_t *apdu_data; - - apdu_id_flags = gh->data[0]; - apdu_len = gh->data[1]; - apdu_data = gh->data+2; - - DEBUGP(DNM, "RX APPLICATION INFO id/flags=0x%02x apdu_len=%u apdu=%s", - apdu_id_flags, apdu_len, hexdump(apdu_data, apdu_len)); - - return db_apdu_blob_store(conn->subscr, apdu_id_flags, apdu_len, apdu_data); -} - -/* Chapter 9.1.10 Ciphering Mode Complete */ -static int gsm48_rx_rr_ciph_m_compl(struct gsm_subscriber_connection *conn, struct msgb *msg) -{ - gsm_cbfn *cb; - int rc = 0; - - DEBUGP(DRR, "CIPHERING MODE COMPLETE\n"); - - /* Safety check */ - if (!conn->sec_operation) { - DEBUGP(DRR, "No authentication/cipher operation in progress !!!\n"); - return -EIO; - } - - /* FIXME: check for MI (if any) */ - - /* Call back whatever was in progress (if anything) ... */ - cb = conn->sec_operation->cb; - if (cb) { - rc = cb(GSM_HOOK_RR_SECURITY, GSM_SECURITY_SUCCEEDED, - NULL, conn, conn->sec_operation->cb_data); - } - - /* Complete the operation */ - release_security_operation(conn); - - return rc; -} - -/* Chapter 9.1.16 Handover complete */ -static int gsm48_rx_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; - dispatch_signal(SS_LCHAN, S_LCHAN_HANDOVER_COMPL, &sig); - /* FIXME: release old channel */ - - return 0; -} - -/* Chapter 9.1.17 Handover Failure */ -static int gsm48_rx_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; - dispatch_signal(SS_LCHAN, S_LCHAN_HANDOVER_FAIL, &sig); - /* FIXME: release allocated new channel */ - - return 0; -} - -/* 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_CLSM_CHG: - rc = gsm48_rx_rr_classmark(conn, msg); - break; - case GSM48_MT_RR_GPRS_SUSP_REQ: - DEBUGP(DRR, "GRPS SUSPEND REQUEST\n"); - break; - case GSM48_MT_RR_PAG_RESP: - rc = gsm48_rx_rr_pag_resp(conn, msg); - break; - case GSM48_MT_RR_STATUS: - rc = gsm48_rx_rr_status(msg); - break; - case GSM48_MT_RR_MEAS_REP: - rc = gsm48_rx_rr_meas_rep(msg); - break; - case GSM48_MT_RR_APP_INFO: - rc = gsm48_rx_rr_app_info(conn, msg); - break; - case GSM48_MT_RR_CIPH_M_COMPL: - rc = gsm48_rx_rr_ciph_m_compl(conn, msg); - break; - case GSM48_MT_RR_HANDO_COMPL: - rc = gsm48_rx_rr_ho_compl(msg); - break; - case GSM48_MT_RR_HANDO_FAIL: - rc = gsm48_rx_rr_ho_fail(msg); - break; - default: - LOGP(DRR, LOGL_NOTICE, "Unimplemented " - "GSM 04.08 RR msg type 0x%02x\n", gh->msg_type); - break; - } - - return rc; -} - -int gsm48_send_rr_app_info(struct gsm_subscriber_connection *conn, u_int8_t apdu_id, - u_int8_t apdu_len, const u_int8_t *apdu) -{ - struct msgb *msg = gsm48_msgb_alloc(); - struct gsm48_hdr *gh; - - msg->lchan = conn->lchan; - - DEBUGP(DRR, "TX APPLICATION INFO id=0x%02x, len=%u\n", - apdu_id, apdu_len); - - gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh) + 2 + apdu_len); - gh->proto_discr = GSM48_PDISC_RR; - gh->msg_type = GSM48_MT_RR_APP_INFO; - gh->data[0] = apdu_id; - gh->data[1] = apdu_len; - memcpy(gh->data+2, apdu, apdu_len); - - return gsm48_conn_sendmsg(msg, conn, NULL); -} - -/* Call Control */ - -/* The entire call control code is written in accordance with Figure 7.10c - * for 'very early assignment', i.e. we allocate a TCH/F during IMMEDIATE - * ASSIGN, then first use that TCH/F for signalling and later MODE MODIFY - * it for voice */ - -static void new_cc_state(struct gsm_trans *trans, int state) -{ - if (state > 31 || state < 0) - return; - - DEBUGP(DCC, "new state %s -> %s\n", - gsm48_cc_state_name(trans->cc.state), - gsm48_cc_state_name(state)); - - trans->cc.state = state; -} - -static int gsm48_cc_tx_status(struct gsm_trans *trans, void *arg) -{ - struct msgb *msg = gsm48_msgb_alloc(); - struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh)); - u_int8_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, - u_int8_t pdisc, u_int8_t msg_type) -{ - struct msgb *msg = gsm48_msgb_alloc(); - struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh)); - - msg->lchan = conn->lchan; - - gh->proto_discr = pdisc; - gh->msg_type = msg_type; - - return gsm48_conn_sendmsg(msg, conn, NULL); -} - -static void gsm48_stop_cc_timer(struct gsm_trans *trans) -{ - if (bsc_timer_pending(&trans->cc.timer)) { - DEBUGP(DCC, "stopping pending timer T%x\n", trans->cc.Tcurrent); - bsc_del_timer(&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, - u_int32_t callref, int location, int value) -{ - struct gsm_mncc rel; - - memset(&rel, 0, sizeof(rel)); - rel.callref = callref; - mncc_set_cause(&rel, location, value); - 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->subscr->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 *param) -{ - int found = 0; - struct gsm_subscriber_connection *conn = _conn; - struct gsm_network **paging_request = param, *net; - struct gsm_trans *transt, *tmp; - - if (hooknum != GSM_HOOK_RR_PAGING) - return -EINVAL; - - net = *paging_request; - if (!net) { - DEBUGP(DCC, "Error Network not set!\n"); - return -EINVAL; - } - - /* check all tranactions (without lchan) for subscriber */ - llist_for_each_entry_safe(transt, tmp, &net->trans_list, entry) { - if (transt->paging_request != paging_request || transt->conn) - continue; - switch (event) { - case GSM_PAGING_SUCCEEDED: - if (!conn) // paranoid - break; - DEBUGP(DCC, "Paging subscr %s succeeded!\n", - transt->subscr->extension); - found = 1; - /* Assign lchan */ - if (!transt->conn) { - transt->paging_request = NULL; - transt->conn = conn; - conn->put_channel = 1; - } - /* 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 */ - found = 1; - mncc_release_ind(transt->subscr->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; - } - } - - talloc_free(paging_request); - - /* - * FIXME: The queue needs to be kicked. This is likely to go through a RF - * failure and then the subscr will be poke again. This needs a lot of fixing - * in the subscriber queue code. - */ - if (!found && conn) - conn->put_channel = 1; - return 0; -} - -static int tch_recv_mncc(struct gsm_network *net, u_int32_t callref, int enable); - -/* handle audio path for handover */ -static int handle_ho_signal(unsigned int subsys, unsigned int signal, - void *handler_data, void *signal_data) -{ - struct rtp_socket *old_rs, *new_rs, *other_rs; - struct ho_signal_data *sig = signal_data; - - if (subsys != SS_HO || signal != S_HANDOVER_ACK) - 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 = sig->new_lchan->abis_ip.rtp_socket; - old_rs = sig->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(sig->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 (sig->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; -} - -/* 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; - - /* 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); - } - } - break; - case S_ABISIP_DLCX_IND: - /* the BTS tells us a RTP stream has been disconnected */ - if (lchan->abis_ip.rtp_socket) { - rtp_socket_free(lchan->abis_ip.rtp_socket); - lchan->abis_ip.rtp_socket = NULL; - } - - break; - } - - return 0; -} - -/* map two ipaccess RTP streams onto each other */ -static int tch_map(struct gsm_lchan *lchan, struct gsm_lchan *remote_lchan) -{ - struct gsm_bts *bts = lchan->ts->trx->bts; - struct gsm_bts *remote_bts = remote_lchan->ts->trx->bts; - int rc; - - DEBUGP(DCC, "Setting up TCH map between (bts=%u,trx=%u,ts=%u) and (bts=%u,trx=%u,ts=%u)\n", - bts->nr, lchan->ts->trx->nr, lchan->ts->nr, - remote_bts->nr, remote_lchan->ts->trx->nr, remote_lchan->ts->nr); - - if (bts->type != remote_bts->type) { - DEBUGP(DCC, "Cannot switch calls between different BTS types yet\n"); - return -EINVAL; - } - - // todo: map between different bts types - switch (bts->type) { - case GSM_BTS_TYPE_NANOBTS: - if (!ipacc_rtp_direct) { - /* 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: - trau_mux_map_lchan(lchan, remote_lchan); - break; - default: - DEBUGP(DCC, "Unknown BTS type %u\n", bts->type); - return -EINVAL; - } - - return 0; -} - -/* bridge channels of two transactions */ -static int tch_bridge(struct gsm_network *net, u_int32_t *refs) -{ - struct gsm_trans *trans1 = trans_find_by_callref(net, refs[0]); - struct gsm_trans *trans2 = trans_find_by_callref(net, refs[1]); - - if (!trans1 || !trans2) - return -EIO; - - if (!trans1->conn || !trans2->conn) - return -EIO; - - /* 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, u_int32_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; - lchan = trans->conn->lchan; - bts = lchan->ts->trx->bts; - - switch (bts->type) { - case GSM_BTS_TYPE_NANOBTS: - if (ipacc_rtp_direct) { - DEBUGP(DCC, "Error: RTP proxy is disabled\n"); - return -EINVAL; - } - /* in case, we don't have a RTP socket yet, we note this - * in the transaction and try later */ - if (!lchan->abis_ip.rtp_socket) { - trans->tch_recv = enable; - 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: - if (enable) - return trau_recv_lchan(lchan, callref); - return trau_mux_unmap(NULL, callref); - break; - default: - DEBUGP(DCC, "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->subscr->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->subscr->net, trans, MNCC_DISC_IND, &l4_rel); - } - - /* process disconnect towards mobile station */ - if (disconnect || release) { - mncc_set_cause(&mo_rel, mo_location, mo_cause); - mo_rel.cause.diag[0] = ((trans->cc.Tcurrent & 0xf00) >> 8) + '0'; - mo_rel.cause.diag[1] = ((trans->cc.Tcurrent & 0x0f0) >> 4) + '0'; - mo_rel.cause.diag[2] = (trans->cc.Tcurrent & 0x00f) + '0'; - mo_rel.cause.diag_len = 3; - - if (disconnect) - gsm48_cc_tx_disconnect(trans, &mo_rel); - if (release) - gsm48_cc_tx_release(trans, &mo_rel); - } - -} - -static void gsm48_start_cc_timer(struct gsm_trans *trans, int current, - int sec, int micro) -{ - DEBUGP(DCC, "starting timer T%x with %d seconds\n", current, sec); - trans->cc.timer.cb = gsm48_cc_timeout; - trans->cc.timer.data = trans; - bsc_schedule_timer(&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); - u_int8_t msg_type = gh->msg_type & 0xbf; - unsigned int payload_len = msgb_l3len(msg) - sizeof(*gh); - struct tlv_parsed tp; - struct gsm_mncc setup; - - memset(&setup, 0, sizeof(struct gsm_mncc)); - setup.callref = trans->callref; - 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 */ - if (trans->subscr) { - setup.fields |= MNCC_F_CALLING; - strncpy(setup.calling.number, trans->subscr->extension, - sizeof(setup.calling.number)-1); - strncpy(setup.imsi, trans->subscr->imsi, - sizeof(setup.imsi)-1); - } - /* bearer capability */ - if (TLVP_PRESENT(&tp, GSM48_IE_BEARER_CAP)) { - setup.fields |= MNCC_F_BEARER_CAP; - gsm48_decode_bearer_cap(&setup.bearer_cap, - TLVP_VAL(&tp, GSM48_IE_BEARER_CAP)-1); - } - /* 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); - - counter_inc(trans->subscr->net->stats.call.mo_setup); - - /* indicate setup to MNCC */ - mncc_recvmsg(trans->subscr->net, trans, MNCC_SETUP_IND, &setup); - - /* MNCC code will modify the channel asynchronously, we should - * ipaccess-bind only after the modification has been made to the - * lchan->tch_mode */ - return 0; -} - -static int gsm48_cc_tx_setup(struct gsm_trans *trans, void *arg) -{ - struct msgb *msg = gsm48_msgb_alloc(); - struct gsm48_hdr *gh; - struct gsm_mncc *setup = arg; - int rc, trans_id; - - gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh)); - - /* transaction id must not be assigned */ - if (trans->transaction_id != 0xff) { /* unasssigned */ - DEBUGP(DCC, "TX Setup with assigned transaction. " - "This is not allowed!\n"); - /* Temporarily out of order */ - rc = mncc_release_ind(trans->subscr->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->subscr, GSM48_PDISC_CC, 0); - if (trans_id < 0) { - /* no free transaction ID */ - rc = mncc_release_ind(trans->subscr->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); - - counter_inc(trans->subscr->net->stats.call.mt_setup); - - return gsm48_conn_sendmsg(msg, trans->conn, trans); -} - -static int gsm48_cc_rx_call_conf(struct gsm_trans *trans, struct msgb *msg) -{ - struct gsm48_hdr *gh = msgb_l3(msg); - unsigned int payload_len = msgb_l3len(msg) - sizeof(*gh); - struct tlv_parsed tp; - struct gsm_mncc call_conf; - - gsm48_stop_cc_timer(trans); - gsm48_start_cc_timer(trans, 0x310, GSM48_T310); - - memset(&call_conf, 0, sizeof(struct gsm_mncc)); - call_conf.callref = trans->callref; - 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); - } - /* 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); - } - - new_cc_state(trans, GSM_CSTATE_MO_TERM_CALL_CONF); - - return mncc_recvmsg(trans->subscr->net, trans, MNCC_CALL_CONF_IND, - &call_conf); -} - -static int gsm48_cc_tx_call_proc(struct gsm_trans *trans, void *arg) -{ - struct gsm_mncc *proceeding = arg; - struct msgb *msg = gsm48_msgb_alloc(); - struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh)); - - gh->msg_type = GSM48_MT_CC_CALL_PROC; - - new_cc_state(trans, GSM_CSTATE_MO_CALL_PROC); - - /* bearer capability */ - if (proceeding->fields & MNCC_F_BEARER_CAP) - gsm48_encode_bearer_cap(msg, 0, &proceeding->bearer_cap); - /* facility */ - if (proceeding->fields & MNCC_F_FACILITY) - gsm48_encode_facility(msg, 0, &proceeding->facility); - /* progress */ - if (proceeding->fields & MNCC_F_PROGRESS) - gsm48_encode_progress(msg, 0, &proceeding->progress); - - return gsm48_conn_sendmsg(msg, trans->conn, trans); -} - -static int gsm48_cc_rx_alerting(struct gsm_trans *trans, struct msgb *msg) -{ - struct gsm48_hdr *gh = msgb_l3(msg); - unsigned int payload_len = msgb_l3len(msg) - sizeof(*gh); - struct tlv_parsed tp; - struct gsm_mncc alerting; - - gsm48_stop_cc_timer(trans); - gsm48_start_cc_timer(trans, 0x301, GSM48_T301); - - memset(&alerting, 0, sizeof(struct gsm_mncc)); - alerting.callref = trans->callref; - tlv_parse(&tp, &gsm48_att_tlvdef, gh->data, payload_len, 0, 0); - /* facility */ - if (TLVP_PRESENT(&tp, GSM48_IE_FACILITY)) { - alerting.fields |= MNCC_F_FACILITY; - gsm48_decode_facility(&alerting.facility, - TLVP_VAL(&tp, GSM48_IE_FACILITY)-1); - } - - /* progress */ - if (TLVP_PRESENT(&tp, GSM48_IE_PROGR_IND)) { - alerting.fields |= MNCC_F_PROGRESS; - gsm48_decode_progress(&alerting.progress, - TLVP_VAL(&tp, GSM48_IE_PROGR_IND)-1); - } - /* ss-version */ - if (TLVP_PRESENT(&tp, GSM48_IE_SS_VERS)) { - alerting.fields |= MNCC_F_SSVERSION; - gsm48_decode_ssversion(&alerting.ssversion, - TLVP_VAL(&tp, GSM48_IE_SS_VERS)-1); - } - - new_cc_state(trans, GSM_CSTATE_CALL_RECEIVED); - - return mncc_recvmsg(trans->subscr->net, trans, MNCC_ALERT_IND, - &alerting); -} - -static int gsm48_cc_tx_alerting(struct gsm_trans *trans, void *arg) -{ - struct gsm_mncc *alerting = arg; - struct msgb *msg = gsm48_msgb_alloc(); - struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh)); - - gh->msg_type = GSM48_MT_CC_ALERTING; - - /* facility */ - if (alerting->fields & MNCC_F_FACILITY) - gsm48_encode_facility(msg, 0, &alerting->facility); - /* progress */ - if (alerting->fields & MNCC_F_PROGRESS) - gsm48_encode_progress(msg, 0, &alerting->progress); - /* user-user */ - if (alerting->fields & MNCC_F_USERUSER) - gsm48_encode_useruser(msg, 0, &alerting->useruser); - - new_cc_state(trans, GSM_CSTATE_CALL_DELIVERED); - - return gsm48_conn_sendmsg(msg, trans->conn, trans); -} - -static int gsm48_cc_tx_progress(struct gsm_trans *trans, void *arg) -{ - struct gsm_mncc *progress = arg; - struct msgb *msg = gsm48_msgb_alloc(); - struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh)); - - gh->msg_type = GSM48_MT_CC_PROGRESS; - - /* progress */ - gsm48_encode_progress(msg, 1, &progress->progress); - /* user-user */ - if (progress->fields & MNCC_F_USERUSER) - gsm48_encode_useruser(msg, 0, &progress->useruser); - - return gsm48_conn_sendmsg(msg, trans->conn, trans); -} - -static int gsm48_cc_tx_connect(struct gsm_trans *trans, void *arg) -{ - struct gsm_mncc *connect = arg; - struct msgb *msg = gsm48_msgb_alloc(); - struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh)); - - gh->msg_type = GSM48_MT_CC_CONNECT; - - gsm48_stop_cc_timer(trans); - gsm48_start_cc_timer(trans, 0x313, GSM48_T313); - - /* facility */ - if (connect->fields & MNCC_F_FACILITY) - gsm48_encode_facility(msg, 0, &connect->facility); - /* progress */ - if (connect->fields & MNCC_F_PROGRESS) - gsm48_encode_progress(msg, 0, &connect->progress); - /* connected number */ - if (connect->fields & MNCC_F_CONNECTED) - gsm48_encode_connected(msg, &connect->connected); - /* user-user */ - if (connect->fields & MNCC_F_USERUSER) - gsm48_encode_useruser(msg, 0, &connect->useruser); - - new_cc_state(trans, GSM_CSTATE_CONNECT_IND); - - return gsm48_conn_sendmsg(msg, trans->conn, trans); -} - -static int gsm48_cc_rx_connect(struct gsm_trans *trans, struct msgb *msg) -{ - struct gsm48_hdr *gh = msgb_l3(msg); - unsigned int payload_len = msgb_l3len(msg) - sizeof(*gh); - struct tlv_parsed tp; - struct gsm_mncc connect; - - gsm48_stop_cc_timer(trans); - - memset(&connect, 0, sizeof(struct gsm_mncc)); - connect.callref = trans->callref; - tlv_parse(&tp, &gsm48_att_tlvdef, gh->data, payload_len, 0, 0); - /* use subscriber as connected party number */ - if (trans->subscr) { - connect.fields |= MNCC_F_CONNECTED; - strncpy(connect.connected.number, trans->subscr->extension, - sizeof(connect.connected.number)-1); - strncpy(connect.imsi, trans->subscr->imsi, - sizeof(connect.imsi)-1); - } - /* facility */ - if (TLVP_PRESENT(&tp, GSM48_IE_FACILITY)) { - connect.fields |= MNCC_F_FACILITY; - gsm48_decode_facility(&connect.facility, - TLVP_VAL(&tp, GSM48_IE_FACILITY)-1); - } - /* user-user */ - if (TLVP_PRESENT(&tp, GSM48_IE_USER_USER)) { - connect.fields |= MNCC_F_USERUSER; - gsm48_decode_useruser(&connect.useruser, - TLVP_VAL(&tp, GSM48_IE_USER_USER)-1); - } - /* ss-version */ - if (TLVP_PRESENT(&tp, GSM48_IE_SS_VERS)) { - connect.fields |= MNCC_F_SSVERSION; - gsm48_decode_ssversion(&connect.ssversion, - TLVP_VAL(&tp, GSM48_IE_SS_VERS)-1); - } - - new_cc_state(trans, GSM_CSTATE_CONNECT_REQUEST); - counter_inc(trans->subscr->net->stats.call.mt_connect); - - return mncc_recvmsg(trans->subscr->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); - counter_inc(trans->subscr->net->stats.call.mo_connect_ack); - - memset(&connect_ack, 0, sizeof(struct gsm_mncc)); - connect_ack.callref = trans->callref; - - return mncc_recvmsg(trans->subscr->net, trans, MNCC_SETUP_COMPL_IND, - &connect_ack); -} - -static int gsm48_cc_tx_connect_ack(struct gsm_trans *trans, void *arg) -{ - struct msgb *msg = gsm48_msgb_alloc(); - struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh)); - - gh->msg_type = GSM48_MT_CC_CONNECT_ACK; - - new_cc_state(trans, GSM_CSTATE_ACTIVE); - - return gsm48_conn_sendmsg(msg, trans->conn, trans); -} - -static int gsm48_cc_rx_disconnect(struct gsm_trans *trans, struct msgb *msg) -{ - struct gsm48_hdr *gh = msgb_l3(msg); - unsigned int payload_len = msgb_l3len(msg) - sizeof(*gh); - struct tlv_parsed tp; - struct gsm_mncc disc; - - gsm48_stop_cc_timer(trans); - - new_cc_state(trans, GSM_CSTATE_DISCONNECT_REQ); - - memset(&disc, 0, sizeof(struct gsm_mncc)); - disc.callref = trans->callref; - tlv_parse(&tp, &gsm48_att_tlvdef, gh->data, payload_len, GSM48_IE_CAUSE, 0); - /* cause */ - if (TLVP_PRESENT(&tp, GSM48_IE_CAUSE)) { - disc.fields |= MNCC_F_CAUSE; - gsm48_decode_cause(&disc.cause, - TLVP_VAL(&tp, GSM48_IE_CAUSE)-1); - } - /* facility */ - if (TLVP_PRESENT(&tp, GSM48_IE_FACILITY)) { - disc.fields |= MNCC_F_FACILITY; - gsm48_decode_facility(&disc.facility, - TLVP_VAL(&tp, GSM48_IE_FACILITY)-1); - } - /* user-user */ - if (TLVP_PRESENT(&tp, GSM48_IE_USER_USER)) { - disc.fields |= MNCC_F_USERUSER; - gsm48_decode_useruser(&disc.useruser, - TLVP_VAL(&tp, GSM48_IE_USER_USER)-1); - } - /* ss-version */ - if (TLVP_PRESENT(&tp, GSM48_IE_SS_VERS)) { - disc.fields |= MNCC_F_SSVERSION; - gsm48_decode_ssversion(&disc.ssversion, - TLVP_VAL(&tp, GSM48_IE_SS_VERS)-1); - } - - return mncc_recvmsg(trans->subscr->net, trans, MNCC_DISC_IND, &disc); - -} - -static struct gsm_mncc_cause default_cause = { - .location = GSM48_CAUSE_LOC_PRN_S_LU, - .coding = 0, - .rec = 0, - .rec_val = 0, - .value = GSM48_CC_CAUSE_NORMAL_UNSPEC, - .diag_len = 0, - .diag = { 0 }, -}; - -static int gsm48_cc_tx_disconnect(struct gsm_trans *trans, void *arg) -{ - struct gsm_mncc *disc = arg; - struct msgb *msg = gsm48_msgb_alloc(); - struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh)); - - gh->msg_type = GSM48_MT_CC_DISCONNECT; - - gsm48_stop_cc_timer(trans); - gsm48_start_cc_timer(trans, 0x306, GSM48_T306); - - /* cause */ - if (disc->fields & MNCC_F_CAUSE) - gsm48_encode_cause(msg, 1, &disc->cause); - else - gsm48_encode_cause(msg, 1, &default_cause); - - /* facility */ - if (disc->fields & MNCC_F_FACILITY) - gsm48_encode_facility(msg, 0, &disc->facility); - /* progress */ - if (disc->fields & MNCC_F_PROGRESS) - gsm48_encode_progress(msg, 0, &disc->progress); - /* user-user */ - if (disc->fields & MNCC_F_USERUSER) - gsm48_encode_useruser(msg, 0, &disc->useruser); - - /* store disconnect cause for T306 expiry */ - memcpy(&trans->cc.msg, disc, sizeof(struct gsm_mncc)); - - new_cc_state(trans, GSM_CSTATE_DISCONNECT_IND); - - return gsm48_conn_sendmsg(msg, trans->conn, trans); -} - -static int gsm48_cc_rx_release(struct gsm_trans *trans, struct msgb *msg) -{ - struct gsm48_hdr *gh = msgb_l3(msg); - unsigned int payload_len = msgb_l3len(msg) - sizeof(*gh); - struct tlv_parsed tp; - struct gsm_mncc rel; - int rc; - - gsm48_stop_cc_timer(trans); - - memset(&rel, 0, sizeof(struct gsm_mncc)); - rel.callref = trans->callref; - tlv_parse(&tp, &gsm48_att_tlvdef, gh->data, payload_len, 0, 0); - /* cause */ - if (TLVP_PRESENT(&tp, GSM48_IE_CAUSE)) { - rel.fields |= MNCC_F_CAUSE; - gsm48_decode_cause(&rel.cause, - TLVP_VAL(&tp, GSM48_IE_CAUSE)-1); - } - /* facility */ - if (TLVP_PRESENT(&tp, GSM48_IE_FACILITY)) { - rel.fields |= MNCC_F_FACILITY; - gsm48_decode_facility(&rel.facility, - TLVP_VAL(&tp, GSM48_IE_FACILITY)-1); - } - /* user-user */ - if (TLVP_PRESENT(&tp, GSM48_IE_USER_USER)) { - rel.fields |= MNCC_F_USERUSER; - gsm48_decode_useruser(&rel.useruser, - TLVP_VAL(&tp, GSM48_IE_USER_USER)-1); - } - /* ss-version */ - if (TLVP_PRESENT(&tp, GSM48_IE_SS_VERS)) { - rel.fields |= MNCC_F_SSVERSION; - gsm48_decode_ssversion(&rel.ssversion, - TLVP_VAL(&tp, GSM48_IE_SS_VERS)-1); - } - - if (trans->cc.state == GSM_CSTATE_RELEASE_REQ) { - /* release collision 5.4.5 */ - rc = mncc_recvmsg(trans->subscr->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->subscr->net, trans, MNCC_REL_IND, &rel); - } - - new_cc_state(trans, GSM_CSTATE_NULL); - - trans->callref = 0; - trans_free(trans); - - return rc; -} - -static int gsm48_cc_tx_release(struct gsm_trans *trans, void *arg) -{ - struct gsm_mncc *rel = arg; - struct msgb *msg = gsm48_msgb_alloc(); - struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh)); - - gh->msg_type = GSM48_MT_CC_RELEASE; - - trans->callref = 0; - - 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->subscr->net, trans, - MNCC_REJ_IND, &rel); - break; - case GSM_CSTATE_RELEASE_REQ: - rc = mncc_recvmsg(trans->subscr->net, trans, - MNCC_REL_CNF, &rel); - break; - default: - rc = mncc_recvmsg(trans->subscr->net, trans, - MNCC_REL_IND, &rel); - } - } - - trans->callref = 0; - trans_free(trans); - - return rc; -} - -static int gsm48_cc_tx_release_compl(struct gsm_trans *trans, void *arg) -{ - struct gsm_mncc *rel = arg; - struct msgb *msg = gsm48_msgb_alloc(); - struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh)); - int ret; - - gh->msg_type = GSM48_MT_CC_RELEASE_COMPL; - - trans->callref = 0; - - gsm48_stop_cc_timer(trans); - - /* cause */ - if (rel->fields & MNCC_F_CAUSE) - gsm48_encode_cause(msg, 0, &rel->cause); - /* facility */ - if (rel->fields & MNCC_F_FACILITY) - gsm48_encode_facility(msg, 0, &rel->facility); - /* user-user */ - if (rel->fields & MNCC_F_USERUSER) - gsm48_encode_useruser(msg, 0, &rel->useruser); - - ret = gsm48_conn_sendmsg(msg, trans->conn, trans); - - trans_free(trans); - - return ret; -} - -static int gsm48_cc_rx_facility(struct gsm_trans *trans, struct msgb *msg) -{ - struct gsm48_hdr *gh = msgb_l3(msg); - unsigned int payload_len = msgb_l3len(msg) - sizeof(*gh); - struct tlv_parsed tp; - struct gsm_mncc fac; - - memset(&fac, 0, sizeof(struct gsm_mncc)); - fac.callref = trans->callref; - tlv_parse(&tp, &gsm48_att_tlvdef, gh->data, payload_len, GSM48_IE_FACILITY, 0); - /* facility */ - if (TLVP_PRESENT(&tp, GSM48_IE_FACILITY)) { - fac.fields |= MNCC_F_FACILITY; - gsm48_decode_facility(&fac.facility, - TLVP_VAL(&tp, GSM48_IE_FACILITY)-1); - } - /* ss-version */ - if (TLVP_PRESENT(&tp, GSM48_IE_SS_VERS)) { - fac.fields |= MNCC_F_SSVERSION; - gsm48_decode_ssversion(&fac.ssversion, - TLVP_VAL(&tp, GSM48_IE_SS_VERS)-1); - } - - return mncc_recvmsg(trans->subscr->net, trans, MNCC_FACILITY_IND, &fac); -} - -static int gsm48_cc_tx_facility(struct gsm_trans *trans, void *arg) -{ - struct gsm_mncc *fac = arg; - struct msgb *msg = gsm48_msgb_alloc(); - struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh)); - - gh->msg_type = GSM48_MT_CC_FACILITY; - - /* facility */ - gsm48_encode_facility(msg, 1, &fac->facility); - - return gsm48_conn_sendmsg(msg, trans->conn, trans); -} - -static int gsm48_cc_rx_hold(struct gsm_trans *trans, struct msgb *msg) -{ - struct gsm_mncc hold; - - memset(&hold, 0, sizeof(struct gsm_mncc)); - hold.callref = trans->callref; - return mncc_recvmsg(trans->subscr->net, trans, MNCC_HOLD_IND, &hold); -} - -static int gsm48_cc_tx_hold_ack(struct gsm_trans *trans, void *arg) -{ - struct msgb *msg = gsm48_msgb_alloc(); - struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh)); - - gh->msg_type = GSM48_MT_CC_HOLD_ACK; - - return gsm48_conn_sendmsg(msg, trans->conn, trans); -} - -static int gsm48_cc_tx_hold_rej(struct gsm_trans *trans, void *arg) -{ - struct gsm_mncc *hold_rej = arg; - struct msgb *msg = gsm48_msgb_alloc(); - struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh)); - - gh->msg_type = GSM48_MT_CC_HOLD_REJ; - - /* cause */ - if (hold_rej->fields & MNCC_F_CAUSE) - gsm48_encode_cause(msg, 1, &hold_rej->cause); - else - gsm48_encode_cause(msg, 1, &default_cause); - - return gsm48_conn_sendmsg(msg, trans->conn, trans); -} - -static int gsm48_cc_rx_retrieve(struct gsm_trans *trans, struct msgb *msg) -{ - struct gsm_mncc retrieve; - - memset(&retrieve, 0, sizeof(struct gsm_mncc)); - retrieve.callref = trans->callref; - return mncc_recvmsg(trans->subscr->net, trans, MNCC_RETRIEVE_IND, - &retrieve); -} - -static int gsm48_cc_tx_retrieve_ack(struct gsm_trans *trans, void *arg) -{ - struct msgb *msg = gsm48_msgb_alloc(); - struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh)); - - gh->msg_type = GSM48_MT_CC_RETR_ACK; - - return gsm48_conn_sendmsg(msg, trans->conn, trans); -} - -static int gsm48_cc_tx_retrieve_rej(struct gsm_trans *trans, void *arg) -{ - struct gsm_mncc *retrieve_rej = arg; - struct msgb *msg = gsm48_msgb_alloc(); - struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh)); - - gh->msg_type = GSM48_MT_CC_RETR_REJ; - - /* cause */ - if (retrieve_rej->fields & MNCC_F_CAUSE) - gsm48_encode_cause(msg, 1, &retrieve_rej->cause); - else - gsm48_encode_cause(msg, 1, &default_cause); - - return gsm48_conn_sendmsg(msg, trans->conn, trans); -} - -static int gsm48_cc_rx_start_dtmf(struct gsm_trans *trans, struct msgb *msg) -{ - struct gsm48_hdr *gh = msgb_l3(msg); - unsigned int payload_len = msgb_l3len(msg) - sizeof(*gh); - struct tlv_parsed tp; - struct gsm_mncc dtmf; - - memset(&dtmf, 0, sizeof(struct gsm_mncc)); - dtmf.callref = trans->callref; - tlv_parse(&tp, &gsm48_att_tlvdef, gh->data, payload_len, 0, 0); - /* keypad facility */ - if (TLVP_PRESENT(&tp, GSM48_IE_KPD_FACILITY)) { - dtmf.fields |= MNCC_F_KEYPAD; - gsm48_decode_keypad(&dtmf.keypad, - TLVP_VAL(&tp, GSM48_IE_KPD_FACILITY)-1); - } - - return mncc_recvmsg(trans->subscr->net, trans, MNCC_START_DTMF_IND, &dtmf); -} - -static int gsm48_cc_tx_start_dtmf_ack(struct gsm_trans *trans, void *arg) -{ - struct gsm_mncc *dtmf = arg; - struct msgb *msg = gsm48_msgb_alloc(); - struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh)); - - gh->msg_type = GSM48_MT_CC_START_DTMF_ACK; - - /* keypad */ - if (dtmf->fields & MNCC_F_KEYPAD) - gsm48_encode_keypad(msg, dtmf->keypad); - - return gsm48_conn_sendmsg(msg, trans->conn, trans); -} - -static int gsm48_cc_tx_start_dtmf_rej(struct gsm_trans *trans, void *arg) -{ - struct gsm_mncc *dtmf = arg; - struct msgb *msg = gsm48_msgb_alloc(); - struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh)); - - gh->msg_type = GSM48_MT_CC_START_DTMF_REJ; - - /* cause */ - if (dtmf->fields & MNCC_F_CAUSE) - gsm48_encode_cause(msg, 1, &dtmf->cause); - else - gsm48_encode_cause(msg, 1, &default_cause); - - return gsm48_conn_sendmsg(msg, trans->conn, trans); -} - -static int gsm48_cc_tx_stop_dtmf_ack(struct gsm_trans *trans, void *arg) -{ - struct msgb *msg = gsm48_msgb_alloc(); - struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh)); - - gh->msg_type = GSM48_MT_CC_STOP_DTMF_ACK; - - return gsm48_conn_sendmsg(msg, trans->conn, trans); -} - -static int gsm48_cc_rx_stop_dtmf(struct gsm_trans *trans, struct msgb *msg) -{ - struct gsm_mncc dtmf; - - memset(&dtmf, 0, sizeof(struct gsm_mncc)); - dtmf.callref = trans->callref; - - return mncc_recvmsg(trans->subscr->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); - } - - new_cc_state(trans, GSM_CSTATE_MO_ORIG_MODIFY); - - return mncc_recvmsg(trans->subscr->net, trans, MNCC_MODIFY_IND, &modify); -} - -static int gsm48_cc_tx_modify(struct gsm_trans *trans, void *arg) -{ - struct gsm_mncc *modify = arg; - struct msgb *msg = gsm48_msgb_alloc(); - struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh)); - - gh->msg_type = GSM48_MT_CC_MODIFY; - - gsm48_start_cc_timer(trans, 0x323, GSM48_T323); - - /* bearer capability */ - gsm48_encode_bearer_cap(msg, 1, &modify->bearer_cap); - - new_cc_state(trans, GSM_CSTATE_MO_TERM_MODIFY); - - return gsm48_conn_sendmsg(msg, trans->conn, trans); -} - -static int gsm48_cc_rx_modify_complete(struct gsm_trans *trans, struct msgb *msg) -{ - struct gsm48_hdr *gh = msgb_l3(msg); - unsigned int payload_len = msgb_l3len(msg) - sizeof(*gh); - struct tlv_parsed tp; - struct gsm_mncc modify; - - gsm48_stop_cc_timer(trans); - - memset(&modify, 0, sizeof(struct gsm_mncc)); - modify.callref = trans->callref; - tlv_parse(&tp, &gsm48_att_tlvdef, gh->data, payload_len, GSM48_IE_BEARER_CAP, 0); - /* bearer capability */ - if (TLVP_PRESENT(&tp, GSM48_IE_BEARER_CAP)) { - modify.fields |= MNCC_F_BEARER_CAP; - gsm48_decode_bearer_cap(&modify.bearer_cap, - TLVP_VAL(&tp, GSM48_IE_BEARER_CAP)-1); - } - - new_cc_state(trans, GSM_CSTATE_ACTIVE); - - return mncc_recvmsg(trans->subscr->net, trans, MNCC_MODIFY_CNF, &modify); -} - -static int gsm48_cc_tx_modify_complete(struct gsm_trans *trans, void *arg) -{ - struct gsm_mncc *modify = arg; - struct msgb *msg = gsm48_msgb_alloc(); - struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh)); - - gh->msg_type = GSM48_MT_CC_MODIFY_COMPL; - - /* bearer capability */ - gsm48_encode_bearer_cap(msg, 1, &modify->bearer_cap); - - new_cc_state(trans, GSM_CSTATE_ACTIVE); - - return gsm48_conn_sendmsg(msg, trans->conn, trans); -} - -static int gsm48_cc_rx_modify_reject(struct gsm_trans *trans, struct msgb *msg) -{ - struct gsm48_hdr *gh = msgb_l3(msg); - unsigned int payload_len = msgb_l3len(msg) - sizeof(*gh); - struct tlv_parsed tp; - struct gsm_mncc modify; - - gsm48_stop_cc_timer(trans); - - memset(&modify, 0, sizeof(struct gsm_mncc)); - modify.callref = trans->callref; - tlv_parse(&tp, &gsm48_att_tlvdef, gh->data, payload_len, GSM48_IE_BEARER_CAP, GSM48_IE_CAUSE); - /* bearer capability */ - if (TLVP_PRESENT(&tp, GSM48_IE_BEARER_CAP)) { - modify.fields |= GSM48_IE_BEARER_CAP; - gsm48_decode_bearer_cap(&modify.bearer_cap, - TLVP_VAL(&tp, GSM48_IE_BEARER_CAP)-1); - } - /* 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->subscr->net, trans, MNCC_MODIFY_REJ, &modify); -} - -static int gsm48_cc_tx_modify_reject(struct gsm_trans *trans, void *arg) -{ - struct gsm_mncc *modify = arg; - struct msgb *msg = gsm48_msgb_alloc(); - struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh)); - - gh->msg_type = GSM48_MT_CC_MODIFY_REJECT; - - /* bearer capability */ - gsm48_encode_bearer_cap(msg, 1, &modify->bearer_cap); - /* cause */ - gsm48_encode_cause(msg, 1, &modify->cause); - - new_cc_state(trans, GSM_CSTATE_ACTIVE); - - return gsm48_conn_sendmsg(msg, trans->conn, trans); -} - -static int gsm48_cc_tx_notify(struct gsm_trans *trans, void *arg) -{ - struct gsm_mncc *notify = arg; - struct msgb *msg = gsm48_msgb_alloc(); - struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh)); - - gh->msg_type = GSM48_MT_CC_NOTIFY; - - /* notify */ - gsm48_encode_notify(msg, notify->notify); - - return gsm48_conn_sendmsg(msg, trans->conn, trans); -} - -static int gsm48_cc_rx_notify(struct gsm_trans *trans, struct msgb *msg) -{ - struct gsm48_hdr *gh = msgb_l3(msg); - unsigned int payload_len = msgb_l3len(msg) - sizeof(*gh); -// struct tlv_parsed tp; - struct gsm_mncc notify; - - memset(¬ify, 0, sizeof(struct gsm_mncc)); - notify.callref = trans->callref; -// tlv_parse(&tp, &gsm48_att_tlvdef, gh->data, payload_len); - if (payload_len >= 1) - gsm48_decode_notify(¬ify.notify, gh->data); - - return mncc_recvmsg(trans->subscr->net, trans, MNCC_NOTIFY_IND, ¬ify); -} - -static int gsm48_cc_tx_userinfo(struct gsm_trans *trans, void *arg) -{ - struct gsm_mncc *user = arg; - struct msgb *msg = gsm48_msgb_alloc(); - struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh)); - - gh->msg_type = GSM48_MT_CC_USER_INFO; - - /* user-user */ - if (user->fields & MNCC_F_USERUSER) - gsm48_encode_useruser(msg, 1, &user->useruser); - /* more data */ - if (user->more) - gsm48_encode_more(msg); - - return gsm48_conn_sendmsg(msg, trans->conn, trans); -} - -static int gsm48_cc_rx_userinfo(struct gsm_trans *trans, struct msgb *msg) -{ - struct gsm48_hdr *gh = msgb_l3(msg); - unsigned int payload_len = msgb_l3len(msg) - sizeof(*gh); - struct tlv_parsed tp; - struct gsm_mncc user; - - memset(&user, 0, sizeof(struct gsm_mncc)); - user.callref = trans->callref; - tlv_parse(&tp, &gsm48_att_tlvdef, gh->data, payload_len, GSM48_IE_USER_USER, 0); - /* user-user */ - if (TLVP_PRESENT(&tp, GSM48_IE_USER_USER)) { - user.fields |= MNCC_F_USERUSER; - gsm48_decode_useruser(&user.useruser, - TLVP_VAL(&tp, GSM48_IE_USER_USER)-1); - } - /* more data */ - if (TLVP_PRESENT(&tp, GSM48_IE_MORE_DATA)) - user.more = 1; - - return mncc_recvmsg(trans->subscr->net, trans, MNCC_USERINFO_IND, &user); -} - -static int _gsm48_lchan_modify(struct gsm_trans *trans, void *arg) -{ - struct gsm_mncc *mode = arg; - - return gsm0808_assign_req(trans->conn, mode->lchan_mode, 1); -} - -static struct downstate { - u_int32_t states; - int type; - int (*rout) (struct gsm_trans *trans, void *arg); -} downstatelist[] = { - /* mobile originating call establishment */ - {SBIT(GSM_CSTATE_INITIATED), /* 5.2.1.2 */ - MNCC_CALL_PROC_REQ, gsm48_cc_tx_call_proc}, - {SBIT(GSM_CSTATE_INITIATED) | SBIT(GSM_CSTATE_MO_CALL_PROC), /* 5.2.1.2 | 5.2.1.5 */ - MNCC_ALERT_REQ, gsm48_cc_tx_alerting}, - {SBIT(GSM_CSTATE_INITIATED) | SBIT(GSM_CSTATE_MO_CALL_PROC) | SBIT(GSM_CSTATE_CALL_DELIVERED), /* 5.2.1.2 | 5.2.1.6 | 5.2.1.6 */ - MNCC_SETUP_RSP, gsm48_cc_tx_connect}, - {SBIT(GSM_CSTATE_MO_CALL_PROC), /* 5.2.1.4.2 */ - MNCC_PROGRESS_REQ, gsm48_cc_tx_progress}, - /* mobile terminating call establishment */ - {SBIT(GSM_CSTATE_NULL), /* 5.2.2.1 */ - MNCC_SETUP_REQ, gsm48_cc_tx_setup}, - {SBIT(GSM_CSTATE_CONNECT_REQUEST), - MNCC_SETUP_COMPL_REQ, gsm48_cc_tx_connect_ack}, - /* signalling during call */ - {SBIT(GSM_CSTATE_ACTIVE), - MNCC_NOTIFY_REQ, gsm48_cc_tx_notify}, - {ALL_STATES - SBIT(GSM_CSTATE_NULL) - SBIT(GSM_CSTATE_RELEASE_REQ), - MNCC_FACILITY_REQ, gsm48_cc_tx_facility}, - {ALL_STATES, - MNCC_START_DTMF_RSP, gsm48_cc_tx_start_dtmf_ack}, - {ALL_STATES, - MNCC_START_DTMF_REJ, gsm48_cc_tx_start_dtmf_rej}, - {ALL_STATES, - MNCC_STOP_DTMF_RSP, gsm48_cc_tx_stop_dtmf_ack}, - {SBIT(GSM_CSTATE_ACTIVE), - MNCC_HOLD_CNF, gsm48_cc_tx_hold_ack}, - {SBIT(GSM_CSTATE_ACTIVE), - MNCC_HOLD_REJ, gsm48_cc_tx_hold_rej}, - {SBIT(GSM_CSTATE_ACTIVE), - MNCC_RETRIEVE_CNF, gsm48_cc_tx_retrieve_ack}, - {SBIT(GSM_CSTATE_ACTIVE), - MNCC_RETRIEVE_REJ, gsm48_cc_tx_retrieve_rej}, - {SBIT(GSM_CSTATE_ACTIVE), - MNCC_MODIFY_REQ, gsm48_cc_tx_modify}, - {SBIT(GSM_CSTATE_MO_ORIG_MODIFY), - MNCC_MODIFY_RSP, gsm48_cc_tx_modify_complete}, - {SBIT(GSM_CSTATE_MO_ORIG_MODIFY), - MNCC_MODIFY_REJ, gsm48_cc_tx_modify_reject}, - {SBIT(GSM_CSTATE_ACTIVE), - MNCC_USERINFO_REQ, gsm48_cc_tx_userinfo}, - /* clearing */ - {SBIT(GSM_CSTATE_INITIATED), - MNCC_REJ_REQ, gsm48_cc_tx_release_compl}, - {ALL_STATES - SBIT(GSM_CSTATE_NULL) - SBIT(GSM_CSTATE_DISCONNECT_IND) - SBIT(GSM_CSTATE_RELEASE_REQ) - SBIT(GSM_CSTATE_DISCONNECT_REQ), /* 5.4.4 */ - MNCC_DISC_REQ, gsm48_cc_tx_disconnect}, - {ALL_STATES - SBIT(GSM_CSTATE_NULL) - SBIT(GSM_CSTATE_RELEASE_REQ), /* 5.4.3.2 */ - MNCC_REL_REQ, gsm48_cc_tx_release}, - /* special */ - {ALL_STATES, - MNCC_LCHAN_MODIFY, _gsm48_lchan_modify}, -}; - -#define DOWNSLLEN \ - (sizeof(downstatelist) / sizeof(struct downstate)) - - -int mncc_tx_to_cc(struct gsm_network *net, int msg_type, void *arg) -{ - int i, rc = 0; - struct gsm_trans *trans = NULL, *transt; - struct gsm_subscriber_connection *conn = NULL; - struct gsm_bts *bts = NULL; - struct gsm_mncc *data = arg, rel; - - DEBUGP(DMNCC, "receive message %s\n", get_mncc_name(msg_type)); - - /* handle special messages */ - switch(msg_type) { - case MNCC_BRIDGE: - return tch_bridge(net, arg); - case MNCC_FRAME_DROP: - return tch_recv_mncc(net, data->callref, 0); - case MNCC_FRAME_RECV: - return tch_recv_mncc(net, data->callref, 1); - case GSM_TCHF_FRAME: - /* 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; - } - if (!trans->conn) { - LOGP(DMNCC, LOGL_NOTICE, "TCH frame for trans without conn\n"); - return 0; - } - if (trans->conn->lchan->type != GSM_LCHAN_TCH_F) { - /* 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\n"); - return 0; - } - bts = trans->conn->lchan->ts->trx->bts; - switch (bts->type) { - case GSM_BTS_TYPE_NANOBTS: - 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: - return trau_send_frame(trans->conn->lchan, arg); - default: - DEBUGP(DCC, "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, - data->called.number); - else - subscr = subscr_get_by_imsi(net, data->imsi); - /* 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(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)); - - /* Get a channel */ - trans->paging_request = talloc_zero(subscr->net, struct gsm_network*); - if (!trans->paging_request) { - LOGP(DCC, LOGL_ERROR, "Failed to allocate paging token.\n"); - subscr_put(subscr); - trans_free(trans); - return 0; - } - - *trans->paging_request = subscr->net; - subscr_get_channel(subscr, RSL_CHANNEED_TCH_F, setup_trig_pag_evt, trans->paging_request); - - subscr_put(subscr); - return 0; - } - /* Assign lchan */ - trans->conn = conn; - subscr_put(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 { - u_int32_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); - u_int8_t msg_type = gh->msg_type & 0xbf; - u_int8_t transaction_id = ((gh->proto_discr & 0xf0) ^ 0x80) >> 4; /* flip */ - struct gsm_trans *trans = NULL; - int i, rc = 0; - - if (msg_type & 0x80) { - DEBUGP(DCC, "MSG 0x%2x not defined for PD error\n", msg_type); - return -EINVAL; - } - - /* Find transaction */ - trans = trans_find_by_id(conn->subscr, 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->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; - } - - 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; - - bsc_del_timer(&conn->anch_operation->timeout); - talloc_free(conn->anch_operation); - conn->anch_operation = NULL; -} - -static void anchor_timeout(void *_data) -{ - struct gsm_subscriber_connection *con = _data; - - release_anchor(con); - msc_release_connection(con); -} - -int gsm0408_new_conn(struct gsm_subscriber_connection *conn) -{ - conn->anch_operation = talloc_zero(conn, struct gsm_anchor_operation); - if (!conn->anch_operation) - return -1; - - conn->anch_operation->timeout.data = conn; - conn->anch_operation->timeout.cb = anchor_timeout; - bsc_schedule_timer(&conn->anch_operation->timeout, 5, 0); - return 0; -} - -/* here we get data from the BSC level... */ -int gsm0408_dispatch(struct gsm_subscriber_connection *conn, struct msgb *msg) -{ - struct gsm48_hdr *gh = msgb_l3(msg); - u_int8_t pdisc = gh->proto_discr & 0x0f; - int rc = 0; - - if (silent_call_reroute(conn, msg)) - return silent_call_rx(conn, msg); - - switch (pdisc) { - case GSM48_PDISC_CC: - release_anchor(conn); - rc = gsm0408_rcv_cc(conn, msg); - break; - case GSM48_PDISC_MM: - rc = gsm0408_rcv_mm(conn, msg); - break; - case GSM48_PDISC_RR: - rc = gsm0408_rcv_rr(conn, msg); - break; - case GSM48_PDISC_SMS: - release_anchor(conn); - rc = gsm0411_rcv_sms(conn, msg); - break; - case GSM48_PDISC_MM_GPRS: - case GSM48_PDISC_SM_GPRS: - LOGP(DRLL, LOGL_NOTICE, "Unimplemented " - "GSM 04.08 discriminator 0x%02x\n", pdisc); - break; - case GSM48_PDISC_NC_SS: - release_anchor(conn); - rc = handle_rcv_ussd(conn, msg); - break; - default: - LOGP(DRLL, LOGL_NOTICE, "Unknown " - "GSM 04.08 discriminator 0x%02x\n", pdisc); - break; - } - - return rc; -} - -/* - * This will be ran by the linker when loading the DSO. We use it to - * do system initialization, e.g. registration of signal handlers. - */ -static __attribute__((constructor)) void on_dso_load_0408(void) -{ - register_signal_handler(SS_HO, handle_ho_signal, NULL); - register_signal_handler(SS_ABISIP, handle_abisip_signal, NULL); -} diff --git a/openbsc/src/msc/gsm_04_11.c b/openbsc/src/msc/gsm_04_11.c deleted file mode 100644 index 812e758cd..000000000 --- a/openbsc/src/msc/gsm_04_11.c +++ /dev/null @@ -1,1240 +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 - * (C) 2009 by Harald Welte - * (C) 2010 by Holger Hans Peter Freyther - * (C) 2010 by On-Waves - * - * All Rights Reserved - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation; either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - * - */ - - -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#define GSM411_ALLOC_SIZE 1024 -#define GSM411_ALLOC_HEADROOM 128 - -void *tall_gsms_ctx; -static u_int32_t new_callref = 0x40000001; - -static const struct value_string cp_cause_strs[] = { - { GSM411_CP_CAUSE_NET_FAIL, "Network Failure" }, - { GSM411_CP_CAUSE_CONGESTION, "Congestion" }, - { GSM411_CP_CAUSE_INV_TRANS_ID, "Invalid Transaction ID" }, - { GSM411_CP_CAUSE_SEMANT_INC_MSG, "Semantically Incorrect Message" }, - { GSM411_CP_CAUSE_INV_MAND_INF, "Invalid Mandatory Information" }, - { GSM411_CP_CAUSE_MSGTYPE_NOTEXIST, "Message Type doesn't exist" }, - { GSM411_CP_CAUSE_MSG_INCOMP_STATE, - "Message incompatible with protocol state" }, - { GSM411_CP_CAUSE_IE_NOTEXIST, "IE does not exist" }, - { GSM411_CP_CAUSE_PROTOCOL_ERR, "Protocol Error" }, - { 0, 0 } -}; - -static const struct value_string rp_cause_strs[] = { - { GSM411_RP_CAUSE_MO_NUM_UNASSIGNED, "(MO) Number not assigned" }, - { GSM411_RP_CAUSE_MO_OP_DET_BARR, "(MO) Operator determined barring" }, - { GSM411_RP_CAUSE_MO_CALL_BARRED, "(MO) Call barred" }, - { GSM411_RP_CAUSE_MO_SMS_REJECTED, "(MO) SMS rejected" }, - { GSM411_RP_CAUSE_MO_DEST_OUT_OF_ORDER, "(MO) Destination out of order" }, - { GSM411_RP_CAUSE_MO_UNIDENTIFIED_SUBSCR, "(MO) Unidentified subscriber" }, - { GSM411_RP_CAUSE_MO_FACILITY_REJ, "(MO) Facility reject" }, - { GSM411_RP_CAUSE_MO_UNKNOWN_SUBSCR, "(MO) Unknown subscriber" }, - { GSM411_RP_CAUSE_MO_NET_OUT_OF_ORDER, "(MO) Network out of order" }, - { GSM411_RP_CAUSE_MO_TEMP_FAIL, "(MO) Temporary failure" }, - { GSM411_RP_CAUSE_MO_CONGESTION, "(MO) Congestion" }, - { GSM411_RP_CAUSE_MO_RES_UNAVAIL, "(MO) Resource unavailable" }, - { GSM411_RP_CAUSE_MO_REQ_FAC_NOTSUBSCR, "(MO) Requested facility not subscribed" }, - { GSM411_RP_CAUSE_MO_REQ_FAC_NOTIMPL, "(MO) Requested facility not implemented" }, - { GSM411_RP_CAUSE_MO_INTERWORKING, "(MO) Interworking" }, - /* valid only for MT */ - { GSM411_RP_CAUSE_MT_MEM_EXCEEDED, "(MT) Memory Exceeded" }, - /* valid for both directions */ - { GSM411_RP_CAUSE_INV_TRANS_REF, "Invalid Transaction Reference" }, - { GSM411_RP_CAUSE_SEMANT_INC_MSG, "Semantically Incorrect Message" }, - { GSM411_RP_CAUSE_INV_MAND_INF, "Invalid Mandatory Information" }, - { GSM411_RP_CAUSE_MSGTYPE_NOTEXIST, "Message Type non-existant" }, - { GSM411_RP_CAUSE_MSG_INCOMP_STATE, "Message incompatible with protocol state" }, - { GSM411_RP_CAUSE_IE_NOTEXIST, "Information Element not existing" }, - { GSM411_RP_CAUSE_PROTOCOL_ERR, "Protocol Error" }, - { 0, NULL } -}; - - -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->sender) - subscr_put(sms->sender); - if (sms->receiver) - subscr_put(sms->receiver); - - talloc_free(sms); -} - -struct gsm_sms *sms_from_text(struct gsm_subscriber *receiver, int dcs, const char *text) -{ - struct gsm_sms *sms = sms_alloc(); - - if (!sms) - return NULL; - - sms->receiver = subscr_get(receiver); - strncpy(sms->text, text, sizeof(sms->text)-1); - - /* FIXME: don't use ID 1 static */ - sms->sender = subscr_get_by_id(receiver->net, 1); - sms->reply_path_req = 0; - sms->status_rep_req = 0; - sms->ud_hdr_ind = 0; - sms->protocol_id = 0; /* implicit */ - sms->data_coding_scheme = dcs; - strncpy(sms->dest_addr, receiver->extension, sizeof(sms->dest_addr)-1); - /* Generate user_data */ - sms->user_data_len = gsm_7bit_encode(sms->user_data, sms->text); - - 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; - dispatch_signal(SS_SMS, sig_no, &sig); -} - -/* - * This should be called whenever all SMS to a given subscriber - * on a given connection has been sent. This will inform the higher - * layers that a channel can be given up. - */ -static void gsm411_release_conn(struct gsm_subscriber_connection *conn) -{ - if (!conn) - return; - - subscr_put_channel(conn->subscr); -} - -struct msgb *gsm411_msgb_alloc(void) -{ - return msgb_alloc_headroom(GSM411_ALLOC_SIZE, GSM411_ALLOC_HEADROOM, - "GSM 04.11"); -} - -static int gsm411_sendmsg(struct gsm_subscriber_connection *conn, struct msgb *msg, u_int8_t link_id) -{ - DEBUGP(DSMS, "GSM4.11 TX %s\n", hexdump(msg->data, msg->len)); - msg->l3h = msg->data; - return gsm0808_submit_dtap(conn, msg, link_id, 1); -} - -/* SMC TC1* is expired */ -static void cp_timer_expired(void *data) -{ - struct gsm_trans *trans = data; - - DEBUGP(DSMS, "SMC Timer TC1* is expired, calling trans_free()\n"); - /* FIXME: we need to re-transmit the last CP-DATA 1..3 times */ - trans_free(trans); -} - -/* Prefix msg with a 04.08/04.11 CP header */ -static int gsm411_cp_sendmsg(struct msgb *msg, struct gsm_trans *trans, - u_int8_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; - - /* mobile originating */ - switch (gh->msg_type) { - case GSM411_MT_CP_DATA: - /* 5.2.3.1.2: enter MO-wait for CP-ack */ - /* 5.2.3.2.3: enter MT-wait for CP-ACK */ - trans->sms.cp_state = GSM411_CPS_WAIT_CP_ACK; - trans->sms.cp_timer.data = trans; - trans->sms.cp_timer.cb = cp_timer_expired; - /* 5.3.2.1: Set Timer TC1A */ - bsc_schedule_timer(&trans->sms.cp_timer, GSM411_TMR_TC1A); - DEBUGP(DSMS, "TX: CP-DATA "); - break; - case GSM411_MT_CP_ACK: - DEBUGP(DSMS, "TX: CP-ACK "); - break; - case GSM411_MT_CP_ERROR: - DEBUGP(DSMS, "TX: CP-ERROR "); - break; - } - - DEBUGPC(DSMS, "trans=%x\n", trans->transaction_id); - - return gsm411_sendmsg(trans->conn, msg, trans->sms.link_id); -} - -/* Prefix msg with a RP-DATA header and send as CP-DATA */ -static int gsm411_rp_sendmsg(struct msgb *msg, struct gsm_trans *trans, - u_int8_t rp_msg_type, u_int8_t rp_msg_ref) -{ - struct gsm411_rp_hdr *rp; - u_int8_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; /* FIXME: Choose randomly */ - - return gsm411_cp_sendmsg(msg, trans, GSM411_MT_CP_DATA); -} - -/* Turn int into semi-octet representation: 98 => 0x89 */ -static u_int8_t bcdify(u_int8_t value) -{ - u_int8_t ret; - - ret = value / 10; - ret |= (value % 10) << 4; - - return ret; -} - -/* Turn semi-octet representation into int: 0x89 => 98 */ -static u_int8_t unbcdify(u_int8_t value) -{ - u_int8_t ret; - - if ((value & 0x0F) > 9 || (value >> 4) > 9) - LOGP(DSMS, LOGL_ERROR, - "unbcdify got too big nibble: 0x%02X\n", value); - - ret = (value&0x0F)*10; - ret += value>>4; - - return ret; -} - -/* Generate 03.40 TP-SCTS */ -static void gsm340_gen_scts(u_int8_t *scts, time_t time) -{ - struct tm *tm = localtime(&time); - - *scts++ = bcdify(tm->tm_year % 100); - *scts++ = bcdify(tm->tm_mon + 1); - *scts++ = bcdify(tm->tm_mday); - *scts++ = bcdify(tm->tm_hour); - *scts++ = bcdify(tm->tm_min); - *scts++ = bcdify(tm->tm_sec); - *scts++ = bcdify(tm->tm_gmtoff/(60*15)); -} - -/* Decode 03.40 TP-SCTS (into utc/gmt timestamp) */ -static time_t gsm340_scts(u_int8_t *scts) -{ - struct tm tm; - - u_int8_t yr = unbcdify(*scts++); - - if (yr <= 80) - tm.tm_year = 100 + yr; - else - tm.tm_year = yr; - tm.tm_mon = unbcdify(*scts++) - 1; - tm.tm_mday = unbcdify(*scts++); - tm.tm_hour = unbcdify(*scts++); - tm.tm_min = unbcdify(*scts++); - tm.tm_sec = unbcdify(*scts++); - /* according to gsm 03.40 time zone is - "expressed in quarters of an hour" */ - tm.tm_gmtoff = unbcdify(*scts++) * 15*60; - - return mktime(&tm); -} - -/* Return the default validity period in minutes */ -static unsigned long gsm340_vp_default(void) -{ - unsigned long minutes; - /* Default validity: two days */ - minutes = 24 * 60 * 2; - return minutes; -} - -/* Decode validity period format 'relative' */ -static unsigned long gsm340_vp_relative(u_int8_t *sms_vp) -{ - /* Chapter 9.2.3.12.1 */ - u_int8_t vp; - unsigned long minutes; - - vp = *(sms_vp); - if (vp <= 143) - minutes = vp + 1 * 5; - else if (vp <= 167) - minutes = 12*60 + (vp-143) * 30; - else if (vp <= 196) - minutes = vp-166 * 60 * 24; - else - minutes = vp-192 * 60 * 24 * 7; - return minutes; -} - -/* Decode validity period format 'absolute' */ -static unsigned long gsm340_vp_absolute(u_int8_t *sms_vp) -{ - /* Chapter 9.2.3.12.2 */ - time_t expires, now; - unsigned long minutes; - - expires = gsm340_scts(sms_vp); - now = time(NULL); - if (expires <= now) - minutes = 0; - else - minutes = (expires-now)/60; - return minutes; -} - -/* Decode validity period format 'relative in integer representation' */ -static unsigned long gsm340_vp_relative_integer(u_int8_t *sms_vp) -{ - u_int8_t vp; - unsigned long minutes; - vp = *(sms_vp); - if (vp == 0) { - LOGP(DSMS, LOGL_ERROR, - "reserved relative_integer validity period\n"); - return gsm340_vp_default(); - } - minutes = vp/60; - return minutes; -} - -/* Decode validity period format 'relative in semi-octet representation' */ -static unsigned long gsm340_vp_relative_semioctet(u_int8_t *sms_vp) -{ - unsigned long minutes; - minutes = unbcdify(*sms_vp++)*60; /* hours */ - minutes += unbcdify(*sms_vp++); /* minutes */ - minutes += unbcdify(*sms_vp++)/60; /* seconds */ - return minutes; -} - -/* decode validity period. return minutes */ -static unsigned long gsm340_validity_period(u_int8_t sms_vpf, u_int8_t *sms_vp) -{ - u_int8_t fi; /* functionality indicator */ - - switch (sms_vpf) { - case GSM340_TP_VPF_RELATIVE: - return gsm340_vp_relative(sms_vp); - case GSM340_TP_VPF_ABSOLUTE: - return gsm340_vp_absolute(sms_vp); - case GSM340_TP_VPF_ENHANCED: - /* Chapter 9.2.3.12.3 */ - fi = *sms_vp++; - /* ignore additional fi */ - if (fi & (1<<7)) sms_vp++; - /* read validity period format */ - switch (fi & 0x7) { - case 0x0: - return gsm340_vp_default(); /* no vpf specified */ - case 0x1: - return gsm340_vp_relative(sms_vp); - case 0x2: - return gsm340_vp_relative_integer(sms_vp); - case 0x3: - return gsm340_vp_relative_semioctet(sms_vp); - default: - /* The GSM spec says that the SC should reject any - unsupported and/or undefined values. FIXME */ - LOGP(DSMS, LOGL_ERROR, - "Reserved enhanced validity period format\n"); - return gsm340_vp_default(); - } - case GSM340_TP_VPF_NONE: - default: - return gsm340_vp_default(); - } -} - -/* determine coding alphabet dependent on GSM 03.38 Section 4 DCS */ -enum sms_alphabet gsm338_get_sms_alphabet(u_int8_t dcs) -{ - u_int8_t cgbits = dcs >> 4; - enum sms_alphabet alpha = DCS_NONE; - - if ((cgbits & 0xc) == 0) { - if (cgbits & 2) { - LOGP(DSMS, LOGL_NOTICE, - "Compressed SMS not supported yet\n"); - return 0xffffffff; - } - - switch ((dcs >> 2)&0x03) { - case 0: - alpha = DCS_7BIT_DEFAULT; - break; - case 1: - alpha = DCS_8BIT_DATA; - break; - case 2: - alpha = DCS_UCS2; - break; - } - } else if (cgbits == 0xc || cgbits == 0xd) - alpha = DCS_7BIT_DEFAULT; - else if (cgbits == 0xe) - alpha = DCS_UCS2; - else if (cgbits == 0xf) { - if (dcs & 4) - alpha = DCS_8BIT_DATA; - else - alpha = DCS_7BIT_DEFAULT; - } - - return alpha; -} - -static int gsm340_rx_sms_submit(struct msgb *msg, struct gsm_sms *gsms) -{ - if (db_sms_store(gsms) != 0) { - LOGP(DSMS, 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(u_int8_t *oa, unsigned int oa_len, - struct gsm_subscriber *subscr) -{ - int len_in_bytes; - - oa[1] = 0xb9; /* networks-specific number, private numbering plan */ - - len_in_bytes = gsm48_encode_bcd_number(oa, oa_len, 1, subscr->extension); - - /* GSM 03.40 tells us the length is in 'useful semi-octets' */ - oa[0] = strlen(subscr->extension) & 0xff; - - return len_in_bytes; -} - -/* generate a msgb containing a TPDU derived from struct gsm_sms, - * returns total size of TPDU */ -static int gsm340_gen_tpdu(struct msgb *msg, struct gsm_sms *sms) -{ - u_int8_t *smsp; - u_int8_t oa[12]; /* max len per 03.40 */ - u_int8_t oa_len = 0; - u_int8_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(oa, sizeof(oa), sms->sender); - 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(DSMS, LOGL_NOTICE, "Unhandled Data Coding Scheme: 0x%02X\n", - sms->data_coding_scheme); - break; - } - - return msg->len - old_msg_len; -} - -/* process an incoming TPDU (called from RP-DATA) - * return value > 0: RP CAUSE for ERROR; < 0: silent error; 0 = success */ -static int gsm340_rx_tpdu(struct gsm_subscriber_connection *conn, struct msgb *msg) -{ - u_int8_t *smsp = msgb_sms(msg); - struct gsm_sms *gsms; - u_int8_t sms_mti, sms_mms, sms_vpf, sms_alphabet, sms_rp; - u_int8_t *sms_vp; - u_int8_t da_len_bytes; - u_int8_t address_lv[12]; /* according to 03.40 / 9.1.2.5 */ - int rc = 0; - - counter_inc(conn->bts->network->stats.sms.submitted); - - gsms = sms_alloc(); - if (!gsms) - return GSM411_RP_CAUSE_MO_NET_OUT_OF_ORDER; - - /* invert those fields where 0 means active/present */ - sms_mti = *smsp & 0x03; - sms_mms = !!(*smsp & 0x04); - sms_vpf = (*smsp & 0x18) >> 3; - gsms->status_rep_req = (*smsp & 0x20); - gsms->ud_hdr_ind = (*smsp & 0x40); - sms_rp = (*smsp & 0x80); - - smsp++; - gsms->msg_ref = *smsp++; - - /* length in bytes of the destination address */ - da_len_bytes = 2 + *smsp/2 + *smsp%2; - if (da_len_bytes > 12) { - LOGP(DSMS, LOGL_ERROR, "Destination Address > 12 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; - /* convert to real number */ - gsm48_decode_bcd_number(gsms->dest_addr, sizeof(gsms->dest_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) { - sms_free(gsms); - return GSM411_RP_CAUSE_MO_NET_OUT_OF_ORDER; - } - - 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(DSMS, LOGL_NOTICE, - "SMS Validity period not implemented: 0x%02x\n", sms_vpf); - return GSM411_RP_CAUSE_MO_NET_OUT_OF_ORDER; - } - 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(gsms->text, smsp, gsms->user_data_len); - break; - case DCS_8BIT_DATA: - case DCS_UCS2: - case DCS_NONE: - break; - } - } - - gsms->sender = subscr_get(conn->subscr); - - LOGP(DSMS, 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(gsms->sender), sms_mti, sms_vpf, gsms->msg_ref, - gsms->protocol_id, gsms->data_coding_scheme, gsms->dest_addr, - gsms->user_data_len, - sms_alphabet == DCS_7BIT_DEFAULT ? gsms->text : - 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); - - /* determine gsms->receiver based on dialled number */ - gsms->receiver = subscr_get_by_extension(conn->bts->network, gsms->dest_addr); - if (!gsms->receiver) { - rc = 1; /* cause 1: unknown subscriber */ - counter_inc(conn->bts->network->stats.sms.no_receiver); - goto out; - } - - 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(DSMS, LOGL_NOTICE, "Unimplemented MTI 0x%02x\n", sms_mti); - rc = GSM411_RP_CAUSE_IE_NOTEXIST; - break; - default: - LOGP(DSMS, 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; - -out: - sms_free(gsms); - - return rc; -} - -static int gsm411_send_rp_ack(struct gsm_trans *trans, u_int8_t msg_ref) -{ - struct msgb *msg = gsm411_msgb_alloc(); - - DEBUGP(DSMS, "TX: SMS RP ACK\n"); - - return gsm411_rp_sendmsg(msg, trans, GSM411_MT_RP_ACK_MT, msg_ref); -} - -static int gsm411_send_rp_error(struct gsm_trans *trans, - u_int8_t msg_ref, u_int8_t cause) -{ - struct msgb *msg = gsm411_msgb_alloc(); - - msgb_tv_put(msg, 1, cause); - - LOGP(DSMS, LOGL_NOTICE, "TX: SMS RP ERROR, cause %d (%s)\n", cause, - get_value_string(rp_cause_strs, cause)); - - return gsm411_rp_sendmsg(msg, trans, GSM411_MT_RP_ERROR_MT, msg_ref); -} - -/* 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, - u_int8_t src_len, u_int8_t *src, - u_int8_t dst_len, u_int8_t *dst, - u_int8_t tpdu_len, u_int8_t *tpdu) -{ - int rc = 0; - - if (src_len && src) - LOGP(DSMS, LOGL_ERROR, "RP-DATA (MO) with SRC ?!?\n"); - - if (!dst_len || !dst || !tpdu_len || !tpdu) { - LOGP(DSMS, 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(DSMS, "DST(%u,%s)\n", dst_len, hexdump(dst, dst_len)); - - rc = gsm340_rx_tpdu(trans->conn, msg); - if (rc == 0) - return gsm411_send_rp_ack(trans, rph->msg_ref); - else if (rc > 0) - return gsm411_send_rp_error(trans, rph->msg_ref, rc); - else - return rc; -} - -/* Receive a 04.11 RP-DATA message in accordance with Section 7.3.1.2 */ -static int gsm411_rx_rp_data(struct msgb *msg, struct gsm_trans *trans, - struct gsm411_rp_hdr *rph) -{ - u_int8_t src_len, dst_len, rpud_len; - u_int8_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(DSMS, "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 (!trans->sms.is_mt) { - LOGP(DSMS, LOGL_ERROR, "RX RP-ACK on a MO transfer ?\n"); - return gsm411_send_rp_error(trans, rph->msg_ref, - GSM411_RP_CAUSE_MSG_INCOMP_STATE); - } - - if (!sms) { - LOGP(DSMS, 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_sent(sms); - - send_signal(S_SMS_DELIVERED, trans, sms, 0); - - sms_free(sms); - trans->sms.sms = NULL; - - /* check for more messages for this subscriber */ - sms = db_sms_get_unsent_for_subscr(trans->subscr); - if (sms) - gsm411_send_sms(trans->conn, sms); - else - gsm411_release_conn(trans->conn); - - /* free the transaction here */ - trans_free(trans); - return 0; -} - -static int gsm411_rx_rp_error(struct msgb *msg, struct gsm_trans *trans, - struct gsm411_rp_hdr *rph) -{ - struct gsm_network *net = trans->conn->bts->network; - struct gsm_sms *sms = trans->sms.sms; - u_int8_t cause_len = rph->data[0]; - u_int8_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(DSMS, LOGL_NOTICE, "%s: RX SMS RP-ERROR, cause %d:%d (%s)\n", - subscr_name(trans->conn->subscr), cause_len, cause, - get_value_string(rp_cause_strs, cause)); - - if (!trans->sms.is_mt) { - LOGP(DSMS, LOGL_ERROR, "RX RP-ERR on a MO transfer ?\n"); -#if 0 - return gsm411_send_rp_error(trans, rph->msg_ref, - GSM411_RP_CAUSE_MSG_INCOMP_STATE); -#endif - } - - if (!sms) { - LOGP(DSMS, 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); - counter_inc(net->stats.sms.rp_err_mem); - } else { - send_signal(S_SMS_UNKNOWN_ERROR, trans, sms, 0); - counter_inc(net->stats.sms.rp_err_other); - } - - sms_free(sms); - trans->sms.sms = NULL; - - return 0; -} - -static int gsm411_rx_rp_smma(struct msgb *msg, struct gsm_trans *trans, - struct gsm411_rp_hdr *rph) -{ - struct gsm_sms *sms; - int rc; - - rc = gsm411_send_rp_ack(trans, rph->msg_ref); - trans->sms.rp_state = GSM411_RPS_IDLE; - - /* 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); - - /* check for more messages for this subscriber */ - sms = db_sms_get_unsent_for_subscr(trans->subscr); - if (sms) - gsm411_send_sms(trans->conn, sms); - else - gsm411_release_conn(trans->conn); - - return rc; -} - -static int gsm411_rx_cp_data(struct msgb *msg, struct gsm48_hdr *gh, - struct gsm_trans *trans) -{ - struct gsm411_rp_hdr *rp_data = (struct gsm411_rp_hdr*)&gh->data; - u_int8_t msg_type = rp_data->msg_type & 0x07; - int rc = 0; - - switch (msg_type) { - case GSM411_MT_RP_DATA_MO: - DEBUGP(DSMS, "RX SMS RP-DATA (MO)\n"); - /* start TR2N and enter 'wait to send RP-ACK state' */ - trans->sms.rp_state = GSM411_RPS_WAIT_TO_TX_RP_ACK; - rc = gsm411_rx_rp_data(msg, trans, rp_data); - break; - case GSM411_MT_RP_ACK_MO: - DEBUGP(DSMS,"RX SMS RP-ACK (MO)\n"); - rc = gsm411_rx_rp_ack(msg, trans, rp_data); - break; - case GSM411_MT_RP_SMMA_MO: - DEBUGP(DSMS, "RX SMS RP-SMMA\n"); - /* start TR2N and enter 'wait to send RP-ACK state' */ - trans->sms.rp_state = GSM411_RPS_WAIT_TO_TX_RP_ACK; - rc = gsm411_rx_rp_smma(msg, trans, rp_data); - break; - case GSM411_MT_RP_ERROR_MO: - rc = gsm411_rx_rp_error(msg, trans, rp_data); - break; - default: - LOGP(DSMS, LOGL_NOTICE, "Invalid RP type 0x%02x\n", msg_type); - rc = gsm411_send_rp_error(trans, rp_data->msg_ref, - GSM411_RP_CAUSE_MSGTYPE_NOTEXIST); - break; - } - - return rc; -} - -/* send CP-ACK to given transaction */ -static int gsm411_tx_cp_ack(struct gsm_trans *trans) -{ - struct msgb *msg = gsm411_msgb_alloc(); - int rc; - - rc = gsm411_cp_sendmsg(msg, trans, GSM411_MT_CP_ACK); - - if (trans->sms.is_mt) { - /* If this is a MT SMS DELIVER, we can clear transaction here */ - trans->sms.cp_state = GSM411_CPS_IDLE; - //trans_free(trans); - } - - return rc; -} - -static int gsm411_tx_cp_error(struct gsm_trans *trans, u_int8_t cause) -{ - struct msgb *msg = gsm411_msgb_alloc(); - u_int8_t *causep; - - LOGP(DSMS, LOGL_NOTICE, "TX CP-ERROR, cause %d (%s)\n", cause, - get_value_string(cp_cause_strs, cause)); - - causep = msgb_put(msg, 1); - *causep = cause; - - return gsm411_cp_sendmsg(msg, trans, GSM411_MT_CP_ERROR); -} - -/* 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); - u_int8_t msg_type = gh->msg_type; - u_int8_t transaction_id = ((gh->proto_discr >> 4) ^ 0x8); /* flip */ - struct gsm_trans *trans; - int rc = 0; - - if (!conn->subscr) - return -EIO; - /* FIXME: send some error message */ - - DEBUGP(DSMS, "trans_id=%x ", transaction_id); - trans = trans_find_by_id(conn->subscr, GSM48_PDISC_SMS, - transaction_id); - if (!trans) { - DEBUGPC(DSMS, "(new) "); - trans = trans_alloc(conn->subscr, GSM48_PDISC_SMS, - transaction_id, new_callref++); - if (!trans) { - DEBUGPC(DSMS, "No memory for trans\n"); - /* FIXME: send some error message */ - return -ENOMEM; - } - trans->sms.cp_state = GSM411_CPS_IDLE; - trans->sms.rp_state = GSM411_RPS_IDLE; - trans->sms.is_mt = 0; - trans->sms.link_id = UM_SAPI_SMS; - - trans->conn = conn; - } - - switch(msg_type) { - case GSM411_MT_CP_DATA: - DEBUGPC(DSMS, "RX SMS CP-DATA\n"); - - /* 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.cp_state == GSM411_CPS_IDLE) { - 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->subscr, - GSM48_PDISC_SMS, i); - if (!ptrans) - continue; - - DEBUGP(DSMS, "Implicit CP-ACK for trans_id=%x\n", i); - - /* Finish it for good */ - bsc_del_timer(&ptrans->sms.cp_timer); - ptrans->sms.cp_state = GSM411_CPS_IDLE; - trans_free(ptrans); - } - } - - /* 5.2.3.1.3: MO state exists when SMC has received - * CP-DATA, including sending of the assoc. CP-ACK */ - /* 5.2.3.2.4: MT state exists when SMC has received - * CP-DATA, including sending of the assoc. CP-ACK */ - trans->sms.cp_state = GSM411_CPS_MM_ESTABLISHED; - - /* SMC instance acknowledges the CP-DATA frame */ - gsm411_tx_cp_ack(trans); - - rc = gsm411_rx_cp_data(msg, gh, trans); -#if 0 - /* Send CP-ACK or CP-ERORR in response */ - if (rc < 0) { - rc = gsm411_tx_cp_error(trans, GSM411_CP_CAUSE_NET_FAIL); - } else - rc = gsm411_tx_cp_ack(trans); -#endif - break; - case GSM411_MT_CP_ACK: - /* previous CP-DATA in this transaction was confirmed */ - DEBUGPC(DSMS, "RX SMS CP-ACK\n"); - /* 5.2.3.1.3: MO state exists when SMC has received CP-ACK */ - /* 5.2.3.2.4: MT state exists when SMC has received CP-ACK */ - trans->sms.cp_state = GSM411_CPS_MM_ESTABLISHED; - /* Stop TC1* after CP-ACK has been received */ - bsc_del_timer(&trans->sms.cp_timer); - - if (!trans->sms.is_mt) { - /* FIXME: we have sent one CP-DATA, which was now - * acknowledged. Check if we want to transfer more, - * i.e. multi-part message */ - trans->sms.cp_state = GSM411_CPS_IDLE; - trans_free(trans); - } - break; - case GSM411_MT_CP_ERROR: - DEBUGPC(DSMS, "RX SMS CP-ERROR, cause %d (%s)\n", gh->data[0], - get_value_string(cp_cause_strs, gh->data[0])); - bsc_del_timer(&trans->sms.cp_timer); - trans->sms.cp_state = GSM411_CPS_IDLE; - trans_free(trans); - break; - default: - DEBUGPC(DSMS, "RX Unimplemented CP msg_type: 0x%02x\n", msg_type); - rc = gsm411_tx_cp_error(trans, GSM411_CP_CAUSE_MSGTYPE_NOTEXIST); - trans->sms.cp_state = GSM411_CPS_IDLE; - trans_free(trans); - break; - } - - 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; - u_int8_t *data, *rp_ud_len; - u_int8_t msg_ref = 42; - int transaction_id; - int rc; - - transaction_id = trans_assign_trans_id(conn->subscr, GSM48_PDISC_SMS, 0); - if (transaction_id == -1) { - LOGP(DSMS, LOGL_ERROR, "No available transaction ids\n"); - send_signal(S_SMS_UNKNOWN_ERROR, NULL, sms, 0); - sms_free(sms); - return -EBUSY; - } - - DEBUGP(DSMS, "send_sms_lchan()\n"); - - /* FIXME: allocate transaction with message reference */ - trans = trans_alloc(conn->subscr, GSM48_PDISC_SMS, - transaction_id, new_callref++); - if (!trans) { - LOGP(DSMS, LOGL_ERROR, "No memory for trans\n"); - send_signal(S_SMS_UNKNOWN_ERROR, NULL, sms, 0); - sms_free(sms); - /* FIXME: send some error message */ - return -ENOMEM; - } - trans->sms.cp_state = GSM411_CPS_IDLE; - trans->sms.rp_state = GSM411_RPS_IDLE; - trans->sms.is_mt = 1; - trans->sms.sms = sms; - trans->sms.link_id = UM_SAPI_SMS; /* FIXME: main or SACCH ? */ - - trans->conn = conn; - - /* Hardcode SMSC Originating Address for now */ - data = (u_int8_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 = (u_int8_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 = (u_int8_t *)msgb_put(msg, 1); - - /* generate the 03.40 TPDU */ - rc = gsm340_gen_tpdu(msg, sms); - if (rc < 0) { - send_signal(S_SMS_UNKNOWN_ERROR, trans, sms, 0); - trans_free(trans); - sms_free(sms); - msgb_free(msg); - return rc; - } - - *rp_ud_len = rc; - - DEBUGP(DSMS, "TX: SMS DELIVER\n"); - - counter_inc(conn->bts->network->stats.sms.delivered); - db_sms_inc_deliver_attempts(trans->sms.sms); - - return gsm411_rp_sendmsg(msg, trans, GSM411_MT_RP_DATA_MT, msg_ref); - /* FIXME: enter 'wait for RP-ACK' state, start TR1N */ -} - -/* 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(DSMS, "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(DSMS, 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; - - /* check if we already have an open lchan to the subscriber. - * if yes, send the SMS this way */ - conn = connection_for_subscr(subscr); - if (conn) { - return gsm411_send_sms(conn, sms); - } - - /* if not, we have to start paging */ - subscr_get_channel(subscr, RSL_CHANNEED_SDCCH, paging_cb_send_sms, sms); - return 0; -} - -void _gsm411_sms_trans_free(struct gsm_trans *trans) -{ - if (trans->sms.sms) { - LOGP(DSMS, 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; - } - - bsc_del_timer(&trans->sms.cp_timer); -} - -void gsm411_sapi_n_reject(struct gsm_subscriber_connection *conn) -{ - struct gsm_subscriber *subscr; - struct gsm_network *net; - struct gsm_trans *trans, *tmp; - - subscr = subscr_get(conn->subscr); - net = conn->bts->network; - - llist_for_each_entry_safe(trans, tmp, &net->trans_list, entry) - if (trans->conn == conn) { - struct gsm_sms *sms = trans->sms.sms; - if (!sms) { - LOGP(DSMS, 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); - } - - subscr_put_channel(subscr); - subscr_put(subscr); -} - diff --git a/openbsc/src/msc/gsm_04_80.c b/openbsc/src/msc/gsm_04_80.c deleted file mode 100644 index 494c319fe..000000000 --- a/openbsc/src/msc/gsm_04_80.c +++ /dev/null @@ -1,175 +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 - * (C) 2008, 2009, 2010 by Holger Hans Peter Freyther - * (C) 2009 by Mike Haben - * - * All Rights Reserved - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation; either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - * - */ - - -#include -#include -#include -#include - -#include -#include -#include -#include -#include - -#include -#include -#include -#include - -static inline unsigned char *msgb_wrap_with_TL(struct msgb *msgb, u_int8_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, u_int8_t tag, - u_int8_t value) -{ - uint8_t *data = msgb_push(msgb, 3); - - data[0] = tag; - data[1] = 1; - data[2] = value; - return data; -} - - -/* Send response to a mobile-originated ProcessUnstructuredSS-Request */ -int gsm0480_send_ussd_response(struct gsm_subscriber_connection *conn, - const struct msgb *in_msg, const char *response_text, - const struct ussd_request *req) -{ - struct msgb *msg = gsm48_msgb_alloc(); - struct gsm48_hdr *gh; - u_int8_t *ptr8; - int response_len; - - /* First put the payload text into the message */ - ptr8 = msgb_put(msg, 0); - response_len = gsm_7bit_encode(ptr8, response_text); - msgb_put(msg, response_len); - - /* Then wrap it as an Octet String */ - msgb_wrap_with_TL(msg, ASN1_OCTET_STRING_TAG); - - /* Pre-pend the DCS octet string */ - msgb_push_TLV1(msg, ASN1_OCTET_STRING_TAG, 0x0F); - - /* Then wrap these as a Sequence */ - msgb_wrap_with_TL(msg, GSM_0480_SEQUENCE_TAG); - - /* Pre-pend the operation code */ - msgb_push_TLV1(msg, GSM0480_OPERATION_CODE, - GSM0480_OP_CODE_PROCESS_USS_REQ); - - /* Wrap the operation code and IA5 string as a sequence */ - msgb_wrap_with_TL(msg, GSM_0480_SEQUENCE_TAG); - - /* Pre-pend the invoke ID */ - msgb_push_TLV1(msg, GSM0480_COMPIDTAG_INVOKE_ID, req->invoke_id); - - /* Wrap this up as a Return Result component */ - msgb_wrap_with_TL(msg, GSM0480_CTYPE_RETURN_RESULT); - - /* Wrap the component in a Facility message */ - msgb_wrap_with_TL(msg, GSM0480_IE_FACILITY); - - /* And finally pre-pend the L3 header */ - gh = (struct gsm48_hdr *) msgb_push(msg, sizeof(*gh)); - gh->proto_discr = GSM48_PDISC_NC_SS | req->transaction_id - | (1<<7); /* TI direction = 1 */ - gh->msg_type = GSM0480_MTYPE_RELEASE_COMPLETE; - - return gsm0808_submit_dtap(conn, msg, 0, 0); -} - -int gsm0480_send_ussd_reject(struct gsm_subscriber_connection *conn, - const struct msgb *in_msg, - const struct ussd_request *req) -{ - struct msgb *msg = gsm48_msgb_alloc(); - struct gsm48_hdr *gh; - - /* First insert the problem code */ - msgb_push_TLV1(msg, GSM_0480_PROBLEM_CODE_TAG_GENERAL, - GSM_0480_GEN_PROB_CODE_UNRECOGNISED); - - /* Before it insert the invoke ID */ - msgb_push_TLV1(msg, GSM0480_COMPIDTAG_INVOKE_ID, req->invoke_id); - - /* Wrap this up as a Reject component */ - msgb_wrap_with_TL(msg, GSM0480_CTYPE_REJECT); - - /* Wrap the component in a Facility message */ - msgb_wrap_with_TL(msg, GSM0480_IE_FACILITY); - - /* And finally pre-pend the L3 header */ - gh = (struct gsm48_hdr *) msgb_push(msg, sizeof(*gh)); - gh->proto_discr = GSM48_PDISC_NC_SS; - gh->proto_discr |= req->transaction_id | (1<<7); /* TI direction = 1 */ - gh->msg_type = GSM0480_MTYPE_RELEASE_COMPLETE; - - return gsm0808_submit_dtap(conn, msg, 0, 0); -} - -int gsm0480_send_ussdNotify(struct gsm_subscriber_connection *conn, int level, const char *text) -{ - struct gsm48_hdr *gh; - struct msgb *msg; - - msg = gsm0480_create_unstructuredSS_Notify(level, text); - if (!msg) - return -1; - - gsm0480_wrap_invoke(msg, GSM0480_OP_CODE_USS_NOTIFY, 0); - gsm0480_wrap_facility(msg); - - /* And finally pre-pend the L3 header */ - gh = (struct gsm48_hdr *) msgb_push(msg, sizeof(*gh)); - gh->proto_discr = GSM48_PDISC_NC_SS; - gh->msg_type = GSM0480_MTYPE_REGISTER; - - return gsm0808_submit_dtap(conn, msg, 0, 0); -} - -int gsm0480_send_releaseComplete(struct gsm_subscriber_connection *conn) -{ - struct gsm48_hdr *gh; - struct msgb *msg; - - msg = gsm48_msgb_alloc(); - if (!msg) - return -1; - - gh = (struct gsm48_hdr *) msgb_push(msg, sizeof(*gh)); - gh->proto_discr = GSM48_PDISC_NC_SS; - gh->msg_type = GSM0480_MTYPE_RELEASE_COMPLETE; - - return gsm0808_submit_dtap(conn, msg, 0, 0); -} diff --git a/openbsc/src/msc/gsm_subscriber.c b/openbsc/src/msc/gsm_subscriber.c deleted file mode 100644 index db61f25aa..000000000 --- a/openbsc/src/msc/gsm_subscriber.c +++ /dev/null @@ -1,410 +0,0 @@ -/* The concept of a subscriber for the MSC, roughly HLR/VLR functionality */ - -/* (C) 2008 by Harald Welte - * (C) 2009 by Holger Hans Peter Freyther - * - * All Rights Reserved - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation; either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - * - */ - -#include -#include -#include -#include -#include - -#include - -#include - -#include -#include -#include -#include -#include -#include - -void *tall_sub_req_ctx; - -extern struct llist_head *subscr_bsc_active_subscriber(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; - - /* back reference */ - struct gsm_subscriber *subscr; - - /* the requested channel type */ - int channel_type; - - /* what did we do */ - int state; - - /* the callback data */ - gsm_cbfn *cbfn; - void *param; -}; - -enum { - REQ_STATE_INITIAL, - REQ_STATE_QUEUED, - REQ_STATE_PAGED, - REQ_STATE_FAILED_START, - REQ_STATE_DISPATCHED, -}; - -/* - * 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; - struct gsm_subscriber_connection *conn = data; - struct gsm_subscriber *subscr = param; - struct paging_signal_data sig_data; - - /* There is no request anymore... */ - if (llist_empty(&subscr->requests)) - return -1; - - /* Dispatch signal */ - sig_data.subscr = subscr; - sig_data.bts = conn ? conn->bts : NULL; - sig_data.conn = conn; - sig_data.paging_result = event; - dispatch_signal( - SS_PAGING, - event == GSM_PAGING_SUCCEEDED ? - S_PAGING_SUCCEEDED : S_PAGING_EXPIRED, - &sig_data - ); - - /* - * FIXME: What to do with paging requests coming during - * this callback? We must be sure to not start paging when - * we have an active connection to a subscriber and to make - * the subscr_put_channel work as required... - */ - request = (struct subscr_request *)subscr->requests.next; - request->state = REQ_STATE_DISPATCHED; - llist_del(&request->entry); - subscr->in_callback = 1; - request->cbfn(hooknum, event, msg, data, request->param); - subscr->in_callback = 0; - - if (event != GSM_PAGING_SUCCEEDED) { - /* - * This is a workaround for a bigger issue. We have - * issued paging that might involve multiple BTSes - * and one of them have failed now. We will stop the - * other paging requests as well as the next timeout - * would work on the next paging request and the queue - * will do bad things. This should be fixed by counting - * the outstanding results. - */ - paging_request_stop(NULL, subscr, NULL, NULL); - subscr_put_channel(subscr); - } - - subscr_put(subscr); - talloc_free(request); - 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); -} - - -static void subscr_send_paging_request(struct gsm_subscriber *subscr) -{ - struct subscr_request *request; - int rc; - - assert(!llist_empty(&subscr->requests)); - - request = (struct subscr_request *)subscr->requests.next; - request->state = REQ_STATE_PAGED; - rc = paging_request(subscr->net, subscr, request->channel_type, - subscr_paging_cb, subscr); - - /* paging failed, quit now */ - if (rc <= 0) { - request->state = REQ_STATE_FAILED_START; - subscr_paging_cb(GSM_HOOK_RR_PAGING, GSM_PAGING_BUSY, - NULL, NULL, subscr); - } -} - -void subscr_get_channel(struct gsm_subscriber *subscr, - int type, gsm_cbfn *cbfn, void *param) -{ - struct subscr_request *request; - - request = talloc(tall_sub_req_ctx, struct subscr_request); - if (!request) { - if (cbfn) - cbfn(GSM_HOOK_RR_PAGING, GSM_PAGING_OOM, - NULL, NULL, param); - return; - } - - memset(request, 0, sizeof(*request)); - request->subscr = subscr_get(subscr); - request->channel_type = type; - request->cbfn = cbfn; - request->param = param; - request->state = REQ_STATE_INITIAL; - - /* - * FIXME: We might be able to assign more than one - * channel, e.g. voice and SMS submit at the same - * time. - */ - if (!subscr->in_callback && llist_empty(&subscr->requests)) { - /* add to the list, send a request */ - llist_add_tail(&request->entry, &subscr->requests); - subscr_send_paging_request(subscr); - } else { - /* this will be picked up later, from subscr_put_channel */ - llist_add_tail(&request->entry, &subscr->requests); - request->state = REQ_STATE_QUEUED; - } -} - -void subscr_put_channel(struct gsm_subscriber *subscr) -{ - /* - * FIXME: Continue with other requests now... by checking - * the gsm_subscriber inside the gsm_lchan. Drop the ref count - * of the lchan after having asked the next requestee to handle - * the channel. - */ - /* - * FIXME: is the lchan is of a different type we could still - * issue an immediate assignment for another channel and then - * close this one. - */ - /* - * Currently we will drop the last ref of the lchan which - * will result in a channel release on RSL and we will start - * the paging. This should work most of the time as the MS - * will listen to the paging requests before we timeout - */ - - if (subscr && !llist_empty(&subscr->requests)) - subscr_send_paging_request(subscr); -} - - -struct gsm_subscriber *subscr_get_by_tmsi(struct gsm_network *net, - u_int32_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_subscriber(), entry) { - if (tmsi == subscr->tmsi) - return subscr_get(subscr); - } - - sprintf(tmsi_string, "%u", tmsi); - return db_get_subscriber(net, GSM_SUBSCRIBER_TMSI, tmsi_string); -} - -struct gsm_subscriber *subscr_get_by_imsi(struct gsm_network *net, - const char *imsi) -{ - struct gsm_subscriber *subscr; - - llist_for_each_entry(subscr, subscr_bsc_active_subscriber(), entry) { - if (strcmp(subscr->imsi, imsi) == 0) - return subscr_get(subscr); - } - - return db_get_subscriber(net, GSM_SUBSCRIBER_IMSI, imsi); -} - -struct gsm_subscriber *subscr_get_by_extension(struct gsm_network *net, - const char *ext) -{ - struct gsm_subscriber *subscr; - - llist_for_each_entry(subscr, subscr_bsc_active_subscriber(), entry) { - if (strcmp(subscr->extension, ext) == 0) - return subscr_get(subscr); - } - - return db_get_subscriber(net, GSM_SUBSCRIBER_EXTENSION, ext); -} - -struct gsm_subscriber *subscr_get_by_id(struct gsm_network *net, - unsigned long long id) -{ - struct gsm_subscriber *subscr; - char buf[32]; - sprintf(buf, "%llu", id); - - llist_for_each_entry(subscr, subscr_bsc_active_subscriber(), entry) { - if (subscr->id == id) - return subscr_get(subscr); - } - - return db_get_subscriber(net, GSM_SUBSCRIBER_ID, buf); -} - - -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->net = bts->network; - /* 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); - rc = db_sync_subscriber(s); - db_subscriber_update(s); - dispatch_signal(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); - dispatch_signal(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); -} - -int subscr_pending_requests(struct gsm_subscriber *sub) -{ - struct subscr_request *req; - int pending = 0; - - llist_for_each_entry(req, &sub->requests, entry) - pending += 1; - - return pending; -} - -int subscr_pending_clear(struct gsm_subscriber *sub) -{ - int deleted = 0; - struct subscr_request *req, *tmp; - - llist_for_each_entry_safe(req, tmp, &sub->requests, entry) { - subscr_put(req->subscr); - llist_del(&req->entry); - talloc_free(req); - deleted += 1; - } - - return deleted; -} - -int subscr_pending_dump(struct gsm_subscriber *sub, struct vty *vty) -{ - struct subscr_request *req; - - vty_out(vty, "Pending Requests for Subscriber %llu.%s", sub->id, VTY_NEWLINE); - llist_for_each_entry(req, &sub->requests, entry) { - vty_out(vty, "Channel type: %d State: %d Sub: %llu.%s", - req->channel_type, req->state, req->subscr->id, VTY_NEWLINE); - } - - return 0; -} - -int subscr_pending_kick(struct gsm_subscriber *sub) -{ - subscr_put_channel(sub); - return 0; -} diff --git a/openbsc/src/msc/mncc.c b/openbsc/src/msc/mncc.c deleted file mode 100644 index 3630b91b4..000000000 --- a/openbsc/src/msc/mncc.c +++ /dev/null @@ -1,110 +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 - * (C) 2009 by Andreas Eversberg - * All Rights Reserved - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation; either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - * - */ - - -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include - -static struct mncc_names { - char *name; - int value; -} mncc_names[] = { - {"MNCC_SETUP_REQ", 0x0101}, - {"MNCC_SETUP_IND", 0x0102}, - {"MNCC_SETUP_RSP", 0x0103}, - {"MNCC_SETUP_CNF", 0x0104}, - {"MNCC_SETUP_COMPL_REQ",0x0105}, - {"MNCC_SETUP_COMPL_IND",0x0106}, - {"MNCC_CALL_CONF_IND", 0x0107}, - {"MNCC_CALL_PROC_REQ", 0x0108}, - {"MNCC_PROGRESS_REQ", 0x0109}, - {"MNCC_ALERT_REQ", 0x010a}, - {"MNCC_ALERT_IND", 0x010b}, - {"MNCC_NOTIFY_REQ", 0x010c}, - {"MNCC_NOTIFY_IND", 0x010d}, - {"MNCC_DISC_REQ", 0x010e}, - {"MNCC_DISC_IND", 0x010f}, - {"MNCC_REL_REQ", 0x0110}, - {"MNCC_REL_IND", 0x0111}, - {"MNCC_REL_CNF", 0x0112}, - {"MNCC_FACILITY_REQ", 0x0113}, - {"MNCC_FACILITY_IND", 0x0114}, - {"MNCC_START_DTMF_IND", 0x0115}, - {"MNCC_START_DTMF_RSP", 0x0116}, - {"MNCC_START_DTMF_REJ", 0x0117}, - {"MNCC_STOP_DTMF_IND", 0x0118}, - {"MNCC_STOP_DTMF_RSP", 0x0119}, - {"MNCC_MODIFY_REQ", 0x011a}, - {"MNCC_MODIFY_IND", 0x011b}, - {"MNCC_MODIFY_RSP", 0x011c}, - {"MNCC_MODIFY_CNF", 0x011d}, - {"MNCC_MODIFY_REJ", 0x011e}, - {"MNCC_HOLD_IND", 0x011f}, - {"MNCC_HOLD_CNF", 0x0120}, - {"MNCC_HOLD_REJ", 0x0121}, - {"MNCC_RETRIEVE_IND", 0x0122}, - {"MNCC_RETRIEVE_CNF", 0x0123}, - {"MNCC_RETRIEVE_REJ", 0x0124}, - {"MNCC_USERINFO_REQ", 0x0125}, - {"MNCC_USERINFO_IND", 0x0126}, - {"MNCC_REJ_REQ", 0x0127}, - {"MNCC_REJ_IND", 0x0128}, - - {"MNCC_BRIDGE", 0x0200}, - {"MNCC_FRAME_RECV", 0x0201}, - {"MNCC_FRAME_DROP", 0x0202}, - {"MNCC_LCHAN_MODIFY", 0x0203}, - - {"GSM_TCH_FRAME", 0x0300}, - - {NULL, 0} }; - -char *get_mncc_name(int value) -{ - int i; - - for (i = 0; mncc_names[i].name; i++) { - if (mncc_names[i].value == value) - return mncc_names[i].name; - } - - return "MNCC_Unknown"; -} - -void mncc_set_cause(struct gsm_mncc *data, int loc, int val) -{ - data->fields |= MNCC_F_CAUSE; - data->cause.location = loc; - data->cause.value = val; -} - diff --git a/openbsc/src/msc/mncc_builtin.c b/openbsc/src/msc/mncc_builtin.c deleted file mode 100644 index 0226b2748..000000000 --- a/openbsc/src/msc/mncc_builtin.c +++ /dev/null @@ -1,411 +0,0 @@ -/* mncc_builtin.c - default, minimal built-in MNCC Application for - * standalone bsc_hack (netowrk-in-the-box mode) */ - -/* (C) 2008-2010 by Harald Welte - * (C) 2009 by Andreas Eversberg - * All Rights Reserved - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - * - */ - - -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include - -void *tall_call_ctx; - -static LLIST_HEAD(call_list); - -static u_int32_t new_callref = 0x00000001; - -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(u_int32_t callref) -{ - struct gsm_call *callt; - - llist_for_each_entry(callt, &call_list, entry) { - if (callt->callref == callref) - return callt; - } - return NULL; -} - - -/* on incoming call, look up database and send setup to remote subscr. */ -static int mncc_setup_ind(struct gsm_call *call, int msg_type, - struct gsm_mncc *setup) -{ - struct gsm_mncc mncc; - struct gsm_call *remote; - - memset(&mncc, 0, sizeof(struct gsm_mncc)); - mncc.callref = call->callref; - - /* already have remote call */ - if (call->remote_ref) - return 0; - - /* transfer mode 1 would be packet mode, which was never specified */ - if (setup->bearer_cap.mode != 0) { - LOGP(DMNCC, LOGL_NOTICE, "(call %x) We don't support " - "packet mode\n", call->callref); - mncc_set_cause(&mncc, GSM48_CAUSE_LOC_PRN_S_LU, - GSM48_CC_CAUSE_BEARER_CA_UNAVAIL); - goto out_reject; - } - - /* we currently only do speech */ - if (setup->bearer_cap.transfer != GSM_MNCC_BCAP_SPEECH) { - LOGP(DMNCC, LOGL_NOTICE, "(call %x) We only support " - "voice calls\n", call->callref); - mncc_set_cause(&mncc, GSM48_CAUSE_LOC_PRN_S_LU, - GSM48_CC_CAUSE_BEARER_CA_UNAVAIL); - goto out_reject; - } - - /* create remote call */ - if (!(remote = talloc(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; - - /* modify mode */ - memset(&mncc, 0, sizeof(struct gsm_mncc)); - mncc.callref = call->callref; - mncc.lchan_mode = GSM48_CMODE_SPEECH_EFR; - DEBUGP(DMNCC, "(call %x) Modify channel mode.\n", call->callref); - mncc_tx_to_cc(call->net, MNCC_LCHAN_MODIFY, &mncc); - - /* 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); - - /* 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; - u_int32_t refs[2]; - - /* acknowledge connect */ - memset(&connect_ack, 0, sizeof(struct gsm_mncc)); - connect_ack.callref = call->callref; - DEBUGP(DMNCC, "(call %x) Acknowledge SETUP.\n", call->callref); - mncc_tx_to_cc(call->net, MNCC_SETUP_COMPL_REQ, &connect_ack); - - /* send connect message to remote */ - if (!(remote = get_call_ref(call->remote_ref))) - return 0; - connect->callref = remote->callref; - DEBUGP(DMNCC, "(call %x) Sending CONNECT to remote.\n", call->callref); - mncc_tx_to_cc(remote->net, MNCC_SETUP_RSP, connect); - - /* bridge tch */ - refs[0] = call->callref; - refs[1] = call->remote_ref; - DEBUGP(DMNCC, "(call %x) Bridging with remote.\n", call->callref); - - /* in direct mode, we always have to bridge the channels */ - if (ipacc_rtp_direct) - return mncc_tx_to_cc(call->net, MNCC_BRIDGE, refs); - - /* proxy mode */ - if (!net->handover.active) { - /* in the no-handover case, we can bridge, i.e. use - * the old RTP proxy code */ - return mncc_tx_to_cc(call->net, MNCC_BRIDGE, refs); - } else { - /* in case of handover, we need to re-write the RTP - * SSRC, sequence and timestamp values and thus - * need to enable RTP receive for both directions */ - memset(&frame_recv, 0, sizeof(struct gsm_mncc)); - frame_recv.callref = call->callref; - mncc_tx_to_cc(call->net, MNCC_FRAME_RECV, &frame_recv); - frame_recv.callref = call->remote_ref; - return mncc_tx_to_cc(call->net, MNCC_FRAME_RECV, &frame_recv); - } -} - -static int mncc_disc_ind(struct gsm_call *call, int msg_type, - struct gsm_mncc *disc) -{ - struct gsm_call *remote; - - /* send release */ - DEBUGP(DMNCC, "(call %x) Releasing call with cause %d\n", - call->callref, disc->cause.value); - mncc_tx_to_cc(call->net, MNCC_REL_REQ, disc); - - /* send disc to remote */ - if (!(remote = get_call_ref(call->remote_ref))) { - return 0; - } - disc->callref = remote->callref; - DEBUGP(DMNCC, "(call %x) Disconnecting remote with cause %d\n", - remote->callref, disc->cause.value); - return mncc_tx_to_cc(remote->net, MNCC_DISC_REQ, disc); -} - -static int mncc_rel_ind(struct gsm_call *call, int msg_type, struct gsm_mncc *rel) -{ - struct gsm_call *remote; - - /* send release to remote */ - if (!(remote = get_call_ref(call->remote_ref))) { - free_call(call); - return 0; - } - - rel->callref = remote->callref; - DEBUGP(DMNCC, "(call %x) Releasing remote with cause %d\n", - call->callref, rel->cause.value); - - /* - * Release this side of the call right now. Otherwise we end up - * in this method for the other call and will also try to release - * it and then we will end up with a double free and a crash - */ - free_call(call); - mncc_tx_to_cc(remote->net, MNCC_REL_REQ, rel); - - return 0; -} - -static int mncc_rel_cnf(struct gsm_call *call, int msg_type, struct gsm_mncc *rel) -{ - free_call(call); - return 0; -} - -/* receiving a TCH/F frame from the BSC code */ -static int mncc_rcv_tchf(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); - } - - switch (msg_type) { - case GSM_TCHF_FRAME: - case GSM_TCHF_FRAME_EFR: - break; - default: - DEBUGP(DMNCC, "(call %x) Received message %s\n", call->callref, - get_mncc_name(msg_type)); - break; - } - - 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 = GSM48_CMODE_SPEECH_EFR; - mncc_tx_to_cc(call->net, MNCC_LCHAN_MODIFY, data); - break; - case MNCC_ALERT_IND: - rc = mncc_alert_ind(call, msg_type, arg); - break; - case MNCC_NOTIFY_IND: - rc = mncc_notify_ind(call, msg_type, arg); - break; - case MNCC_DISC_IND: - rc = mncc_disc_ind(call, msg_type, arg); - break; - case MNCC_REL_IND: - case MNCC_REJ_IND: - rc = mncc_rel_ind(call, msg_type, arg); - break; - case MNCC_REL_CNF: - rc = mncc_rel_cnf(call, msg_type, arg); - break; - case MNCC_FACILITY_IND: - break; - case MNCC_START_DTMF_IND: - break; - case MNCC_STOP_DTMF_IND: - break; - case MNCC_MODIFY_IND: - mncc_set_cause(data, GSM48_CAUSE_LOC_PRN_S_LU, - GSM48_CC_CAUSE_SERV_OPT_UNIMPL); - DEBUGP(DMNCC, "(call %x) Rejecting MODIFY with cause %d\n", - call->callref, data->cause.value); - rc = mncc_tx_to_cc(net, MNCC_MODIFY_REJ, data); - break; - case MNCC_MODIFY_CNF: - break; - case MNCC_HOLD_IND: - mncc_set_cause(data, GSM48_CAUSE_LOC_PRN_S_LU, - GSM48_CC_CAUSE_SERV_OPT_UNIMPL); - DEBUGP(DMNCC, "(call %x) Rejecting HOLD with cause %d\n", - call->callref, data->cause.value); - rc = mncc_tx_to_cc(net, MNCC_HOLD_REJ, data); - break; - case MNCC_RETRIEVE_IND: - mncc_set_cause(data, GSM48_CAUSE_LOC_PRN_S_LU, - GSM48_CC_CAUSE_SERV_OPT_UNIMPL); - DEBUGP(DMNCC, "(call %x) Rejecting RETRIEVE with cause %d\n", - call->callref, data->cause.value); - rc = mncc_tx_to_cc(net, MNCC_RETRIEVE_REJ, data); - break; - case GSM_TCHF_FRAME: - case GSM_TCHF_FRAME_EFR: - rc = mncc_rcv_tchf(call, msg_type, arg); - break; - default: - LOGP(DMNCC, LOGL_NOTICE, "(call %x) Message unhandled\n", callref); - break; - } - -out_free: - talloc_free(msg); - - return rc; -} diff --git a/openbsc/src/msc/mncc_sock.c b/openbsc/src/msc/mncc_sock.c deleted file mode 100644 index 2eef7c86e..000000000 --- a/openbsc/src/msc/mncc_sock.c +++ /dev/null @@ -1,337 +0,0 @@ -/* mncc_sock.c: Tie the MNCC interface to a unix domain socket */ - -/* (C) 2008-2010 by Harald Welte - * (C) 2009 by Andreas Eversberg - * All Rights Reserved - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - * - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include - -#include -#include -#include - -struct mncc_sock_state { - struct gsm_network *net; - struct bsc_fd listen_bfd; /* fd for listen socket */ - struct bsc_fd conn_bfd; /* fd for connection to lcr */ -}; - -/* FIXME: avoid this */ -static struct mncc_sock_state *g_state; - -/* 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 (g_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 (msg_type != GSM_TCHF_FRAME && - msg_type != GSM_TCHF_FRAME_EFR) { - /* 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); - g_state->conn_bfd.when |= BSC_FD_WRITE; - return 0; -} - -void mncc_sock_write_pending(void) -{ - g_state->conn_bfd.when |= BSC_FD_WRITE; -} - -/* FIXME: move this to libosmocore */ -int osmo_unixsock_listen(struct bsc_fd *bfd, int type, const char *path); - -static void mncc_sock_close(struct mncc_sock_state *state) -{ - struct bsc_fd *bfd = &state->conn_bfd; - - LOGP(DMNCC, LOGL_NOTICE, "MNCC Socket has LOST connection\n"); - - close(bfd->fd); - bfd->fd = -1; - bsc_unregister_fd(bfd); - - /* re-enable the generation of ACCEPT for new connections */ - state->listen_bfd.when |= BSC_FD_READ; - - /* FIXME: make sure we don't enqueue anymore */ - - /* 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 bsc_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 bsc_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; - - /* 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; - } - /* _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 bsc_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; -} - -/* accept a new connection */ -static int mncc_sock_accept(struct bsc_fd *bfd, unsigned int flags) -{ - struct mncc_sock_state *state = (struct mncc_sock_state *)bfd->data; - struct bsc_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 (bsc_register_fd(conn_bfd) != 0) { - LOGP(DMNCC, LOGL_ERROR, "Failed to register new connection fd\n"); - close(conn_bfd->fd); - conn_bfd->fd = -1; - state->listen_bfd.when |= ~BSC_FD_READ; - return -1; - } - - LOGP(DMNCC, LOGL_NOTICE, "MNCC Socket has connection with external " - "call control application\n"); - - return 0; -} - - -int mncc_sock_init(struct gsm_network *net) -{ - struct mncc_sock_state *state; - struct bsc_fd *bfd; - int rc; - - state = talloc_zero(tall_bsc_ctx, struct mncc_sock_state); - if (!state) - return -ENOMEM; - - state->net = net; - state->conn_bfd.fd = -1; - - bfd = &state->listen_bfd; - - rc = osmo_unixsock_listen(bfd, SOCK_SEQPACKET, "/tmp/bsc_mncc"); - if (rc < 0) { - LOGP(DMNCC, LOGL_ERROR, "Could not create unix socket: %s\n", - strerror(errno)); - talloc_free(state); - return rc; - } - - bfd->when = BSC_FD_READ; - bfd->cb = mncc_sock_accept; - bfd->data = state; - - rc = bsc_register_fd(bfd); - if (rc < 0) { - LOGP(DMNCC, LOGL_ERROR, "Could not register listen fd: %d\n", rc); - close(bfd->fd); - talloc_free(state); - return rc; - } - - g_state = state; - - return 0; -} - -/* FIXME: move this to libosmocore */ -int osmo_unixsock_listen(struct bsc_fd *bfd, int type, const char *path) -{ - struct sockaddr_un local; - unsigned int namelen; - int rc; - - bfd->fd = socket(AF_UNIX, type, 0); - - if (bfd->fd < 0) { - fprintf(stderr, "Failed to create Unix Domain Socket.\n"); - return -1; - } - - local.sun_family = AF_UNIX; - strncpy(local.sun_path, path, sizeof(local.sun_path)); - local.sun_path[sizeof(local.sun_path) - 1] = '\0'; - unlink(local.sun_path); - - /* we use the same magic that X11 uses in Xtranssock.c for - * calculating the proper length of the sockaddr */ -#if defined(BSD44SOCKETS) || defined(__UNIXWARE__) - local.sun_len = strlen(local.sun_path); -#endif -#if defined(BSD44SOCKETS) || defined(SUN_LEN) - namelen = SUN_LEN(&local); -#else - namelen = strlen(local.sun_path) + - offsetof(struct sockaddr_un, sun_path); -#endif - - rc = bind(bfd->fd, (struct sockaddr *) &local, namelen); - if (rc != 0) { - fprintf(stderr, "Failed to bind the unix domain socket. '%s'\n", - local.sun_path); - return -1; - } - - if (listen(bfd->fd, 0) != 0) { - fprintf(stderr, "Failed to listen.\n"); - return -1; - } - - return 0; -} diff --git a/openbsc/src/msc/osmo_msc.c b/openbsc/src/msc/osmo_msc.c deleted file mode 100644 index 8c86dcc8e..000000000 --- a/openbsc/src/msc/osmo_msc.c +++ /dev/null @@ -1,104 +0,0 @@ -/* main MSC management code... */ - -/* - * (C) 2010 by Holger Hans Peter Freyther - * (C) 2010 by On-Waves - * - * All Rights Reserved - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation; either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - * - */ - -#include -#include -#include - -#include - -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); - if (conn->put_channel) { - conn->put_channel = 0; - subscr_put_channel(conn->subscr); - } - return 1; -} - -static int msc_compl_l3(struct gsm_subscriber_connection *conn, struct msgb *msg, - uint16_t chosen_channel) -{ - gsm0408_new_conn(conn); - gsm0408_dispatch(conn, msg); - - /* TODO: do better */ - return BSC_API_CONN_POL_ACCEPT; -} - -static void msc_dtap(struct gsm_subscriber_connection *conn, uint8_t link_id, struct msgb *msg) -{ - gsm0408_dispatch(conn, msg); -} - -static struct bsc_api msc_handler = { - .sapi_n_reject = msc_sapi_n_reject, - .clear_request = msc_clear_request, - .compl_l3 = msc_compl_l3, - .dtap = msc_dtap, -}; - -struct bsc_api *msc_bsc_api() { - return &msc_handler; -} - -/* lchan release handling */ -void msc_release_connection(struct gsm_subscriber_connection *conn) -{ - struct gsm_trans *trans; - - /* 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; - - llist_for_each_entry(trans, &conn->bts->network->trans_list, entry) { - if (trans->conn == conn) - return; - } - - /* no more connections, asking to release the channel */ - conn->in_release = 1; - gsm0808_clear(conn); - if (conn->put_channel) { - conn->put_channel = 0; - subscr_put_channel(conn->subscr); - } - subscr_con_free(conn); -} diff --git a/openbsc/src/msc/rrlp.c b/openbsc/src/msc/rrlp.c deleted file mode 100644 index ae5ca478e..000000000 --- a/openbsc/src/msc/rrlp.c +++ /dev/null @@ -1,105 +0,0 @@ -/* Radio Resource LCS (Location) Protocol, GMS TS 04.31 */ - -/* (C) 2009 by Harald Welte - * - * All Rights Reserved - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation; either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - * - */ - - -#include - -#include -#include -#include -#include - -/* RRLP msPositionReq, nsBased, - * Accuracy=60, Method=gps, ResponseTime=2, oneSet */ -static const u_int8_t ms_based_pos_req[] = { 0x40, 0x01, 0x78, 0xa8 }; - -/* RRLP msPositionReq, msBasedPref, - Accuracy=60, Method=gpsOrEOTD, ResponseTime=5, multipleSets */ -static const u_int8_t ms_pref_pos_req[] = { 0x40, 0x02, 0x79, 0x50 }; - -/* RRLP msPositionReq, msAssistedPref, - Accuracy=60, Method=gpsOrEOTD, ResponseTime=5, multipleSets */ -static const u_int8_t ass_pref_pos_req[] = { 0x40, 0x03, 0x79, 0x50 }; - -static int send_rrlp_req(struct gsm_subscriber_connection *conn) -{ - struct gsm_network *net = conn->bts->network; - const u_int8_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) -{ - register_signal_handler(SS_SUBSCR, subscr_sig_cb, NULL); - register_signal_handler(SS_PAGING, paging_sig_cb, NULL); -} diff --git a/openbsc/src/msc/silent_call.c b/openbsc/src/msc/silent_call.c deleted file mode 100644 index 64ebdfdb9..000000000 --- a/openbsc/src/msc/silent_call.c +++ /dev/null @@ -1,143 +0,0 @@ -/* GSM silent call feature */ - -/* - * (C) 2009 by Harald Welte - * - * All Rights Reserved - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation; either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - * - */ - -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -/* paging of the requested subscriber has completed */ -static int paging_cb_silent(unsigned int hooknum, unsigned int event, - struct msgb *msg, void *_conn, void *_data) -{ - struct gsm_subscriber_connection *conn = _conn; - struct scall_signal_data sigdata; - int rc = 0; - - if (hooknum != GSM_HOOK_RR_PAGING) - return -EINVAL; - - DEBUGP(DSMS, "paging_cb_silent: "); - - sigdata.conn = conn; - sigdata.data = _data; - - switch (event) { - case GSM_PAGING_SUCCEEDED: - DEBUGPC(DSMS, "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 */ - dispatch_signal(SS_SCALL, S_SCALL_SUCCESS, &sigdata); - break; - case GSM_PAGING_EXPIRED: - case GSM_PAGING_BUSY: - case GSM_PAGING_OOM: - DEBUGP(DSMS, "expired\n"); - dispatch_signal(SS_SCALL, S_SCALL_EXPIRED, &sigdata); - break; - default: - rc = -EINVAL; - break; - } - - return rc; -} - -/* receive a layer 3 message from a silent call */ -int silent_call_rx(struct gsm_subscriber_connection *conn, struct msgb *msg) -{ - /* FIXME: do something like sending it through a UDP port */ - return 0; -} - -struct msg_match { - u_int8_t pdisc; - u_int8_t msg_type; -}; - -/* list of messages that are handled inside OpenBSC, even in a silent call */ -static const struct msg_match silent_call_accept[] = { - { GSM48_PDISC_MM, GSM48_MT_MM_LOC_UPD_REQUEST }, - { GSM48_PDISC_MM, GSM48_MT_MM_CM_SERV_REQ }, -}; - -/* decide if we need to reroute a message as part of a silent call */ -int silent_call_reroute(struct gsm_subscriber_connection *conn, struct msgb *msg) -{ - struct gsm48_hdr *gh = msgb_l3(msg); - u_int8_t pdisc = gh->proto_discr & 0x0f; - int i; - - /* if we're not part of a silent call, never reroute */ - if (!conn->silent_call) - return 0; - - /* check if we are a special message that is handled in openbsc */ - for (i = 0; i < ARRAY_SIZE(silent_call_accept); i++) { - if (silent_call_accept[i].pdisc == pdisc && - silent_call_accept[i].msg_type == gh->msg_type) - return 0; - } - - /* otherwise, reroute */ - return 1; -} - - -/* initiate a silent call with a given subscriber */ -int gsm_silent_call_start(struct gsm_subscriber *subscr, void *data, int type) -{ - int rc; - - rc = paging_request(subscr->net, subscr, type, - paging_cb_silent, data); - return rc; -} - -/* 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; - - conn->silent_call = 0; - msc_release_connection(conn); - - return 0; -} diff --git a/openbsc/src/msc/sms_queue.c b/openbsc/src/msc/sms_queue.c deleted file mode 100644 index 079755d22..000000000 --- a/openbsc/src/msc/sms_queue.c +++ /dev/null @@ -1,479 +0,0 @@ -/* SMS queue to continously attempt to deliver SMS */ -/* - * (C) 2010 by Holger Hans Peter Freyther - * All Rights Reserved - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation; either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - * - */ - -/** - * The difficulty of such a queue is to send a lot of SMS without - * overloading the paging subsystem and the database and other users - * of the MSC. To make the best use we would need to know the number - * of pending paging requests, then throttle the number of SMS we - * want to send and such. - * We will start with a very simple SMS Queue and then try to speed - * things up by collecting data from other parts of the system. - */ - -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -#include - -/* - * One pending SMS that we wait for. - */ -struct gsm_sms_pending { - struct llist_head entry; - - struct gsm_subscriber *subscr; - unsigned long long sms_id; - int failed_attempts; - int resend; -}; - -struct gsm_sms_queue { - struct timer_list resend_pending; - struct 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 int sms_subscriber_is_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 1; - } - - return 0; -} - -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(DSMS, LOGL_DEBUG, - "Scheduling resend of SMS %llu.\n", pending->sms_id); - - pending->resend = 1; - - smsq = pending->subscr->net->sms_queue; - if (bsc_timer_pending(&smsq->resend_pending)) - return; - - bsc_schedule_timer(&smsq->resend_pending, 1, 0); -} - -static void sms_pending_failed(struct gsm_sms_pending *pending, int paging_error) -{ - struct gsm_sms_queue *smsq; - - LOGP(DSMS, LOGL_NOTICE, "Sending SMS %llu failed %d times.\n", - pending->sms_id, pending->failed_attempts); - - smsq = pending->subscr->net->sms_queue; - if (++pending->failed_attempts < smsq->max_fail) - return sms_pending_resend(pending); - - if (paging_error) { - LOGP(DSMS, LOGL_NOTICE, - "Subscriber %llu is not reachable. Setting LAC=0.\n", pending->subscr->id); - pending->subscr->lac = GSM_LAC_RESERVED_DETACHED; - db_sync_subscriber(pending->subscr); - - /* Workaround a failing sync */ - db_subscriber_update(pending->subscr); - } - - 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(DSMS, LOGL_NOTICE, "Attempting to send %d SMS\n", attempts); - - do { - struct gsm_sms_pending *pending; - struct gsm_sms *sms; - - - sms = take_next_sms(smsq); - if (!sms) - break; - - rounds += 1; - - /* - * This code needs to detect a loop. It assumes that no SMS - * will vanish during the time this is executed. We will remember - * the id of the first GSM subscriber we see and then will - * compare this. The Database code should make sure that we will - * see all other subscribers first before seeing this one again. - * - * It is always scary to have an infinite loop like this. - */ - if (!initialized) { - first_sub = sms->receiver->id; - initialized = 1; - } else if (first_sub == sms->receiver->id) { - sms_free(sms); - break; - } - - /* no need to send a pending sms */ - if (sms_is_in_pending(smsq, sms)) { - LOGP(DSMS, 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(DSMS, 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(DSMS, 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(DSMS, LOGL_DEBUG, "SMSqueue added %d messages in %d rounds\n", attempted, rounds); -} - -/* - * Kick off the queue again. - */ -int sms_queue_trigger(struct gsm_sms_queue *smsq) -{ - if (bsc_timer_pending(&smsq->push_queue)) - return 0; - - bsc_schedule_timer(&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; - } - - register_signal_handler(SS_SUBSCR, sms_subscr_cb, network); - register_signal_handler(SS_SMS, sms_sms_cb, network); - - network->sms_queue = sms; - INIT_LLIST_HEAD(&sms->pending_sms); - sms->max_fail = 1; - sms->network = network; - sms->max_pending = max_pending; - sms->push_queue.data = sms; - sms->push_queue.cb = sms_submit_pending; - sms->resend_pending.data = sms; - sms->resend_pending.cb = sms_resend_pending; - - sms_submit_pending(sms); - - return 0; -} - -static int sub_ready_for_sm(struct gsm_subscriber *subscr) -{ - struct gsm_subscriber_connection *conn; - struct gsm_sms *sms; - - /* A subscriber has attached. Check if there are - * any pending SMS for him to be delivered */ - conn = connection_for_subscr(subscr); - if (!conn) - return -1; - 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(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; - - /* We got a new SMS and maybe should launch the queue again. */ - if (signal == S_SMS_SUBMITTED || signal == S_SMS_SMMA) { - 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: - /* - * Create place for a new SMS but keep the pending data - * so we will not attempt to send the SMS for this subscriber - * as we still have an open channel and will attempt to submit - * SMS to it anyway. - */ - network->sms_queue->pending -= 1; - sms_submit_pending(network->sms_queue); - sms_pending_free(pending); - 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(DSMS, LOGL_ERROR, "Unhandled result: %d\n", - sig_sms->paging_result); - } - break; - default: - LOGP(DSMS, 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(DSMS, 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(DSMS, 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(DSMS, 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/msc/token_auth.c b/openbsc/src/msc/token_auth.c deleted file mode 100644 index 3404dd4ee..000000000 --- a/openbsc/src/msc/token_auth.c +++ /dev/null @@ -1,153 +0,0 @@ -/* SMS based token authentication for ad-hoc GSM networks */ - -/* (C) 2009 by Harald Welte - * - * All Rights Reserved - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation; either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - * - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#define TOKEN_SMS_TEXT "HAR 2009 GSM. Register at http://har2009.gnumonks.org/ " \ - "Your IMSI is %s, auth token is %08X, phone no is %s." - -static char *build_sms_string(struct gsm_subscriber *subscr, u_int32_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->net->auth_policy != GSM_AUTH_POLICY_TOKEN) - return 0; - - if (subscr->flags & GSM_SUBSCRIBER_FIRST_CONTACT) { - u_int32_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; - } - - sms = sms_from_text(subscr, 0, sms_str); - 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) { - u_int8_t auth_rand[16]; - /* kick the subscriber off the network */ - gsm48_tx_mm_auth_req(conn, auth_rand, 0); - gsm48_tx_mm_auth_rej(conn); - /* FIXME: close the channel early ?*/ - //gsm48_send_rr_Release(lchan); - } - } - } - - return rc; -} - -static int token_sms_cb(unsigned int subsys, unsigned int signal, - void *handler_data, void *signal_data) -{ - struct sms_signal_data *sig = signal_data; - struct gsm_sms *sms = sig->sms;; - struct gsm_subscriber_connection *conn; - u_int8_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->net->auth_policy != GSM_AUTH_POLICY_TOKEN) - return 0; - - - conn = connection_for_subscr(sms->receiver); - if (conn) { - /* kick the subscriber off the network */ - gsm48_tx_mm_auth_req(conn, auth_rand, 0); - gsm48_tx_mm_auth_rej(conn); - /* FIXME: close the channel early ?*/ - //gsm48_send_rr_Release(lchan); - } - - return 0; -} - -//static __attribute__((constructor)) void on_dso_load_token(void) -void on_dso_load_token(void) -{ - register_signal_handler(SS_SUBSCR, token_subscr_cb, NULL); - register_signal_handler(SS_SMS, token_sms_cb, NULL); -} diff --git a/openbsc/src/msc/ussd.c b/openbsc/src/msc/ussd.c deleted file mode 100644 index 72f26bd36..000000000 --- a/openbsc/src/msc/ussd.c +++ /dev/null @@ -1,79 +0,0 @@ -/* Network-specific handling of mobile-originated USSDs. */ - -/* (C) 2008-2009 by Harald Welte - * (C) 2008, 2009 by Holger Hans Peter Freyther - * (C) 2009 by Mike Haben - * - * All Rights Reserved - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation; either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - * - */ - -/* This module defines the network-specific handling of mobile-originated - USSD messages. */ - -#include -#include -#include -#include - -#include -#include -#include -#include - -/* Declarations of USSD strings to be recognised */ -const char USSD_TEXT_OWN_NUMBER[] = "*#100#"; - -/* Forward declarations of network-specific handler functions */ -static int send_own_number(struct gsm_subscriber_connection *conn, const struct msgb *msg, const struct ussd_request *req); - - -/* Entrypoint - handler function common to all mobile-originated USSDs */ -int handle_rcv_ussd(struct gsm_subscriber_connection *conn, struct msgb *msg) -{ - int rc; - struct ussd_request req; - struct gsm48_hdr *gh; - - memset(&req, 0, sizeof(req)); - gh = msgb_l3(msg); - rc = gsm0480_decode_ussd_request(gh, msgb_l3len(msg), &req); - if (req.text[0] == 0xFF) /* Release-Complete */ - return 0; - - if (strstr(USSD_TEXT_OWN_NUMBER, req.text) != NULL) { - DEBUGP(DMM, "USSD: Own number requested\n"); - rc = send_own_number(conn, msg, &req); - } else { - DEBUGP(DMM, "Unhandled USSD %s\n", req.text); - rc = gsm0480_send_ussd_reject(conn, msg, &req); - } - - /* check if we can release it */ - msc_release_connection(conn); - return rc; -} - -/* A network-specific handler function */ -static int send_own_number(struct gsm_subscriber_connection *conn, const struct msgb *msg, const struct ussd_request *req) -{ - char *own_number = conn->subscr->extension; - char response_string[GSM_EXTENSION_LENGTH + 20]; - - /* Need trailing CR as EOT character */ - snprintf(response_string, sizeof(response_string), "Your extension is %s\r", own_number); - return gsm0480_send_ussd_response(conn, msg, response_string, req); -} diff --git a/openbsc/src/msc/vty_interface_layer3.c b/openbsc/src/msc/vty_interface_layer3.c deleted file mode 100644 index a38d15bbb..000000000 --- a/openbsc/src/msc/vty_interface_layer3.c +++ /dev/null @@ -1,790 +0,0 @@ -/* OpenBSC interface to quagga VTY */ -/* (C) 2009 by Harald Welte - * (C) 2009 by Holger Hans Peter Freyther - * All Rights Reserved - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation; either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - * - */ - -#include -#include -#include -#include - -#include -#include -#include - -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -extern struct gsm_network *gsmnet_from_vty(struct vty *v); - -static void subscr_dump_full_vty(struct vty *vty, struct gsm_subscriber *subscr, int pending) -{ - int rc; - struct gsm_auth_info ainfo; - struct gsm_auth_tuple atuple; - - vty_out(vty, " ID: %llu, Authorized: %d%s", subscr->id, - subscr->authorized, VTY_NEWLINE); - if (subscr->name) - vty_out(vty, " Name: '%s'%s", subscr->name, VTY_NEWLINE); - if (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); - if (subscr->imsi) - 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", - 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", - hexdump(atuple.rand, sizeof(atuple.rand)), - VTY_NEWLINE); - vty_out(vty, " SRES : %s%s", - hexdump(atuple.sres, sizeof(atuple.sres)), - VTY_NEWLINE); - vty_out(vty, " Kc : %s%s", - hexdump(atuple.kc, sizeof(atuple.kc)), - VTY_NEWLINE); - } - if (pending) - vty_out(vty, " Pending: %d%s", - subscr_pending_requests(subscr), 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 "Display contents of subscriber cache\n") -{ - struct gsm_subscriber *subscr; - - llist_for_each_entry(subscr, &active_subscribers, entry) { - vty_out(vty, " Subscriber:%s", VTY_NEWLINE); - subscr_dump_full_vty(vty, subscr, 0); - } - - return CMD_SUCCESS; -} - -DEFUN(sms_send_pend, - sms_send_pend_cmd, - "sms send pending", - "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, char *str, - u_int8_t tp_pid) -{ - struct gsm_sms *sms; - - sms = sms_from_text(receiver, 0, str); - sms->protocol_id = tp_pid; - - /* store in database for the queue */ - if (db_sms_store(sms) != 0) { - LOGP(DSMS, LOGL_ERROR, "Failed to store SMS in Database\n"); - sms_free(sms); - return CMD_WARNING; - } - - sms_free(sms); - sms_queue_trigger(receiver->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, id); - else if (!strcmp(type, "imsi")) - return subscr_get_by_imsi(gsmnet, id); - else if (!strcmp(type, "tmsi")) - return subscr_get_by_tmsi(gsmnet, atoi(id)); - else if (!strcmp(type, "id")) - return subscr_get_by_id(gsmnet, atoi(id)); - - return NULL; -} -#define SUBSCR_TYPES "(extension|imsi|tmsi|id)" -#define SUBSCR_HELP "Operations on a Subscriber\n" \ - "Identify subscriber by his extension (phone number)\n" \ - "Identify subscriber by his IMSI\n" \ - "Identify subscriber by his TMSI\n" \ - "Identify subscriber by his database ID\n" \ - "Identifier for the subscriber\n" - -DEFUN(show_subscr, - show_subscr_cmd, - "show subscriber " SUBSCR_TYPES " ID", - SHOW_STR SUBSCR_HELP) -{ - struct gsm_network *gsmnet = gsmnet_from_vty(vty); - struct gsm_subscriber *subscr = - get_subscr_by_argv(gsmnet, argv[0], argv[1]); - - if (!subscr) { - vty_out(vty, "%% No subscriber found for %s %s%s", - argv[0], argv[1], VTY_NEWLINE); - return CMD_WARNING; - } - - subscr_dump_full_vty(vty, subscr, 1); - - 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 = get_subscr_by_argv(gsmnet, argv[0], argv[1]); - struct gsm_sms *sms; - - 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 send .LINE", - SUBSCR_HELP "SMS Operations\n" "Send SMS\n" "Actual SMS Text") -{ - struct gsm_network *gsmnet = gsmnet_from_vty(vty); - struct gsm_subscriber *subscr = get_subscr_by_argv(gsmnet, argv[0], argv[1]); - char *str; - int rc; - - if (!subscr) { - vty_out(vty, "%% No subscriber found for %s %s%s", - argv[0], argv[1], VTY_NEWLINE); - return CMD_WARNING; - } - str = argv_concat(argv, argc, 2); - rc = _send_sms_str(subscr, str, 0); - talloc_free(str); - - subscr_put(subscr); - - return rc; -} - -DEFUN(subscriber_silent_sms, - subscriber_silent_sms_cmd, - "subscriber " SUBSCR_TYPES " ID silent-sms send .LINE", - SUBSCR_HELP - "Silent SMS Operation\n" "Send Silent 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]); - char *str; - int rc; - - if (!subscr) { - vty_out(vty, "%% No subscriber found for %s %s%s", - argv[0], argv[1], VTY_NEWLINE); - return CMD_WARNING; - } - - str = argv_concat(argv, argc, 2); - rc = _send_sms_str(subscr, str, 64); - talloc_free(str); - - 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 "USSD Notify\n" - "Subscriber ID\n" - "Alerting Level\n" - "Text Message to send\n") -{ - char *text; - struct gsm_subscriber_connection *conn; - struct gsm_network *gsmnet = gsmnet_from_vty(vty); - struct gsm_subscriber *subscr = get_subscr_by_argv(gsmnet, argv[0], argv[1]); - int level; - - if (!subscr) { - vty_out(vty, "%% No subscriber found for %s %s%s", - argv[0], argv[1], VTY_NEWLINE); - return CMD_WARNING; - } - - level = atoi(argv[2]); - text = argv_concat(argv, argc, 3); - if (!text) { - subscr_put(subscr); - return CMD_WARNING; - } - - conn = connection_for_subscr(subscr); - if (!conn) { - vty_out(vty, "%% An active connection is required for %s %s%s", - argv[0], argv[1], VTY_NEWLINE); - subscr_put(subscr); - talloc_free(text); - return CMD_WARNING; - } - - gsm0480_send_ussdNotify(conn, level, text); - gsm0480_send_releaseComplete(conn); - - subscr_put(subscr); - talloc_free(text); - return CMD_SUCCESS; -} - -DEFUN(ena_subscr_authorizde, - 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; - } - - strncpy(subscr->name, name, sizeof(subscr->name)); - talloc_free(name); - db_sync_subscriber(subscr); - - subscr_put(subscr); - - return CMD_SUCCESS; -} - -DEFUN(ena_subscr_extension, - ena_subscr_extension_cmd, - "subscriber " SUBSCR_TYPES " ID extension EXTENSION", - SUBSCR_HELP "Set the extension (phone number) of the subscriber\n" - "Extension (phone number)\n") -{ - struct gsm_network *gsmnet = gsmnet_from_vty(vty); - struct gsm_subscriber *subscr = - get_subscr_by_argv(gsmnet, argv[0], argv[1]); - const char *name = argv[2]; - - if (!subscr) { - vty_out(vty, "%% No subscriber found for %s %s%s", - argv[0], argv[1], VTY_NEWLINE); - return CMD_WARNING; - } - - strncpy(subscr->extension, name, sizeof(subscr->name)); - db_sync_subscriber(subscr); - - subscr_put(subscr); - - return CMD_SUCCESS; -} - -DEFUN(ena_subscr_clear, - ena_subscr_clear_cmd, - "subscriber " SUBSCR_TYPES " ID clear-requests", - SUBSCR_HELP "Clear the paging requests for this subscriber\n") -{ - int del; - 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; - } - - del = subscr_pending_clear(subscr); - vty_out(vty, "Cleared %d pending requests.%s", del, VTY_NEWLINE); - subscr_put(subscr); - - return CMD_SUCCESS; -} - -DEFUN(ena_subscr_pend, - ena_subscr_pend_cmd, - "subscriber " SUBSCR_TYPES " ID show-pending", - SUBSCR_HELP "Clear the paging requests for this subscriber\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_pending_dump(subscr, vty); - subscr_put(subscr); - - return CMD_SUCCESS; -} - -DEFUN(ena_subscr_kick, - ena_subscr_kick_cmd, - "subscriber " SUBSCR_TYPES " ID kick-pending", - SUBSCR_HELP "Clear the paging requests for this subscriber\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_pending_kick(subscr); - 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); - return CMD_WARNING; - } - - if (ki_str) { - rc = hexparse(ki_str, ainfo.a3a8_ki, sizeof(ainfo.a3a8_ki)); - if ((rc > maxlen) || (rc < minlen)) { - subscr_put(subscr); - return CMD_WARNING; - } - ainfo.a3a8_ki_len = rc; - } else { - ainfo.a3a8_ki_len = 0; - if (minlen) { - subscr_put(subscr); - 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); - - return rc ? CMD_WARNING : 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); - 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, "Channel Requests : %lu total, %lu no channel%s", - counter_get(net->stats.chreq.total), - counter_get(net->stats.chreq.no_channel), VTY_NEWLINE); - vty_out(vty, "Location Update : %lu attach, %lu normal, %lu periodic%s", - counter_get(net->stats.loc_upd_type.attach), - counter_get(net->stats.loc_upd_type.normal), - counter_get(net->stats.loc_upd_type.periodic), VTY_NEWLINE); - vty_out(vty, "IMSI Detach Indications : %lu%s", - counter_get(net->stats.loc_upd_type.detach), VTY_NEWLINE); - vty_out(vty, "Location Update Response: %lu accept, %lu reject%s", - counter_get(net->stats.loc_upd_resp.accept), - counter_get(net->stats.loc_upd_resp.reject), VTY_NEWLINE); - vty_out(vty, "Handover : %lu attempted, %lu no_channel, %lu timeout, " - "%lu completed, %lu failed%s", - counter_get(net->stats.handover.attempted), - counter_get(net->stats.handover.no_channel), - counter_get(net->stats.handover.timeout), - counter_get(net->stats.handover.completed), - counter_get(net->stats.handover.failed), VTY_NEWLINE); - vty_out(vty, "SMS MO : %lu submitted, %lu no receiver%s", - counter_get(net->stats.sms.submitted), - counter_get(net->stats.sms.no_receiver), VTY_NEWLINE); - vty_out(vty, "SMS MT : %lu delivered, %lu no memory, %lu other error%s", - counter_get(net->stats.sms.delivered), - counter_get(net->stats.sms.rp_err_mem), - counter_get(net->stats.sms.rp_err_other), VTY_NEWLINE); - vty_out(vty, "MO Calls : %lu setup, %lu connect ack%s", - counter_get(net->stats.call.mo_setup), - counter_get(net->stats.call.mo_connect_ack), VTY_NEWLINE); - vty_out(vty, "MT Calls : %lu setup, %lu connect%s", - counter_get(net->stats.call.mt_setup), - counter_get(net->stats.call.mt_connect), VTY_NEWLINE); - return CMD_SUCCESS; -} - -DEFUN(show_smsqueue, - show_smsqueue_cmd, - "show sms-queue", - SHOW_STR "Display SMSqueue statistics\n") -{ - struct gsm_network *net = gsmnet_from_vty(vty); - - sms_queue_stats(net->sms_queue, vty); - return CMD_SUCCESS; -} - -DEFUN(smsqueue_trigger, - smsqueue_trigger_cmd, - "sms-queue trigger", - "SMS Queue\n" "Trigger sending messages\n") -{ - struct gsm_network *net = gsmnet_from_vty(vty); - - sms_queue_trigger(net->sms_queue); - return CMD_SUCCESS; -} - -DEFUN(smsqueue_max, - smsqueue_max_cmd, - "sms-queue max-pending <1-500>", - "SMS Queue\n" "SMS to attempt to deliver at the same time\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" "Set maximum amount of failures\n") -{ - struct gsm_network *net = gsmnet_from_vty(vty); - - sms_queue_set_max_failure(net->sms_queue, atoi(argv[0])); - return CMD_SUCCESS; -} - -int bsc_vty_init_extra(void) -{ - register_signal_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_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(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_clear_cmd); - install_element(ENABLE_NODE, &ena_subscr_pend_cmd); - install_element(ENABLE_NODE, &ena_subscr_kick_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); - - return 0; -} diff --git a/openbsc/src/osmo-bsc/Makefile.am b/openbsc/src/osmo-bsc/Makefile.am index 735d440a1..95b9ef4f1 100644 --- a/openbsc/src/osmo-bsc/Makefile.am +++ b/openbsc/src/osmo-bsc/Makefile.am @@ -9,6 +9,10 @@ osmo_bsc_SOURCES = osmo_bsc_main.c osmo_bsc_rf.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 # once again since TRAU uses CC symbol :( -osmo_bsc_LDADD = ../bsc/libbsc.a ../msc/libmsc.a ../bsc/libbsc.a \ - ../abis/libabis.a ../trau/libtrau.a ../common/libcommon.a \ +osmo_bsc_LDADD = $(top_builddir)/src/libbsc/libbsc.a \ + $(top_builddir)/src/libmsc/libmsc.a \ + $(top_builddir)/src/libbsc/libbsc.a \ + $(top_builddir)/src/libabis/libabis.a \ + $(top_builddir)/src/libtrau/libtrau.a \ + $(top_builddir)/src/libcommon/libcommon.a \ $(LIBOSMOSCCP_LIBS) diff --git a/openbsc/src/osmo-bsc_mgcp/Makefile.am b/openbsc/src/osmo-bsc_mgcp/Makefile.am index 0dc410348..32cc81300 100644 --- a/openbsc/src/osmo-bsc_mgcp/Makefile.am +++ b/openbsc/src/osmo-bsc_mgcp/Makefile.am @@ -5,5 +5,6 @@ AM_LDFLAGS = $(LIBOSMOCORE_LIBS) $(COVERAGE_LDFLAGS) bin_PROGRAMS = bsc_mgcp bsc_mgcp_SOURCES = mgcp_main.c -bsc_mgcp_LDADD = $(top_builddir)/src/common/libcommon.a $(top_builddir)/src/mgcp/libmgcp.a \ - $(LIBOSMOVTY_LIBS) +bsc_mgcp_LDADD = $(top_builddir)/src/libcommon/libcommon.a \ + $(top_builddir)/src/libmgcp/libmgcp.a \ + $(LIBOSMOVTY_LIBS) diff --git a/openbsc/src/osmo-bsc_nat/Makefile.am b/openbsc/src/osmo-bsc_nat/Makefile.am index c7905ce3a..98a343108 100644 --- a/openbsc/src/osmo-bsc_nat/Makefile.am +++ b/openbsc/src/osmo-bsc_nat/Makefile.am @@ -7,7 +7,9 @@ bin_PROGRAMS = osmo-bsc_nat osmo_bsc_nat_SOURCES = bsc_filter.c bsc_mgcp_utils.c bsc_nat.c bsc_nat_utils.c \ bsc_nat_vty.c bsc_sccp.c bsc_ussd.c -osmo_bsc_nat_LDADD = $(top_builddir)/src/common/libcommon.a \ - $(top_builddir)/src/mgcp/libmgcp.a $(top_builddir)/src/bsc/libbsc.a \ - $(top_builddir)/src/abis/libabis.a $(top_builddir)/src/trau/libtrau.a \ +osmo_bsc_nat_LDADD = $(top_builddir)/src/libcommon/libcommon.a \ + $(top_builddir)/src/libmgcp/libmgcp.a \ + $(top_builddir)/src/libbsc/libbsc.a \ + $(top_builddir)/src/libabis/libabis.a \ + $(top_builddir)/src/libtrau/libtrau.a \ -lrt $(LIBOSMOSCCP_LIBS) diff --git a/openbsc/src/osmo-nitb/Makefile.am b/openbsc/src/osmo-nitb/Makefile.am index 55822e906..44cb0239f 100644 --- a/openbsc/src/osmo-nitb/Makefile.am +++ b/openbsc/src/osmo-nitb/Makefile.am @@ -6,5 +6,9 @@ bin_PROGRAMS = osmo-nitb osmo_nitb_SOURCES = bsc_hack.c osmo_nitb_LDADD = -ldl -ldbi $(LIBCRYPT) $(LIBOSMOVTY_LIBS) \ - ../bsc/libbsc.a ../msc/libmsc.a ../bsc/libbsc.a ../trau/libtrau.a ../abis/libabis.a \ - ../common/libcommon.a + $(top_builddir)/src/libbsc/libbsc.a \ + $(top_builddir)/src/libmsc/libmsc.a \ + $(top_builddir)/src/libbsc/libbsc.a \ + $(top_builddir)/src/libtrau/libtrau.a \ + $(top_builddir)/src/libabis/libabis.a \ + $(top_builddir)/src/libcommon/libcommon.a diff --git a/openbsc/src/trau/Makefile.am b/openbsc/src/trau/Makefile.am deleted file mode 100644 index 01ed251d8..000000000 --- a/openbsc/src/trau/Makefile.am +++ /dev/null @@ -1,7 +0,0 @@ -INCLUDES = $(all_includes) -I$(top_srcdir)/include -I$(top_builddir) -AM_CFLAGS=-Wall $(LIBOSMOCORE_CFLAGS) $(LIBOSMOVTY_CFLAGS) $(COVERAGE_CFLAGS) -AM_LDFLAGS = $(LIBOSMOCORE_LIBS) $(COVERAGE_LDFLAGS) - -noinst_LIBRARIES = libtrau.a - -libtrau_a_SOURCES = rtp_proxy.c subchan_demux.c trau_frame.c trau_mux.c trau_upqueue.c diff --git a/openbsc/src/trau/rtp_proxy.c b/openbsc/src/trau/rtp_proxy.c deleted file mode 100644 index eefc0e1d6..000000000 --- a/openbsc/src/trau/rtp_proxy.c +++ /dev/null @@ -1,728 +0,0 @@ -/* RTP proxy handling for ip.access nanoBTS */ - -/* (C) 2009 by Harald Welte - * All Rights Reserved - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation; either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - * - */ - -#include -#include -#include -#include -#include -#include /* gettimeofday() */ -#include /* get..() */ -#include /* clock() */ -#include /* uname() */ - -#include -#include -#include -#include -#include -#include - -/* attempt to determine byte order */ -#include -#include -#include - -#ifndef __BYTE_ORDER -#error "__BYTE_ORDER should be defined by someone" -#endif - -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 - -/* according to RFC 1889 */ -struct rtcp_hdr { - u_int8_t byte0; - u_int8_t type; - u_int16_t length; -} __attribute__((packed)); - -#define RTCP_TYPE_SDES 202 - -#define RTCP_IE_CNAME 1 - -/* according to RFC 3550 */ -struct rtp_hdr { -#if __BYTE_ORDER == __LITTLE_ENDIAN - u_int8_t csrc_count:4, - extension:1, - padding:1, - version:2; - u_int8_t payload_type:7, - marker:1; -#elif __BYTE_ORDER == __BIG_ENDIAN - u_int8_t version:2, - padding:1, - extension:1, - csrc_count:4; - u_int8_t marker:1, - payload_type:7; -#endif - u_int16_t sequence; - u_int32_t timestamp; - u_int32_t ssrc; -} __attribute__((packed)); - -struct rtp_x_hdr { - u_int16_t by_profile; - u_int16_t length; -} __attribute__((packed)); - -#define RTP_VERSION 2 - -/* decode an rtp frame and create a new buffer with payload */ -static int rtp_decode(struct msgb *msg, u_int32_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; - u_int8_t *payload; - int payload_len; - int msg_type; - int x_len; - - if (msg->len < 12) { - DEBUGPC(DMUX, "received RTP frame too short (len = %d)\n", - msg->len); - return -EINVAL; - } - if (rtph->version != RTP_VERSION) { - DEBUGPC(DMUX, "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(DMUX, "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(DMUX, "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(DMUX, "received RTP frame too short, " - "extension header exceeds frame length\n"); - return -EINVAL; - } - } - if (rtph->padding) { - if (payload_len < 0) { - DEBUGPC(DMUX, "received RTP frame too short for " - "padding length\n"); - return -EINVAL; - } - payload_len -= payload[payload_len - 1]; - if (payload_len < 0) { - DEBUGPC(DMUX, "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 != 33) { - DEBUGPC(DMUX, "received RTP full rate frame with " - "payload length != 32 (len = %d)\n", - payload_len); - return -EINVAL; - } - break; - case RTP_PT_GSM_EFR: - msg_type = GSM_TCHF_FRAME_EFR; - break; - default: - DEBUGPC(DMUX, "received RTP frame with unknown payload " - "type %d\n", rtph->payload_type); - return -EINVAL; - } - - new_msg = msgb_alloc(sizeof(struct gsm_data_frame) + payload_len, - "GSM-DATA"); - if (!new_msg) - return -ENOMEM; - frame = (struct gsm_data_frame *)(new_msg->data); - frame->msg_type = msg_type; - frame->callref = callref; - memcpy(frame->data, payload, payload_len); - msgb_put(new_msg, sizeof(struct gsm_data_frame) + payload_len); - - *data = new_msg; - return 0; -} - -/* "to - from" */ -static void tv_difference(struct timeval *diff, const struct timeval *from, - const struct timeval *__to) -{ - struct timeval _to = *__to, *to = &_to; - - if (to->tv_usec < from->tv_usec) { - to->tv_sec -= 1; - to->tv_usec += 1000000; - } - - diff->tv_usec = to->tv_usec - from->tv_usec; - diff->tv_sec = to->tv_sec - from->tv_sec; -} - -/* encode and send a rtp frame */ -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; - int payload_type; - int payload_len; - int duration; /* in samples */ - - 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 = 33; - duration = 160; - break; - case GSM_TCHF_FRAME_EFR: - payload_type = RTP_PT_GSM_EFR; - payload_len = 31; - duration = 160; - break; - default: - DEBUGPC(DMUX, "unsupported message type %d\n", - frame->msg_type); - return -EINVAL; - } - - { - struct timeval tv, tv_diff; - long int usec_diff, frame_diff; - - gettimeofday(&tv, NULL); - tv_difference(&tv_diff, &rs->transmit.last_tv, &tv); - rs->transmit.last_tv = tv; - - usec_diff = tv_diff.tv_sec * 1000000 + tv_diff.tv_usec; - frame_diff = (usec_diff / 20000); - - if (abs(frame_diff) > 1) { - long int frame_diff_excess = frame_diff - 1; - - LOGP(DMUX, LOGL_NOTICE, - "Correcting frame difference of %ld frames\n", frame_diff_excess); - rs->transmit.sequence += frame_diff_excess; - rs->transmit.timestamp += frame_diff_excess * duration; - } - } - - msg = msgb_alloc(sizeof(struct rtp_hdr) + payload_len, "RTP-GSM-FULL"); - if (!msg) - return -ENOMEM; - rtph = (struct rtp_hdr *)msg->data; - 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); - memcpy(msg->data + sizeof(struct rtp_hdr), frame->data, payload_len); - msgb_put(msg, sizeof(struct rtp_hdr) + 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, - u_int16_t *rtcp_len, const char *new_cname) -{ - u_int8_t *rtcp_end; - u_int8_t *cur = (u_int8_t *) rh; - u_int8_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) { } - 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; - u_int16_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(DMUX, "received RTCP packet too short for " - "length element\n"); - return -EINVAL; - } - if (rtph->type == RTCP_TYPE_SDES) { - char new_cname[255]; - strncpy(new_cname, inet_ntoa(rss->sin_local.sin_addr), - sizeof(new_cname)); - new_cname[sizeof(new_cname)-1] = '\0'; - rc = rtcp_sdes_cname_mangle(msg, rtph, &old_len, - new_cname); - if (rc < 0) - return rc; - } - rtph = (void *)rtph + old_len; - } - - return 0; -} - -/* read from incoming RTP/RTCP socket */ -static int rtp_socket_read(struct rtp_socket *rs, struct rtp_sub_socket *rss) -{ - int rc; - struct msgb *msg = msgb_alloc(RTP_ALLOC_SIZE, "RTP/RTCP"); - struct msgb *new_msg; - struct rtp_sub_socket *other_rss; - - if (!msg) - return -ENOMEM; - - rc = read(rss->bfd.fd, msg->data, RTP_ALLOC_SIZE); - if (rc <= 0) { - rss->bfd.when &= ~BSC_FD_READ; - return rc; - } - - 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; -} - -/* 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(DMIB, LOGL_ERROR, "short write"); - msgb_free(msg); - return -EIO; - } - - msgb_free(msg); - - return 0; -} - - -/* callback for the select.c:bfd_* layer */ -static int rtp_bfd_cb(struct bsc_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; -} - -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; -} - -struct rtp_socket *rtp_socket_create(void) -{ - int rc; - struct rtp_socket *rs; - - DEBUGP(DMUX, "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 = bsc_register_fd(&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 = bsc_register_fd(&rs->rtcp.bfd); - if (rc < 0) - goto out_rtcp_socket; - - DEBUGPC(DMUX, "success\n"); - - rc = rtp_socket_bind(rs, INADDR_ANY); - if (rc < 0) - goto out_rtcp_bfd; - - return rs; - -out_rtcp_bfd: - bsc_unregister_fd(&rs->rtcp.bfd); -out_rtcp_socket: - close(rs->rtcp.bfd.fd); -out_rtp_bfd: - bsc_unregister_fd(&rs->rtp.bfd); -out_rtp_socket: - close(rs->rtp.bfd.fd); -out_free: - talloc_free(rs); - DEBUGPC(DMUX, "failed\n"); - return NULL; -} - -static int rtp_sub_socket_bind(struct rtp_sub_socket *rss, u_int32_t ip, - u_int16_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; - -/* bind a RTP socket to a local address */ -int rtp_socket_bind(struct rtp_socket *rs, u_int32_t ip) -{ - int rc = -EIO; - struct in_addr ia; - - ia.s_addr = htonl(ip); - DEBUGP(DMUX, "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(DMUX, "failed\n"); - return rc; - } - - ia.s_addr = rs->rtp.sin_local.sin_addr.s_addr; - DEBUGPC(DMUX, "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, - u_int32_t ip, u_int16_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); -} - -/* 'connect' a RTP socket to a remote peer */ -int rtp_socket_connect(struct rtp_socket *rs, u_int32_t ip, u_int16_t port) -{ - int rc; - struct in_addr ia; - - ia.s_addr = htonl(ip); - DEBUGP(DMUX, "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); -} - -/* bind two RTP/RTCP sockets together */ -int rtp_socket_proxy(struct rtp_socket *this, struct rtp_socket *other) -{ - DEBUGP(DMUX, "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; -} - -/* bind RTP/RTCP socket to application */ -int rtp_socket_upstream(struct rtp_socket *this, struct gsm_network *net, - u_int32_t callref) -{ - DEBUGP(DMUX, "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); -} - -int rtp_socket_free(struct rtp_socket *rs) -{ - DEBUGP(DMUX, "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; - - bsc_unregister_fd(&rs->rtp.bfd); - close(rs->rtp.bfd.fd); - free_tx_queue(&rs->rtp); - - bsc_unregister_fd(&rs->rtcp.bfd); - close(rs->rtcp.bfd.fd); - free_tx_queue(&rs->rtcp); - - talloc_free(rs); - - return 0; -} diff --git a/openbsc/src/trau/subchan_demux.c b/openbsc/src/trau/subchan_demux.c deleted file mode 100644 index 6bcf279fe..000000000 --- a/openbsc/src/trau/subchan_demux.c +++ /dev/null @@ -1,321 +0,0 @@ -/* A E1 sub-channel (de)multiplexer with TRAU frame sync */ - -/* (C) 2009 by Harald Welte - * All Rights Reserved - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation; either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - * - */ - -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include - -void *tall_tqe_ctx; - -static inline void append_bit(struct demux_subch *sch, u_int8_t bit) -{ - sch->out_bitbuf[sch->out_idx++] = bit; -} - -#define SYNC_HDR_BITS 16 -static const u_int8_t nullbytes[SYNC_HDR_BITS]; - -/* check if we have just completed the 16 bit zero sync header, - * in accordance with GSM TS 08.60 Chapter 4.8.1 */ -static int sync_hdr_complete(struct demux_subch *sch, u_int8_t bit) -{ - if (bit == 0) - sch->consecutive_zeros++; - else - sch->consecutive_zeros = 0; - - if (sch->consecutive_zeros >= SYNC_HDR_BITS) { - sch->consecutive_zeros = 0; - return 1; - } - - return 0; -} - -/* resynchronize to current location */ -static void resync_to_here(struct demux_subch *sch) -{ - memset(sch->out_bitbuf, 0, SYNC_HDR_BITS); - - /* set index in a way that we can continue receiving bits after - * the end of the SYNC header */ - sch->out_idx = SYNC_HDR_BITS; - sch->in_sync = 1; -} - -int subch_demux_init(struct subch_demux *dmx) -{ - int i; - - dmx->chan_activ = 0; - for (i = 0; i < NR_SUBCH; i++) { - struct demux_subch *sch = &dmx->subch[i]; - sch->out_idx = 0; - memset(sch->out_bitbuf, 0xff, sizeof(sch->out_bitbuf)); - } - return 0; -} - -/* input some arbitrary (modulo 4) number of bytes of a 64k E1 channel, - * split it into the 16k subchannels */ -int subch_demux_in(struct subch_demux *dmx, u_int8_t *data, int len) -{ - int i, c; - - /* we avoid partially filled bytes in outbuf */ - if (len % 4) - return -EINVAL; - - for (i = 0; i < len; i++) { - u_int8_t inbyte = data[i]; - - for (c = 0; c < NR_SUBCH; c++) { - struct demux_subch *sch = &dmx->subch[c]; - u_int8_t inbits; - u_int8_t bit; - - /* ignore inactive subchannels */ - if (!(dmx->chan_activ & (1 << c))) - continue; - - inbits = inbyte >> (c << 1); - - /* two bits for each subchannel */ - if (inbits & 0x01) - bit = 1; - else - bit = 0; - append_bit(sch, bit); - - if (sync_hdr_complete(sch, bit)) - resync_to_here(sch); - - if (inbits & 0x02) - bit = 1; - else - bit = 0; - append_bit(sch, bit); - - if (sync_hdr_complete(sch, bit)) - resync_to_here(sch); - - /* FIXME: verify the first bit in octet 2, 4, 6, ... - * according to TS 08.60 4.8.1 */ - - /* once we have reached TRAU_FRAME_BITS, call - * the TRAU frame handler callback function */ - if (sch->out_idx >= TRAU_FRAME_BITS) { - if (sch->in_sync) { - dmx->out_cb(dmx, c, sch->out_bitbuf, - sch->out_idx, dmx->data); - sch->in_sync = 0; - } - sch->out_idx = 0; - } - } - } - return i; -} - -int subch_demux_activate(struct subch_demux *dmx, int subch) -{ - if (subch >= NR_SUBCH) - return -EINVAL; - - dmx->chan_activ |= (1 << subch); - return 0; -} - -int subch_demux_deactivate(struct subch_demux *dmx, int subch) -{ - if (subch >= NR_SUBCH) - return -EINVAL; - - dmx->chan_activ &= ~(1 << subch); - return 0; -} - -/* MULTIPLEXER */ - -static int alloc_add_idle_frame(struct subch_mux *mx, int sch_nr) -{ - /* allocate and initialize with idle pattern */ - return subchan_mux_enqueue(mx, sch_nr, trau_idle_frame(), - TRAU_FRAME_BITS); -} - -/* return the requested number of bits from the specified subchannel */ -static int get_subch_bits(struct subch_mux *mx, int subch, - u_int8_t *bits, int num_requested) -{ - struct mux_subch *sch = &mx->subch[subch]; - int num_bits = 0; - - while (num_bits < num_requested) { - struct subch_txq_entry *txe; - int num_bits_left; - int num_bits_thistime; - - /* make sure we have a valid entry at top of tx queue. - * if not, add an idle frame */ - if (llist_empty(&sch->tx_queue)) - alloc_add_idle_frame(mx, subch); - - if (llist_empty(&sch->tx_queue)) - return -EIO; - - txe = llist_entry(sch->tx_queue.next, struct subch_txq_entry, list); - num_bits_left = txe->bit_len - txe->next_bit; - - if (num_bits_left < num_requested) - num_bits_thistime = num_bits_left; - else - num_bits_thistime = num_requested; - - /* pull the bits from the txe */ - memcpy(bits + num_bits, txe->bits + txe->next_bit, num_bits_thistime); - txe->next_bit += num_bits_thistime; - - /* free the tx_queue entry if it is fully consumed */ - if (txe->next_bit >= txe->bit_len) { - llist_del(&txe->list); - talloc_free(txe); - } - - /* increment global number of bits dequeued */ - num_bits += num_bits_thistime; - } - - return num_requested; -} - -/* compact an array of 8 single-bit bytes into one byte of 8 bits */ -static u_int8_t compact_bits(const u_int8_t *bits) -{ - u_int8_t ret = 0; - int i; - - for (i = 0; i < 8; i++) - ret |= (bits[i] ? 1 : 0) << i; - - return ret; -} - -/* obtain a single output byte from the subchannel muxer */ -static int mux_output_byte(struct subch_mux *mx, u_int8_t *byte) -{ - u_int8_t bits[8]; - int rc; - - /* combine two bits of every subchan */ - rc = get_subch_bits(mx, 0, &bits[0], 2); - rc = get_subch_bits(mx, 1, &bits[2], 2); - rc = get_subch_bits(mx, 2, &bits[4], 2); - rc = get_subch_bits(mx, 3, &bits[6], 2); - - *byte = compact_bits(bits); - - return rc; -} - -/* Request the output of some muxed bytes from the subchan muxer */ -int subchan_mux_out(struct subch_mux *mx, u_int8_t *data, int len) -{ - int i; - - for (i = 0; i < len; i++) { - int rc; - rc = mux_output_byte(mx, &data[i]); - if (rc < 0) - break; - } - return i; -} - -static int llist_len(struct llist_head *head) -{ - struct llist_head *entry; - int i = 0; - - llist_for_each(entry, head) - i++; - - return i; -} - -/* evict the 'num_evict' number of oldest entries in the queue */ -static void tx_queue_evict(struct mux_subch *sch, int num_evict) -{ - struct subch_txq_entry *tqe; - int i; - - for (i = 0; i < num_evict; i++) { - if (llist_empty(&sch->tx_queue)) - return; - - tqe = llist_entry(sch->tx_queue.next, struct subch_txq_entry, list); - llist_del(&tqe->list); - talloc_free(tqe); - } -} - -/* enqueue some data into the tx_queue of a given subchannel */ -int subchan_mux_enqueue(struct subch_mux *mx, int s_nr, const u_int8_t *data, - int len) -{ - struct mux_subch *sch = &mx->subch[s_nr]; - int list_len = llist_len(&sch->tx_queue); - struct subch_txq_entry *tqe = talloc_zero_size(tall_tqe_ctx, - sizeof(*tqe) + len); - if (!tqe) - return -ENOMEM; - - tqe->bit_len = len; - memcpy(tqe->bits, data, len); - - if (list_len > 2) - tx_queue_evict(sch, list_len-2); - - llist_add_tail(&tqe->list, &sch->tx_queue); - - return 0; -} - -/* initialize one subchannel muxer instance */ -int subchan_mux_init(struct subch_mux *mx) -{ - int i; - - memset(mx, 0, sizeof(*mx)); - for (i = 0; i < NR_SUBCH; i++) { - struct mux_subch *sch = &mx->subch[i]; - INIT_LLIST_HEAD(&sch->tx_queue); - } - - return 0; -} diff --git a/openbsc/src/trau/trau_frame.c b/openbsc/src/trau/trau_frame.c deleted file mode 100644 index d4d6447cc..000000000 --- a/openbsc/src/trau/trau_frame.c +++ /dev/null @@ -1,260 +0,0 @@ -/* TRAU frame handling according to GSM TS 08.60 */ - -/* (C) 2009 by Harald Welte - * All Rights Reserved - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation; either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - * - */ - -#include -#include -#include -#include -#include - -#include -#include -#include - -static u_int32_t get_bits(const u_int8_t *bitbuf, int offset, int num) -{ - int i; - u_int32_t ret = 0; - - for (i = offset; i < offset + num; i++) { - ret = ret << 1; - if (bitbuf[i]) - ret |= 1; - } - return ret; -} - -/* Decode according to 3.1.1 */ -static void decode_fr(struct decoded_trau_frame *fr, const u_int8_t *trau_bits) -{ - int i; - int d_idx = 0; - - /* C1 .. C15 */ - memcpy(fr->c_bits+0, trau_bits+17, 15); - /* C16 .. C21 */ - memcpy(fr->c_bits+15, trau_bits+310, 6); - /* T1 .. T4 */ - memcpy(fr->t_bits+0, trau_bits+316, 4); - /* D1 .. D255 */ - for (i = 32; i < 304; i+= 16) { - memcpy(fr->d_bits + d_idx, trau_bits+i+1, 15); - d_idx += 15; - } - /* D256 .. D260 */ - memcpy(fr->d_bits + d_idx, trau_bits + 305, 5); -} - -/* Decode according to 3.1.2 */ -static void decode_amr(struct decoded_trau_frame *fr, const u_int8_t *trau_bits) -{ - int i; - int d_idx = 0; - - /* C1 .. C15 */ - memcpy(fr->c_bits+0, trau_bits+17, 15); - /* C16 .. C25 */ - memcpy(fr->c_bits+15, trau_bits+33, 10); - /* T1 .. T4 */ - memcpy(fr->t_bits+0, trau_bits+316, 4); - /* D1 .. D5 */ - memcpy(fr->d_bits, trau_bits+43, 5); - /* D6 .. D245 */ - for (i = 48; i < 304; i += 16) { - memcpy(fr->d_bits + d_idx, trau_bits+i+1, 15); - d_idx += 15; - } - /* D246 .. D256 */ - memcpy(fr->d_bits + d_idx, trau_bits + 305, 11); -} - -int decode_trau_frame(struct decoded_trau_frame *fr, const u_int8_t *trau_bits) -{ - u_int8_t cbits5 = get_bits(trau_bits, 17, 5); - - switch (cbits5) { - case TRAU_FT_FR_UP: - case TRAU_FT_FR_DOWN: - case TRAU_FT_IDLE_UP: - case TRAU_FT_IDLE_DOWN: - case TRAU_FT_EFR: - decode_fr(fr, trau_bits); - break; - case TRAU_FT_AMR: - decode_amr(fr, trau_bits); - break; - case TRAU_FT_OM_UP: - case TRAU_FT_OM_DOWN: - case TRAU_FT_DATA_UP: - case TRAU_FT_DATA_DOWN: - case TRAU_FT_D145_SYNC: - case TRAU_FT_EDATA: - LOGP(DMUX, LOGL_NOTICE, "can't decode unimplemented TRAU " - "Frame Type 0x%02x\n", cbits5); - return -1; - break; - default: - LOGP(DMUX, LOGL_NOTICE, "can't decode unknown TRAU " - "Frame Type 0x%02x\n", cbits5); - return -1; - break; - } - - return 0; -} - -const u_int8_t ft_fr_down_bits[] = { 1, 1, 1, 0, 0 }; -const u_int8_t ft_idle_down_bits[] = { 0, 1, 1, 1, 0 }; - -/* modify an uplink TRAU frame so we can send it downlink */ -int trau_frame_up2down(struct decoded_trau_frame *fr) -{ - u_int8_t cbits5 = get_bits(fr->c_bits, 0, 5); - - switch (cbits5) { - case TRAU_FT_FR_UP: - memcpy(fr->c_bits, ft_fr_down_bits, 5); - /* clear time alignment */ - memset(fr->c_bits+5, 0, 6); - /* FIXME: SP / BFI in case of DTx */ - /* C12 .. C21 are spare and coded as '1' */ - memset(fr->c_bits+11, 0x01, 10); - break; - case TRAU_FT_EFR: - /* clear time alignment */ - memset(fr->c_bits+5, 0, 6); - /* FIXME: set UFE appropriately */ - /* FIXME: SP / BFI in case of DTx */ - break; - case TRAU_FT_IDLE_UP: - memcpy(fr->c_bits, ft_idle_down_bits, 5); - /* clear time alignment */ - memset(fr->c_bits+5, 0, 6); - /* FIXME: SP / BFI in case of DTx */ - /* C12 .. C21 are spare and coded as '1' */ - memset(fr->c_bits+11, 0x01, 10); - break; - case TRAU_FT_FR_DOWN: - case TRAU_FT_IDLE_DOWN: - case TRAU_FT_OM_DOWN: - case TRAU_FT_DATA_DOWN: - /* we cannot convert a downlink to a downlink frame */ - return -EINVAL; - break; - case TRAU_FT_AMR: - case TRAU_FT_OM_UP: - case TRAU_FT_DATA_UP: - case TRAU_FT_D145_SYNC: - case TRAU_FT_EDATA: - LOGP(DMUX, LOGL_NOTICE, "unimplemented TRAU Frame Type " - "0x%02x\n", cbits5); - return -1; - break; - default: - LOGP(DMUX, LOGL_NOTICE, "unknown TRAU Frame Type " - "0x%02x\n", cbits5); - return -1; - break; - } - - return 0; - -} - -static void encode_fr(u_int8_t *trau_bits, const struct decoded_trau_frame *fr) -{ - int i; - int d_idx = 0; - - trau_bits[16] = 1; - /* C1 .. C15 */ - memcpy(trau_bits+17, fr->c_bits+0, 15); - /* D1 .. D255 */ - for (i = 32; i < 304; i+= 16) { - trau_bits[i] = 1; - memcpy(trau_bits+i+1, fr->d_bits + d_idx, 15); - d_idx += 15; - } - /* D256 .. D260 */ - trau_bits[304] = 1; - memcpy(trau_bits + 305, fr->d_bits + d_idx, 5); - /* C16 .. C21 */ - memcpy(trau_bits+310, fr->c_bits+15, 6); - - /* FIXME: handle timing adjustment */ - - /* T1 .. T4 */ - memcpy(trau_bits+316, fr->t_bits+0, 4); -} - - -int encode_trau_frame(u_int8_t *trau_bits, const struct decoded_trau_frame *fr) -{ - u_int8_t cbits5 = get_bits(fr->c_bits, 0, 5); - - /* 16 bits of sync header */ - memset(trau_bits, 0, 16); - - switch (cbits5) { - case TRAU_FT_FR_UP: - case TRAU_FT_FR_DOWN: - case TRAU_FT_IDLE_UP: - case TRAU_FT_IDLE_DOWN: - case TRAU_FT_EFR: - encode_fr(trau_bits, fr); - break; - case TRAU_FT_AMR: - case TRAU_FT_OM_UP: - case TRAU_FT_OM_DOWN: - case TRAU_FT_DATA_UP: - case TRAU_FT_DATA_DOWN: - case TRAU_FT_D145_SYNC: - case TRAU_FT_EDATA: - LOGP(DMUX, LOGL_NOTICE, "unimplemented TRAU Frame Type " - "0x%02x\n", cbits5); - return -1; - break; - default: - LOGP(DMUX, LOGL_NOTICE, "unknown TRAU Frame Type " - "0x%02x\n", cbits5); - return -1; - break; - } - - return 0; -} - -static struct decoded_trau_frame fr_idle_frame = { - .c_bits = { 0, 1, 1, 1, 0 }, /* IDLE DOWNLINK 3.5.5 */ - .t_bits = { 1, 1, 1, 1 }, -}; -static u_int8_t encoded_idle_frame[TRAU_FRAME_BITS]; -static int dbits_initted; - -u_int8_t *trau_idle_frame(void) -{ - /* only initialize during the first call */ - if (!dbits_initted) { - /* set all D-bits to 1 */ - memset(&fr_idle_frame.d_bits, 0x01, 260); - encode_fr(encoded_idle_frame, &fr_idle_frame); - } - return encoded_idle_frame; -} diff --git a/openbsc/src/trau/trau_mux.c b/openbsc/src/trau/trau_mux.c deleted file mode 100644 index 712e22d85..000000000 --- a/openbsc/src/trau/trau_mux.c +++ /dev/null @@ -1,312 +0,0 @@ -/* Simple TRAU frame reflector to route voice calls */ - -/* (C) 2009 by Harald Welte - * All Rights Reserved - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation; either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - * - */ - -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include - -u_int8_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 -}; - -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; - u_int32_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(DMIB, 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, u_int32_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 u_int8_t c_bits_check[] = { 0, 0, 0, 1, 0 }; - -/* we get called by subchan_demux */ -int trau_mux_input(struct gsm_e1_subslot *src_e1_ss, - const u_int8_t *trau_bits, int num_bits) -{ - struct decoded_trau_frame tf; - u_int8_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; - struct gsm_data_frame *frame; - unsigned char *data; - int i, j, k, l, o; - /* 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, sizeof(c_bits_check))) - DEBUGPC(DMUX, "illegal trau (C1-C5) %s\n", - hexdump(tf.c_bits, sizeof(c_bits_check))); - msg = msgb_alloc(sizeof(struct gsm_data_frame) + 33, - "GSM-DATA"); - if (!msg) - return -ENOMEM; - - 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))); - if (--k < 0) { - o += gsm_fr_map[l]; - k = gsm_fr_map[++l]-1; - } - i++; - j++; - } - frame->msg_type = GSM_TCHF_FRAME; - frame->callref = ue->callref; - 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); -} - -/* add receiver instance for lchan and callref */ -int trau_recv_lchan(struct gsm_lchan *lchan, u_int32_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; -} - -int trau_send_frame(struct gsm_lchan *lchan, struct gsm_data_frame *frame) -{ - u_int8_t trau_bits_out[TRAU_FRAME_BITS]; - struct gsm_e1_subslot *dst_e1_ss = &lchan->ts->e1_link; - struct subch_mux *mx; - int i, j, k, l, o; - unsigned char *data = frame->data; - 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: - /* 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; - if (--k < 0) { - o += gsm_fr_map[l]; - k = gsm_fr_map[++l]-1; - } - i++; - j++; - } - break; - default: - DEBUGPC(DMUX, "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); -} diff --git a/openbsc/src/trau/trau_upqueue.c b/openbsc/src/trau/trau_upqueue.c deleted file mode 100644 index f8edaf0ff..000000000 --- a/openbsc/src/trau/trau_upqueue.c +++ /dev/null @@ -1,27 +0,0 @@ -/* trau_upqueue.c - Pass msgb's up the chain */ - -/* (C) 2010 by Harald Welte - * All Rights Reserved - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation; either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - * - */ - -#include -#include - -void trau_tx_to_mncc(struct gsm_network *net, struct msgb *msg) -{ - net->mncc_recv(net, msg); -} diff --git a/openbsc/src/utils/Makefile.am b/openbsc/src/utils/Makefile.am index 2847c6ad8..2351f8a46 100644 --- a/openbsc/src/utils/Makefile.am +++ b/openbsc/src/utils/Makefile.am @@ -5,6 +5,8 @@ AM_LDFLAGS = $(LIBOSMOCORE_LIBS) $(COVERAGE_LDFLAGS) bin_PROGRAMS = bs11_config isdnsync bs11_config_SOURCES = bs11_config.c rs232.c -bs11_config_LDADD = ../common/libcommon.a ../abis/libabis.a ../bsc/libbsc.a +bs11_config_LDADD = $(top_builddir)/src/libcommon/libcommon.a \ + $(top_builddir)/src/libabis/libabis.a \ + $(top_builddir)/src/libbsc/libbsc.a isdnsync_SOURCES = isdnsync.c diff --git a/openbsc/tests/channel/Makefile.am b/openbsc/tests/channel/Makefile.am index 8593b5a13..bf709ff28 100644 --- a/openbsc/tests/channel/Makefile.am +++ b/openbsc/tests/channel/Makefile.am @@ -3,14 +3,8 @@ AM_CFLAGS=-Wall -ggdb3 $(LIBOSMOCORE_CFLAGS) noinst_PROGRAMS = channel_test -channel_test_SOURCES = channel_test.c \ - $(top_srcdir)/src/db.c \ - $(top_srcdir)/src/gsm_subscriber_base.c \ - $(top_srcdir)/src/gsm_subscriber.c \ - $(top_srcdir)/src/debug.c \ - $(top_srcdir)/src/gsm_data.c \ - $(top_srcdir)/src/abis_nm.c \ - $(top_srcdir)/src/bts_ipaccess_nanobts.c \ - $(top_srcdir)/src/bts_siemens_bs11.c -channel_test_LDADD = -ldl -ldbi $(LIBOSMOCORE_LIBS) - +channel_test_SOURCES = channel_test.c +channel_test_LDADD = -ldl -ldbi $(LIBOSMOCORE_LIBS) \ + $(top_builddir)/src/libcommon/libcommon.a \ + $(top_builddir)/src/libbsc/libbsc.a \ + $(top_builddir)/src/libmsc/libmsc.a diff --git a/openbsc/tests/db/Makefile.am b/openbsc/tests/db/Makefile.am index a7bed85df..a4395aefa 100644 --- a/openbsc/tests/db/Makefile.am +++ b/openbsc/tests/db/Makefile.am @@ -5,5 +5,11 @@ AM_LDFLAGS = $(COVERAGE_LDFLAGS) noinst_PROGRAMS = db_test db_test_SOURCES = db_test.c -db_test_LDADD = $(top_builddir)/src/libbsc.a $(top_builddir)/src/libmsc.a $(top_builddir)/src/libbsc.a $(LIBOSMOCORE_LIBS) -ldl -ldbi +db_test_LDADD = $(top_builddir)/src/libbsc/libbsc.a \ + $(top_builddir)/src/libmsc/libmsc.a \ + $(top_builddir)/src/libbsc/libbsc.a \ + $(top_builddir)/src/libabis/libabis.a \ + $(top_builddir)/src/libtrau/libtrau.a \ + $(top_builddir)/src/libcommon/libcommon.a \ + $(LIBOSMOCORE_LIBS) -ldl -ldbi diff --git a/openbsc/tests/debug/Makefile.am b/openbsc/tests/debug/Makefile.am index 8423fd178..47437b1e8 100644 --- a/openbsc/tests/debug/Makefile.am +++ b/openbsc/tests/debug/Makefile.am @@ -2,5 +2,6 @@ INCLUDES = $(all_includes) -I$(top_srcdir)/include AM_CFLAGS=-Wall $(LIBOSMOCORE_CFLAGS) noinst_PROGRAMS = debug_test -debug_test_SOURCES = debug_test.c $(top_srcdir)/src/debug.c -debug_test_LDADD = $(LIBOSMOCORE_LIBS) +debug_test_SOURCES = debug_test.c +debug_test_LDADD = $(LIBOSMOCORE_LIBS) \ + $(top_srcdir)/src/libcommon/libcommon.a diff --git a/openbsc/tests/gsm0408/Makefile.am b/openbsc/tests/gsm0408/Makefile.am index f98c673ea..de6feb272 100644 --- a/openbsc/tests/gsm0408/Makefile.am +++ b/openbsc/tests/gsm0408/Makefile.am @@ -3,4 +3,7 @@ AM_CFLAGS=-Wall $(LIBOSMOCORE_CFLAGS) noinst_PROGRAMS = gsm0408_test gsm0408_test_SOURCES = gsm0408_test.c -gsm0408_test_LDADD = $(top_builddir)/src/libbsc.a $(top_builddir)/src/libmsc.a $(top_builddir)/src/libbsc.a $(LIBOSMOCORE_LIBS) -ldbi +gsm0408_test_LDADD = $(top_builddir)/src/libbsc/libbsc.a \ + $(top_builddir)/src/libmsc/libmsc.a \ + $(top_builddir)/src/libbsc/libbsc.a \ + $(LIBOSMOCORE_LIBS) -ldbi diff --git a/openbsc/tests/mgcp/Makefile.am b/openbsc/tests/mgcp/Makefile.am index fdbe56549..472368cb3 100644 --- a/openbsc/tests/mgcp/Makefile.am +++ b/openbsc/tests/mgcp/Makefile.am @@ -4,7 +4,9 @@ AM_LDFLAGS = $(COVERAGE_LDFLAGS) noinst_PROGRAMS = mgcp_test -mgcp_test_SOURCES = mgcp_test.c \ - $(top_srcdir)/src/mgcp/mgcp_protocol.c \ - $(top_srcdir)/src/mgcp/mgcp_network.c -mgcp_test_LDADD = $(top_builddir)/src/libbsc.a $(LIBOSMOCORE_LIBS) -lrt $(LIBOSMOSCCP_LIBS) $(LIBOSMOVTY_LIBS) +mgcp_test_SOURCES = mgcp_test.c + +mgcp_test_LDADD = $(top_builddir)/src/libbsc/libbsc.a \ + $(top_builddir)/src/libmgcp/libmgcp.a \ + $(top_builddir)/src/libcommon/libcommon.a \ + $(LIBOSMOCORE_LIBS) -lrt $(LIBOSMOSCCP_LIBS) $(LIBOSMOVTY_LIBS) -- cgit v1.2.3