From d31c3fbaeac88aa3e871e9139d19270b4fd43839 Mon Sep 17 00:00:00 2001 From: Neels Hofmeyr Date: Tue, 4 Jul 2017 23:08:44 +0200 Subject: [PATCH] move openbsc/* to repos root This is the first step in creating this repository from the legacy openbsc.git. Like all other Osmocom repositories, keep the autoconf and automake files in the repository root. openbsc.git has been the sole exception, which ends now. Change-Id: I9c6f2a448d9cb1cc088cf1cf6918b69d7e69b4e7 --- openbsc/AUTHORS => AUTHORS | 0 openbsc/COPYING => COPYING | 0 openbsc/Makefile.am => Makefile.am | 0 openbsc/README => README | 0 openbsc/README.vty-tests => README.vty-tests | 0 openbsc/configure.ac => configure.ac | 0 .../a-link/sccp-split-by-con.lua | 0 {openbsc/contrib => contrib}/bsc-test/README | 0 .../contrib => contrib}/bsc-test/all_dial | 0 {openbsc/contrib => contrib}/bsc-test/dial.sh | 0 .../contrib => contrib}/bsc-test/drop-oml.sh | 0 {openbsc/contrib => contrib}/bsc-test/drop.sh | 0 {openbsc/contrib => contrib}/bsc-test/hangup | 0 {openbsc/contrib => contrib}/bsc-test/msc.sh | 0 {openbsc/contrib => contrib}/bsc_control.py | 0 {openbsc/contrib => contrib}/bt.py | 0 .../contrib => contrib}/convert_to_enum.py | 0 {openbsc/contrib => contrib}/ctrl2sse.py | 0 .../gprs/gb-proxy-unblock-bug.py | 0 .../gprs/gprs-bssgp-histogram.lua | 0 .../gprs/gprs-buffer-count.lua | 0 .../gprs/gprs-split-trace-by-tlli.lua | 0 .../gprs/gprs-verify-nu.lua | 0 .../contrib => contrib}/hlr-remove-old.sql | 0 .../contrib => contrib}/hlrsync/hlrsync.py | 0 {openbsc/contrib => contrib}/ipa.py | 0 contrib/jenkins.sh | 1 - {openbsc/contrib => contrib}/mgcp_server.py | 0 .../contrib => contrib}/nat/test_regexp.c | 0 .../contrib => contrib}/nat/ussd_example.py | 0 .../rtp/gen_rtp_header.erl | 0 .../contrib => contrib}/rtp/rtp_replay.st | 0 .../rtp/rtp_replay_shared.st | 0 .../contrib => contrib}/rtp/rtp_replay_sip.st | 0 .../contrib => contrib}/rtp/timestamp_rtp.lua | 0 {openbsc/contrib => contrib}/sms/fill-hlr.st | 0 {openbsc/contrib => contrib}/sms/hlr-query.st | 0 .../sms/sqlite-probe.tap.d | 0 {openbsc/contrib => contrib}/soap.py | 0 .../systemd/osmo-bsc-mgcp.service | 0 .../systemd/osmo-bsc.service | 0 .../systemd/osmo-gbproxy.service | 0 .../systemd/osmo-nitb.service | 0 .../systemd/osmo-sgsn.service | 0 .../contrib => contrib}/testconv/Makefile | 0 .../testconv/testconv_main.c | 0 {openbsc/contrib => contrib}/twisted_ipa.py | 0 {openbsc/doc => doc}/BS11-OML.txt | 0 {openbsc/doc => doc}/Makefile.am | 0 {openbsc/doc => doc}/call-routing.txt | 0 {openbsc/doc => doc}/channel_release.txt | 0 {openbsc/doc => doc}/e1-data-model.txt | 0 {openbsc/doc => doc}/examples/Makefile.am | 0 .../examples/osmo-bsc/osmo-bsc.cfg | 0 .../examples/osmo-bsc_mgcp/mgcp.cfg | 0 .../examples/osmo-bsc_nat/black-list.cfg | 0 .../examples/osmo-bsc_nat/bscs.config | 0 .../examples/osmo-bsc_nat/osmo-bsc_nat.cfg | 0 .../osmo-gbproxy/osmo-gbproxy-legacy.cfg | 0 .../examples/osmo-gbproxy/osmo-gbproxy.cfg | 0 .../examples/osmo-gtphub/gtphub-example.txt | 0 .../osmo-gtphub/osmo-gtphub-1iface.cfg | 0 .../examples/osmo-gtphub/osmo-gtphub.cfg | 0 .../bs11/openbsc-1bts-2trx-hopping.cfg | 0 .../osmo-nitb/bs11/openbsc-1bts-2trx.cfg | 0 .../osmo-nitb/bs11/openbsc-2bts-2trx.cfg | 0 .../examples/osmo-nitb/bs11/openbsc.cfg | 0 .../osmo-nitb/nanobts/openbsc-multitrx.cfg | 0 .../examples/osmo-nitb/nanobts/openbsc.cfg | 0 .../osmo-nitb/nokia/openbsc_nokia_3trx.cfg | 0 .../examples/osmo-nitb/rbs2308/openbsc.cfg | 0 .../examples/osmo-nitb/sysmobts/openbsc.cfg | 0 .../examples/osmo-sgsn/osmo-sgsn.cfg | 0 {openbsc/doc => doc}/gsm-hopping.txt | 0 {openbsc/doc => doc}/handover.txt | 0 {openbsc/doc => doc}/ipa-sccp.txt | 0 {openbsc/doc => doc}/oml-interface.txt | 0 .../doc => doc}/osmo-nitb-data_structures.dot | 0 {openbsc/doc => doc}/paging.txt | 0 openbsc/git-version-gen => git-version-gen | 0 {openbsc/m4 => m4}/README | 0 {openbsc/m4 => m4}/ax_check_compile_flag.m4 | 0 openbsc/openbsc.pc.in => openbsc.pc.in | 0 openbsc/include/Makefile.am | 8 - openbsc/include/compat_af_isdn.h | 39 - openbsc/include/mISDNif.h | 387 -- openbsc/include/openbsc/Makefile.am | 100 - openbsc/include/openbsc/abis_nm.h | 180 - openbsc/include/openbsc/abis_om2000.h | 129 - openbsc/include/openbsc/abis_rsl.h | 118 - openbsc/include/openbsc/arfcn_range_encode.h | 26 - openbsc/include/openbsc/auth.h | 26 - openbsc/include/openbsc/bsc_api.h | 55 - openbsc/include/openbsc/bsc_msc.h | 65 - openbsc/include/openbsc/bsc_msc_data.h | 142 - openbsc/include/openbsc/bsc_msg_filter.h | 107 - openbsc/include/openbsc/bsc_nat.h | 462 -- openbsc/include/openbsc/bsc_nat_callstats.h | 55 - openbsc/include/openbsc/bsc_nat_sccp.h | 105 - openbsc/include/openbsc/bsc_rll.h | 19 - openbsc/include/openbsc/bsc_subscriber.h | 43 - openbsc/include/openbsc/bss.h | 20 - .../openbsc/bts_ipaccess_nanobts_omlattr.h | 32 - openbsc/include/openbsc/chan_alloc.h | 54 - openbsc/include/openbsc/common_bsc.h | 9 - openbsc/include/openbsc/common_cs.h | 27 - openbsc/include/openbsc/crc24.h | 10 - openbsc/include/openbsc/ctrl.h | 4 - openbsc/include/openbsc/db.h | 86 - openbsc/include/openbsc/debug.h | 49 - openbsc/include/openbsc/e1_config.h | 11 - openbsc/include/openbsc/gb_proxy.h | 288 -- openbsc/include/openbsc/gprs_gb_parse.h | 59 - openbsc/include/openbsc/gprs_gmm.h | 35 - openbsc/include/openbsc/gprs_llc.h | 284 -- openbsc/include/openbsc/gprs_llc_xid.h | 57 - openbsc/include/openbsc/gprs_sgsn.h | 473 -- openbsc/include/openbsc/gprs_sndcp.h | 79 - openbsc/include/openbsc/gprs_sndcp_comp.h | 82 - openbsc/include/openbsc/gprs_sndcp_dcomp.h | 53 - openbsc/include/openbsc/gprs_sndcp_pcomp.h | 46 - openbsc/include/openbsc/gprs_sndcp_xid.h | 218 - openbsc/include/openbsc/gprs_subscriber.h | 31 - openbsc/include/openbsc/gprs_utils.h | 45 - openbsc/include/openbsc/gsm_04_08.h | 85 - openbsc/include/openbsc/gsm_04_11.h | 47 - openbsc/include/openbsc/gsm_04_80.h | 25 - openbsc/include/openbsc/gsm_data.h | 596 --- openbsc/include/openbsc/gsm_data_shared.h | 994 ---- openbsc/include/openbsc/gsm_subscriber.h | 130 - openbsc/include/openbsc/gsup_client.h | 60 - openbsc/include/openbsc/gtphub.h | 523 -- openbsc/include/openbsc/handover.h | 14 - openbsc/include/openbsc/handover_decision.h | 7 - openbsc/include/openbsc/ipaccess.h | 52 - openbsc/include/openbsc/iu.h | 62 - openbsc/include/openbsc/meas_feed.h | 41 - openbsc/include/openbsc/meas_rep.h | 67 - openbsc/include/openbsc/mgcp.h | 285 -- openbsc/include/openbsc/mgcp_internal.h | 342 -- openbsc/include/openbsc/mgcp_transcode.h | 90 - openbsc/include/openbsc/misdn.h | 27 - openbsc/include/openbsc/mncc.h | 219 - openbsc/include/openbsc/mncc_int.h | 14 - openbsc/include/openbsc/nat_rewrite_trie.h | 47 - openbsc/include/openbsc/network_listen.h | 16 - openbsc/include/openbsc/oap_client.h | 82 - openbsc/include/openbsc/openbscdefines.h | 34 - openbsc/include/openbsc/osmo_bsc.h | 71 - openbsc/include/openbsc/osmo_bsc_grace.h | 35 - openbsc/include/openbsc/osmo_bsc_rf.h | 66 - openbsc/include/openbsc/osmo_msc.h | 11 - openbsc/include/openbsc/osmux.h | 42 - openbsc/include/openbsc/paging.h | 77 - openbsc/include/openbsc/pcu_if.h | 35 - openbsc/include/openbsc/pcuif_proto.h | 176 - openbsc/include/openbsc/rest_octets.h | 139 - openbsc/include/openbsc/rrlp.h | 7 - openbsc/include/openbsc/rs232.h | 9 - openbsc/include/openbsc/rtp_proxy.h | 95 - openbsc/include/openbsc/sgsn.h | 186 - openbsc/include/openbsc/signal.h | 262 - openbsc/include/openbsc/silent_call.h | 15 - openbsc/include/openbsc/slhc.h | 187 - openbsc/include/openbsc/smpp.h | 4 - openbsc/include/openbsc/sms_queue.h | 17 - openbsc/include/openbsc/socket.h | 14 - openbsc/include/openbsc/system_information.h | 22 - openbsc/include/openbsc/token_auth.h | 7 - openbsc/include/openbsc/transaction.h | 79 - openbsc/include/openbsc/trau_mux.h | 70 - openbsc/include/openbsc/trau_upqueue.h | 7 - openbsc/include/openbsc/ussd.h | 10 - openbsc/include/openbsc/v42bis.h | 147 - openbsc/include/openbsc/v42bis_private.h | 126 - openbsc/include/openbsc/vty.h | 51 - openbsc/src/Makefile.am | 60 - openbsc/src/gprs/.gitignore | 2 - openbsc/src/gprs/Makefile.am | 132 - openbsc/src/gprs/crc24.c | 67 - openbsc/src/gprs/gb_proxy.c | 1437 ------ openbsc/src/gprs/gb_proxy_main.c | 315 -- openbsc/src/gprs/gb_proxy_patch.c | 458 -- openbsc/src/gprs/gb_proxy_peer.c | 218 - openbsc/src/gprs/gb_proxy_tlli.c | 723 --- openbsc/src/gprs/gb_proxy_vty.c | 852 ---- openbsc/src/gprs/gprs_gb_parse.c | 636 --- openbsc/src/gprs/gprs_gmm.c | 2939 ----------- openbsc/src/gprs/gprs_llc.c | 1132 ----- openbsc/src/gprs/gprs_llc_parse.c | 251 - openbsc/src/gprs/gprs_llc_vty.c | 116 - openbsc/src/gprs/gprs_llc_xid.c | 281 -- openbsc/src/gprs/gprs_sgsn.c | 895 ---- openbsc/src/gprs/gprs_sndcp.c | 1258 ----- openbsc/src/gprs/gprs_sndcp_comp.c | 323 -- openbsc/src/gprs/gprs_sndcp_dcomp.c | 358 -- openbsc/src/gprs/gprs_sndcp_pcomp.c | 282 -- openbsc/src/gprs/gprs_sndcp_vty.c | 71 - openbsc/src/gprs/gprs_sndcp_xid.c | 1822 ------- openbsc/src/gprs/gprs_subscriber.c | 921 ---- openbsc/src/gprs/gprs_utils.c | 274 -- openbsc/src/gprs/gtphub.c | 2931 ----------- openbsc/src/gprs/gtphub_ares.c | 220 - openbsc/src/gprs/gtphub_main.c | 357 -- openbsc/src/gprs/gtphub_sock.c | 60 - openbsc/src/gprs/gtphub_vty.c | 613 --- openbsc/src/gprs/osmo_sgsn.cfg | 23 - openbsc/src/gprs/sgsn_ares.c | 173 - openbsc/src/gprs/sgsn_auth.c | 312 -- openbsc/src/gprs/sgsn_cdr.c | 258 - openbsc/src/gprs/sgsn_ctrl.c | 69 - openbsc/src/gprs/sgsn_libgtp.c | 860 ---- openbsc/src/gprs/sgsn_main.c | 462 -- openbsc/src/gprs/sgsn_vty.c | 1323 ----- openbsc/src/gprs/slhc.c | 813 ---- openbsc/src/gprs/v42bis.c | 767 --- openbsc/src/ipaccess/Makefile.am | 66 - openbsc/src/ipaccess/abisip-find.c | 216 - openbsc/src/ipaccess/ipaccess-config.c | 1019 ---- openbsc/src/ipaccess/ipaccess-firmware.c | 135 - openbsc/src/ipaccess/ipaccess-proxy.c | 1226 ----- openbsc/src/ipaccess/network_listen.c | 257 - openbsc/src/libbsc/Makefile.am | 57 - openbsc/src/libbsc/abis_nm.c | 2917 ----------- openbsc/src/libbsc/abis_nm_ipaccess.c | 87 - openbsc/src/libbsc/abis_nm_vty.c | 191 - openbsc/src/libbsc/abis_om2000.c | 2776 ----------- openbsc/src/libbsc/abis_om2000_vty.c | 609 --- openbsc/src/libbsc/abis_rsl.c | 2927 ----------- openbsc/src/libbsc/arfcn_range_encode.c | 330 -- openbsc/src/libbsc/bsc_api.c | 894 ---- openbsc/src/libbsc/bsc_ctrl_commands.c | 459 -- openbsc/src/libbsc/bsc_ctrl_lookup.c | 107 - openbsc/src/libbsc/bsc_dyn_ts.c | 77 - openbsc/src/libbsc/bsc_init.c | 552 --- openbsc/src/libbsc/bsc_msc.c | 320 -- openbsc/src/libbsc/bsc_rf_ctrl.c | 534 -- openbsc/src/libbsc/bsc_rll.c | 139 - openbsc/src/libbsc/bsc_subscriber.c | 168 - openbsc/src/libbsc/bsc_vty.c | 4304 ----------------- openbsc/src/libbsc/bts_ericsson_rbs2000.c | 204 - openbsc/src/libbsc/bts_init.c | 30 - openbsc/src/libbsc/bts_ipaccess_nanobts.c | 520 -- .../src/libbsc/bts_ipaccess_nanobts_omlattr.c | 240 - openbsc/src/libbsc/bts_nokia_site.c | 1739 ------- openbsc/src/libbsc/bts_siemens_bs11.c | 602 --- openbsc/src/libbsc/bts_sysmobts.c | 60 - openbsc/src/libbsc/bts_unknown.c | 40 - openbsc/src/libbsc/chan_alloc.c | 543 --- openbsc/src/libbsc/e1_config.c | 297 -- openbsc/src/libbsc/gsm_04_08_utils.c | 687 --- openbsc/src/libbsc/gsm_04_80_utils.c | 40 - openbsc/src/libbsc/handover_decision.c | 304 -- openbsc/src/libbsc/handover_logic.c | 378 -- openbsc/src/libbsc/meas_proc.c | 84 - openbsc/src/libbsc/meas_rep.c | 115 - openbsc/src/libbsc/net_init.c | 69 - openbsc/src/libbsc/paging.c | 449 -- openbsc/src/libbsc/pcu_sock.c | 742 --- openbsc/src/libbsc/rest_octets.c | 860 ---- openbsc/src/libbsc/system_information.c | 1169 ----- openbsc/src/libcommon-cs/Makefile.am | 20 - openbsc/src/libcommon-cs/common_cs.c | 133 - openbsc/src/libcommon-cs/common_cs_vty.c | 325 -- openbsc/src/libcommon/Makefile.am | 47 - openbsc/src/libcommon/bsc_version.c | 30 - openbsc/src/libcommon/common_vty.c | 145 - openbsc/src/libcommon/debug.c | 235 - openbsc/src/libcommon/gsm_data.c | 473 -- openbsc/src/libcommon/gsm_data_shared.c | 849 ---- openbsc/src/libcommon/gsm_subscriber_base.c | 163 - openbsc/src/libcommon/gsup_client.c | 341 -- openbsc/src/libcommon/gsup_test_client.c | 298 -- openbsc/src/libcommon/oap_client.c | 280 -- openbsc/src/libcommon/socket.c | 111 - openbsc/src/libcommon/talloc_ctx.c | 56 - openbsc/src/libfilter/Makefile.am | 26 - openbsc/src/libfilter/bsc_msg_acc.c | 118 - openbsc/src/libfilter/bsc_msg_filter.c | 398 -- openbsc/src/libfilter/bsc_msg_vty.c | 140 - openbsc/src/libiu/Makefile.am | 28 - openbsc/src/libiu/iu.c | 759 --- openbsc/src/libiu/iu_vty.c | 50 - openbsc/src/libmgcp/Makefile.am | 43 - openbsc/src/libmgcp/g711common.h | 187 - openbsc/src/libmgcp/mgcp_network.c | 1064 ---- openbsc/src/libmgcp/mgcp_osmux.c | 586 --- openbsc/src/libmgcp/mgcp_protocol.c | 1598 ------ openbsc/src/libmgcp/mgcp_sdp.c | 305 -- openbsc/src/libmgcp/mgcp_transcode.c | 612 --- openbsc/src/libmgcp/mgcp_vty.c | 1543 ------ openbsc/src/libmsc/Makefile.am | 58 - openbsc/src/libmsc/auth.c | 157 - openbsc/src/libmsc/ctrl_commands.c | 212 - openbsc/src/libmsc/db.c | 1752 ------- openbsc/src/libmsc/gsm_04_08.c | 4047 ---------------- openbsc/src/libmsc/gsm_04_11.c | 1070 ---- openbsc/src/libmsc/gsm_04_80.c | 155 - openbsc/src/libmsc/gsm_subscriber.c | 422 -- openbsc/src/libmsc/meas_feed.c | 167 - openbsc/src/libmsc/meas_feed.h | 12 - openbsc/src/libmsc/mncc.c | 107 - openbsc/src/libmsc/mncc_builtin.c | 426 -- openbsc/src/libmsc/mncc_sock.c | 318 -- openbsc/src/libmsc/osmo_msc.c | 177 - openbsc/src/libmsc/rrlp.c | 104 - openbsc/src/libmsc/silent_call.c | 152 - openbsc/src/libmsc/smpp_openbsc.c | 758 --- openbsc/src/libmsc/smpp_smsc.c | 1030 ---- openbsc/src/libmsc/smpp_smsc.h | 166 - openbsc/src/libmsc/smpp_utils.c | 62 - openbsc/src/libmsc/smpp_vty.c | 612 --- openbsc/src/libmsc/sms_queue.c | 544 --- openbsc/src/libmsc/token_auth.c | 160 - openbsc/src/libmsc/transaction.c | 163 - openbsc/src/libmsc/ussd.c | 95 - openbsc/src/libmsc/vty_interface_layer3.c | 1210 ----- openbsc/src/libtrau/Makefile.am | 31 - openbsc/src/libtrau/rtp_proxy.c | 764 --- openbsc/src/libtrau/trau_mux.c | 547 --- openbsc/src/libtrau/trau_upqueue.c | 27 - openbsc/src/osmo-bsc/Makefile.am | 55 - openbsc/src/osmo-bsc/osmo_bsc_api.c | 550 --- openbsc/src/osmo-bsc/osmo_bsc_audio.c | 73 - openbsc/src/osmo-bsc/osmo_bsc_bssap.c | 548 --- openbsc/src/osmo-bsc/osmo_bsc_ctrl.c | 680 --- openbsc/src/osmo-bsc/osmo_bsc_filter.c | 381 -- openbsc/src/osmo-bsc/osmo_bsc_grace.c | 169 - openbsc/src/osmo-bsc/osmo_bsc_main.c | 301 -- openbsc/src/osmo-bsc/osmo_bsc_msc.c | 586 --- openbsc/src/osmo-bsc/osmo_bsc_sccp.c | 328 -- openbsc/src/osmo-bsc/osmo_bsc_vty.c | 945 ---- openbsc/src/osmo-bsc_mgcp/Makefile.am | 35 - openbsc/src/osmo-bsc_mgcp/mgcp_main.c | 311 -- openbsc/src/osmo-bsc_nat/Makefile.am | 58 - openbsc/src/osmo-bsc_nat/bsc_filter.c | 218 - openbsc/src/osmo-bsc_nat/bsc_mgcp_utils.c | 1152 ----- openbsc/src/osmo-bsc_nat/bsc_nat.c | 1736 ------- openbsc/src/osmo-bsc_nat/bsc_nat_ctrl.c | 524 -- openbsc/src/osmo-bsc_nat/bsc_nat_filter.c | 119 - openbsc/src/osmo-bsc_nat/bsc_nat_rewrite.c | 714 --- .../src/osmo-bsc_nat/bsc_nat_rewrite_trie.c | 259 - openbsc/src/osmo-bsc_nat/bsc_nat_utils.c | 535 -- openbsc/src/osmo-bsc_nat/bsc_nat_vty.c | 1336 ----- openbsc/src/osmo-bsc_nat/bsc_sccp.c | 247 - openbsc/src/osmo-bsc_nat/bsc_ussd.c | 453 -- openbsc/src/osmo-nitb/Makefile.am | 45 - openbsc/src/osmo-nitb/bsc_hack.c | 402 -- openbsc/src/utils/Makefile.am | 147 - openbsc/src/utils/bs11_config.c | 953 ---- openbsc/src/utils/isdnsync.c | 189 - openbsc/src/utils/meas_db.c | 330 -- openbsc/src/utils/meas_db.h | 17 - openbsc/src/utils/meas_json.c | 190 - openbsc/src/utils/meas_pcap2db.c | 138 - openbsc/src/utils/meas_udp2db.c | 126 - openbsc/src/utils/meas_vis.c | 310 -- openbsc/src/utils/smpp_mirror.c | 329 -- openbsc/osmoappdesc.py => osmoappdesc.py | 0 {openbsc/tests => tests}/Makefile.am | 0 {openbsc/tests => tests}/abis/Makefile.am | 0 {openbsc/tests => tests}/abis/abis_test.c | 0 {openbsc/tests => tests}/abis/abis_test.ok | 0 {openbsc/tests => tests}/atlocal.in | 0 .../tests => tests}/bsc-nat-trie/Makefile.am | 0 .../bsc-nat-trie/bsc_nat_trie_test.c | 0 .../bsc-nat-trie/bsc_nat_trie_test.ok | 0 .../tests => tests}/bsc-nat-trie/prefixes.csv | 0 {openbsc/tests => tests}/bsc-nat/Makefile.am | 0 {openbsc/tests => tests}/bsc-nat/barr.cfg | 0 {openbsc/tests => tests}/bsc-nat/barr_dup.cfg | 0 {openbsc/tests => tests}/bsc-nat/bsc_data.c | 0 .../tests => tests}/bsc-nat/bsc_nat_test.c | 0 .../tests => tests}/bsc-nat/bsc_nat_test.ok | 0 {openbsc/tests => tests}/bsc-nat/prefixes.csv | 0 {openbsc/tests => tests}/bsc/Makefile.am | 0 {openbsc/tests => tests}/bsc/bsc_test.c | 0 {openbsc/tests => tests}/bsc/bsc_test.ok | 0 {openbsc/tests => tests}/channel/Makefile.am | 0 .../tests => tests}/channel/channel_test.c | 0 .../tests => tests}/channel/channel_test.ok | 0 {openbsc/tests => tests}/ctrl_test_runner.py | 0 {openbsc/tests => tests}/db/Makefile.am | 0 {openbsc/tests => tests}/db/db_test.c | 0 {openbsc/tests => tests}/db/db_test.err | 0 {openbsc/tests => tests}/db/db_test.ok | 0 {openbsc/tests => tests}/db/hlr.sqlite3 | Bin {openbsc/tests => tests}/gbproxy/Makefile.am | 0 .../tests => tests}/gbproxy/gbproxy_test.c | 0 .../tests => tests}/gbproxy/gbproxy_test.ok | 0 {openbsc/tests => tests}/gprs/Makefile.am | 0 {openbsc/tests => tests}/gprs/gprs_test.c | 0 {openbsc/tests => tests}/gprs/gprs_test.ok | 0 {openbsc/tests => tests}/gsm0408/Makefile.am | 0 .../tests => tests}/gsm0408/gsm0408_test.c | 0 .../tests => tests}/gsm0408/gsm0408_test.ok | 0 {openbsc/tests => tests}/gtphub/Makefile.am | 0 {openbsc/tests => tests}/gtphub/gtphub_test.c | 0 .../tests => tests}/gtphub/gtphub_test.ok | 0 {openbsc/tests => tests}/mgcp/Makefile.am | 0 {openbsc/tests => tests}/mgcp/mgcp_test.c | 0 {openbsc/tests => tests}/mgcp/mgcp_test.ok | 0 .../mgcp/mgcp_transcoding_test.c | 0 .../mgcp/mgcp_transcoding_test.ok | 0 {openbsc/tests => tests}/mm_auth/Makefile.am | 0 .../tests => tests}/mm_auth/mm_auth_test.c | 0 .../tests => tests}/mm_auth/mm_auth_test.ok | 0 .../nanobts_omlattr/Makefile.am | 0 .../nanobts_omlattr/nanobts_omlattr_test.c | 0 .../nanobts_omlattr/nanobts_omlattr_test.ok | 0 {openbsc/tests => tests}/oap/Makefile.am | 0 .../tests => tests}/oap/oap_client_test.c | 0 .../tests => tests}/oap/oap_client_test.err | 0 .../tests => tests}/oap/oap_client_test.ok | 0 {openbsc/tests => tests}/sgsn/Makefile.am | 0 {openbsc/tests => tests}/sgsn/sgsn_test.c | 0 {openbsc/tests => tests}/sgsn/sgsn_test.ok | 0 {openbsc/tests => tests}/slhc/Makefile.am | 0 {openbsc/tests => tests}/slhc/slhc_test.c | 0 {openbsc/tests => tests}/slhc/slhc_test.ok | 0 {openbsc/tests => tests}/smpp/Makefile.am | 0 {openbsc/tests => tests}/smpp/smpp_test.c | 0 {openbsc/tests => tests}/smpp/smpp_test.err | 0 {openbsc/tests => tests}/smpp/smpp_test.ok | 0 {openbsc/tests => tests}/smpp_test_runner.py | 0 .../tests => tests}/sndcp_xid/Makefile.am | 0 .../sndcp_xid/sndcp_xid_test.c | 0 .../sndcp_xid/sndcp_xid_test.ok | 0 {openbsc/tests => tests}/subscr/Makefile.am | 0 .../tests => tests}/subscr/bsc_subscr_test.c | 0 .../subscr/bsc_subscr_test.err | 0 .../tests => tests}/subscr/bsc_subscr_test.ok | 0 {openbsc/tests => tests}/subscr/subscr_test.c | 0 .../tests => tests}/subscr/subscr_test.ok | 0 {openbsc/tests => tests}/testsuite.at | 0 {openbsc/tests => tests}/trau/Makefile.am | 0 {openbsc/tests => tests}/trau/trau_test.c | 0 {openbsc/tests => tests}/trau/trau_test.ok | 0 {openbsc/tests => tests}/v42bis/Makefile.am | 0 {openbsc/tests => tests}/v42bis/v42bis_test.c | 0 .../tests => tests}/v42bis/v42bis_test.ok | 0 {openbsc/tests => tests}/vty_test_runner.py | 0 {openbsc/tests => tests}/xid/Makefile.am | 0 {openbsc/tests => tests}/xid/xid_test.c | 0 {openbsc/tests => tests}/xid/xid_test.ok | 0 {openbsc/tools => tools}/hlrstat.pl | 0 446 files changed, 106505 deletions(-) rename openbsc/AUTHORS => AUTHORS (100%) rename openbsc/COPYING => COPYING (100%) rename openbsc/Makefile.am => Makefile.am (100%) rename openbsc/README => README (100%) rename openbsc/README.vty-tests => README.vty-tests (100%) rename openbsc/configure.ac => configure.ac (100%) rename {openbsc/contrib => contrib}/a-link/sccp-split-by-con.lua (100%) rename {openbsc/contrib => contrib}/bsc-test/README (100%) rename {openbsc/contrib => contrib}/bsc-test/all_dial (100%) rename {openbsc/contrib => contrib}/bsc-test/dial.sh (100%) rename {openbsc/contrib => contrib}/bsc-test/drop-oml.sh (100%) rename {openbsc/contrib => contrib}/bsc-test/drop.sh (100%) rename {openbsc/contrib => contrib}/bsc-test/hangup (100%) rename {openbsc/contrib => contrib}/bsc-test/msc.sh (100%) rename {openbsc/contrib => contrib}/bsc_control.py (100%) rename {openbsc/contrib => contrib}/bt.py (100%) rename {openbsc/contrib => contrib}/convert_to_enum.py (100%) rename {openbsc/contrib => contrib}/ctrl2sse.py (100%) rename {openbsc/contrib => contrib}/gprs/gb-proxy-unblock-bug.py (100%) rename {openbsc/contrib => contrib}/gprs/gprs-bssgp-histogram.lua (100%) rename {openbsc/contrib => contrib}/gprs/gprs-buffer-count.lua (100%) rename {openbsc/contrib => contrib}/gprs/gprs-split-trace-by-tlli.lua (100%) rename {openbsc/contrib => contrib}/gprs/gprs-verify-nu.lua (100%) rename {openbsc/contrib => contrib}/hlr-remove-old.sql (100%) rename {openbsc/contrib => contrib}/hlrsync/hlrsync.py (100%) rename {openbsc/contrib => contrib}/ipa.py (100%) rename {openbsc/contrib => contrib}/mgcp_server.py (100%) rename {openbsc/contrib => contrib}/nat/test_regexp.c (100%) rename {openbsc/contrib => contrib}/nat/ussd_example.py (100%) rename {openbsc/contrib => contrib}/rtp/gen_rtp_header.erl (100%) rename {openbsc/contrib => contrib}/rtp/rtp_replay.st (100%) rename {openbsc/contrib => contrib}/rtp/rtp_replay_shared.st (100%) rename {openbsc/contrib => contrib}/rtp/rtp_replay_sip.st (100%) rename {openbsc/contrib => contrib}/rtp/timestamp_rtp.lua (100%) rename {openbsc/contrib => contrib}/sms/fill-hlr.st (100%) rename {openbsc/contrib => contrib}/sms/hlr-query.st (100%) rename {openbsc/contrib => contrib}/sms/sqlite-probe.tap.d (100%) rename {openbsc/contrib => contrib}/soap.py (100%) rename {openbsc/contrib => contrib}/systemd/osmo-bsc-mgcp.service (100%) rename {openbsc/contrib => contrib}/systemd/osmo-bsc.service (100%) rename {openbsc/contrib => contrib}/systemd/osmo-gbproxy.service (100%) rename {openbsc/contrib => contrib}/systemd/osmo-nitb.service (100%) rename {openbsc/contrib => contrib}/systemd/osmo-sgsn.service (100%) rename {openbsc/contrib => contrib}/testconv/Makefile (100%) rename {openbsc/contrib => contrib}/testconv/testconv_main.c (100%) rename {openbsc/contrib => contrib}/twisted_ipa.py (100%) rename {openbsc/doc => doc}/BS11-OML.txt (100%) rename {openbsc/doc => doc}/Makefile.am (100%) rename {openbsc/doc => doc}/call-routing.txt (100%) rename {openbsc/doc => doc}/channel_release.txt (100%) rename {openbsc/doc => doc}/e1-data-model.txt (100%) rename {openbsc/doc => doc}/examples/Makefile.am (100%) rename {openbsc/doc => doc}/examples/osmo-bsc/osmo-bsc.cfg (100%) rename {openbsc/doc => doc}/examples/osmo-bsc_mgcp/mgcp.cfg (100%) rename {openbsc/doc => doc}/examples/osmo-bsc_nat/black-list.cfg (100%) rename {openbsc/doc => doc}/examples/osmo-bsc_nat/bscs.config (100%) rename {openbsc/doc => doc}/examples/osmo-bsc_nat/osmo-bsc_nat.cfg (100%) rename {openbsc/doc => doc}/examples/osmo-gbproxy/osmo-gbproxy-legacy.cfg (100%) rename {openbsc/doc => doc}/examples/osmo-gbproxy/osmo-gbproxy.cfg (100%) rename {openbsc/doc => doc}/examples/osmo-gtphub/gtphub-example.txt (100%) rename {openbsc/doc => doc}/examples/osmo-gtphub/osmo-gtphub-1iface.cfg (100%) rename {openbsc/doc => doc}/examples/osmo-gtphub/osmo-gtphub.cfg (100%) rename {openbsc/doc => doc}/examples/osmo-nitb/bs11/openbsc-1bts-2trx-hopping.cfg (100%) rename {openbsc/doc => doc}/examples/osmo-nitb/bs11/openbsc-1bts-2trx.cfg (100%) rename {openbsc/doc => doc}/examples/osmo-nitb/bs11/openbsc-2bts-2trx.cfg (100%) rename {openbsc/doc => doc}/examples/osmo-nitb/bs11/openbsc.cfg (100%) rename {openbsc/doc => doc}/examples/osmo-nitb/nanobts/openbsc-multitrx.cfg (100%) rename {openbsc/doc => doc}/examples/osmo-nitb/nanobts/openbsc.cfg (100%) rename {openbsc/doc => doc}/examples/osmo-nitb/nokia/openbsc_nokia_3trx.cfg (100%) rename {openbsc/doc => doc}/examples/osmo-nitb/rbs2308/openbsc.cfg (100%) rename {openbsc/doc => doc}/examples/osmo-nitb/sysmobts/openbsc.cfg (100%) rename {openbsc/doc => doc}/examples/osmo-sgsn/osmo-sgsn.cfg (100%) rename {openbsc/doc => doc}/gsm-hopping.txt (100%) rename {openbsc/doc => doc}/handover.txt (100%) rename {openbsc/doc => doc}/ipa-sccp.txt (100%) rename {openbsc/doc => doc}/oml-interface.txt (100%) rename {openbsc/doc => doc}/osmo-nitb-data_structures.dot (100%) rename {openbsc/doc => doc}/paging.txt (100%) rename openbsc/git-version-gen => git-version-gen (100%) rename {openbsc/m4 => m4}/README (100%) rename {openbsc/m4 => m4}/ax_check_compile_flag.m4 (100%) rename openbsc/openbsc.pc.in => openbsc.pc.in (100%) delete mode 100644 openbsc/include/Makefile.am delete mode 100644 openbsc/include/compat_af_isdn.h delete mode 100644 openbsc/include/mISDNif.h delete mode 100644 openbsc/include/openbsc/Makefile.am delete mode 100644 openbsc/include/openbsc/abis_nm.h delete mode 100644 openbsc/include/openbsc/abis_om2000.h delete mode 100644 openbsc/include/openbsc/abis_rsl.h delete mode 100644 openbsc/include/openbsc/arfcn_range_encode.h delete mode 100644 openbsc/include/openbsc/auth.h delete mode 100644 openbsc/include/openbsc/bsc_api.h delete mode 100644 openbsc/include/openbsc/bsc_msc.h delete mode 100644 openbsc/include/openbsc/bsc_msc_data.h delete mode 100644 openbsc/include/openbsc/bsc_msg_filter.h delete mode 100644 openbsc/include/openbsc/bsc_nat.h delete mode 100644 openbsc/include/openbsc/bsc_nat_callstats.h delete mode 100644 openbsc/include/openbsc/bsc_nat_sccp.h delete mode 100644 openbsc/include/openbsc/bsc_rll.h delete mode 100644 openbsc/include/openbsc/bsc_subscriber.h delete mode 100644 openbsc/include/openbsc/bss.h delete mode 100644 openbsc/include/openbsc/bts_ipaccess_nanobts_omlattr.h delete mode 100644 openbsc/include/openbsc/chan_alloc.h delete mode 100644 openbsc/include/openbsc/common_bsc.h delete mode 100644 openbsc/include/openbsc/common_cs.h delete mode 100644 openbsc/include/openbsc/crc24.h delete mode 100644 openbsc/include/openbsc/ctrl.h delete mode 100644 openbsc/include/openbsc/db.h delete mode 100644 openbsc/include/openbsc/debug.h delete mode 100644 openbsc/include/openbsc/e1_config.h delete mode 100644 openbsc/include/openbsc/gb_proxy.h delete mode 100644 openbsc/include/openbsc/gprs_gb_parse.h delete mode 100644 openbsc/include/openbsc/gprs_gmm.h delete mode 100644 openbsc/include/openbsc/gprs_llc.h delete mode 100644 openbsc/include/openbsc/gprs_llc_xid.h delete mode 100644 openbsc/include/openbsc/gprs_sgsn.h delete mode 100644 openbsc/include/openbsc/gprs_sndcp.h delete mode 100644 openbsc/include/openbsc/gprs_sndcp_comp.h delete mode 100644 openbsc/include/openbsc/gprs_sndcp_dcomp.h delete mode 100644 openbsc/include/openbsc/gprs_sndcp_pcomp.h delete mode 100644 openbsc/include/openbsc/gprs_sndcp_xid.h delete mode 100644 openbsc/include/openbsc/gprs_subscriber.h delete mode 100644 openbsc/include/openbsc/gprs_utils.h delete mode 100644 openbsc/include/openbsc/gsm_04_08.h delete mode 100644 openbsc/include/openbsc/gsm_04_11.h delete mode 100644 openbsc/include/openbsc/gsm_04_80.h delete mode 100644 openbsc/include/openbsc/gsm_data.h delete mode 100644 openbsc/include/openbsc/gsm_data_shared.h delete mode 100644 openbsc/include/openbsc/gsm_subscriber.h delete mode 100644 openbsc/include/openbsc/gsup_client.h delete mode 100644 openbsc/include/openbsc/gtphub.h delete mode 100644 openbsc/include/openbsc/handover.h delete mode 100644 openbsc/include/openbsc/handover_decision.h delete mode 100644 openbsc/include/openbsc/ipaccess.h delete mode 100644 openbsc/include/openbsc/iu.h delete mode 100644 openbsc/include/openbsc/meas_feed.h delete mode 100644 openbsc/include/openbsc/meas_rep.h delete mode 100644 openbsc/include/openbsc/mgcp.h delete mode 100644 openbsc/include/openbsc/mgcp_internal.h delete mode 100644 openbsc/include/openbsc/mgcp_transcode.h delete mode 100644 openbsc/include/openbsc/misdn.h delete mode 100644 openbsc/include/openbsc/mncc.h delete mode 100644 openbsc/include/openbsc/mncc_int.h delete mode 100644 openbsc/include/openbsc/nat_rewrite_trie.h delete mode 100644 openbsc/include/openbsc/network_listen.h delete mode 100644 openbsc/include/openbsc/oap_client.h delete mode 100644 openbsc/include/openbsc/openbscdefines.h delete mode 100644 openbsc/include/openbsc/osmo_bsc.h delete mode 100644 openbsc/include/openbsc/osmo_bsc_grace.h delete mode 100644 openbsc/include/openbsc/osmo_bsc_rf.h delete mode 100644 openbsc/include/openbsc/osmo_msc.h delete mode 100644 openbsc/include/openbsc/osmux.h delete mode 100644 openbsc/include/openbsc/paging.h delete mode 100644 openbsc/include/openbsc/pcu_if.h delete mode 100644 openbsc/include/openbsc/pcuif_proto.h delete mode 100644 openbsc/include/openbsc/rest_octets.h delete mode 100644 openbsc/include/openbsc/rrlp.h delete mode 100644 openbsc/include/openbsc/rs232.h delete mode 100644 openbsc/include/openbsc/rtp_proxy.h delete mode 100644 openbsc/include/openbsc/sgsn.h delete mode 100644 openbsc/include/openbsc/signal.h delete mode 100644 openbsc/include/openbsc/silent_call.h delete mode 100644 openbsc/include/openbsc/slhc.h delete mode 100644 openbsc/include/openbsc/smpp.h delete mode 100644 openbsc/include/openbsc/sms_queue.h delete mode 100644 openbsc/include/openbsc/socket.h delete mode 100644 openbsc/include/openbsc/system_information.h delete mode 100644 openbsc/include/openbsc/token_auth.h delete mode 100644 openbsc/include/openbsc/transaction.h delete mode 100644 openbsc/include/openbsc/trau_mux.h delete mode 100644 openbsc/include/openbsc/trau_upqueue.h delete mode 100644 openbsc/include/openbsc/ussd.h delete mode 100644 openbsc/include/openbsc/v42bis.h delete mode 100644 openbsc/include/openbsc/v42bis_private.h delete mode 100644 openbsc/include/openbsc/vty.h delete mode 100644 openbsc/src/Makefile.am delete mode 100644 openbsc/src/gprs/.gitignore delete mode 100644 openbsc/src/gprs/Makefile.am delete mode 100644 openbsc/src/gprs/crc24.c delete mode 100644 openbsc/src/gprs/gb_proxy.c delete mode 100644 openbsc/src/gprs/gb_proxy_main.c delete mode 100644 openbsc/src/gprs/gb_proxy_patch.c delete mode 100644 openbsc/src/gprs/gb_proxy_peer.c delete mode 100644 openbsc/src/gprs/gb_proxy_tlli.c delete mode 100644 openbsc/src/gprs/gb_proxy_vty.c delete mode 100644 openbsc/src/gprs/gprs_gb_parse.c delete mode 100644 openbsc/src/gprs/gprs_gmm.c delete mode 100644 openbsc/src/gprs/gprs_llc.c delete mode 100644 openbsc/src/gprs/gprs_llc_parse.c delete mode 100644 openbsc/src/gprs/gprs_llc_vty.c delete mode 100644 openbsc/src/gprs/gprs_llc_xid.c delete mode 100644 openbsc/src/gprs/gprs_sgsn.c delete mode 100644 openbsc/src/gprs/gprs_sndcp.c delete mode 100644 openbsc/src/gprs/gprs_sndcp_comp.c delete mode 100644 openbsc/src/gprs/gprs_sndcp_dcomp.c delete mode 100644 openbsc/src/gprs/gprs_sndcp_pcomp.c delete mode 100644 openbsc/src/gprs/gprs_sndcp_vty.c delete mode 100644 openbsc/src/gprs/gprs_sndcp_xid.c delete mode 100644 openbsc/src/gprs/gprs_subscriber.c delete mode 100644 openbsc/src/gprs/gprs_utils.c delete mode 100644 openbsc/src/gprs/gtphub.c delete mode 100644 openbsc/src/gprs/gtphub_ares.c delete mode 100644 openbsc/src/gprs/gtphub_main.c delete mode 100644 openbsc/src/gprs/gtphub_sock.c delete mode 100644 openbsc/src/gprs/gtphub_vty.c delete mode 100644 openbsc/src/gprs/osmo_sgsn.cfg delete mode 100644 openbsc/src/gprs/sgsn_ares.c delete mode 100644 openbsc/src/gprs/sgsn_auth.c delete mode 100644 openbsc/src/gprs/sgsn_cdr.c delete mode 100644 openbsc/src/gprs/sgsn_ctrl.c delete mode 100644 openbsc/src/gprs/sgsn_libgtp.c delete mode 100644 openbsc/src/gprs/sgsn_main.c delete mode 100644 openbsc/src/gprs/sgsn_vty.c delete mode 100644 openbsc/src/gprs/slhc.c delete mode 100644 openbsc/src/gprs/v42bis.c delete mode 100644 openbsc/src/ipaccess/Makefile.am delete mode 100644 openbsc/src/ipaccess/abisip-find.c delete mode 100644 openbsc/src/ipaccess/ipaccess-config.c delete mode 100644 openbsc/src/ipaccess/ipaccess-firmware.c delete mode 100644 openbsc/src/ipaccess/ipaccess-proxy.c delete mode 100644 openbsc/src/ipaccess/network_listen.c delete mode 100644 openbsc/src/libbsc/Makefile.am delete mode 100644 openbsc/src/libbsc/abis_nm.c delete mode 100644 openbsc/src/libbsc/abis_nm_ipaccess.c delete mode 100644 openbsc/src/libbsc/abis_nm_vty.c delete mode 100644 openbsc/src/libbsc/abis_om2000.c delete mode 100644 openbsc/src/libbsc/abis_om2000_vty.c delete mode 100644 openbsc/src/libbsc/abis_rsl.c delete mode 100644 openbsc/src/libbsc/arfcn_range_encode.c delete mode 100644 openbsc/src/libbsc/bsc_api.c delete mode 100644 openbsc/src/libbsc/bsc_ctrl_commands.c delete mode 100644 openbsc/src/libbsc/bsc_ctrl_lookup.c delete mode 100644 openbsc/src/libbsc/bsc_dyn_ts.c delete mode 100644 openbsc/src/libbsc/bsc_init.c delete mode 100644 openbsc/src/libbsc/bsc_msc.c delete mode 100644 openbsc/src/libbsc/bsc_rf_ctrl.c delete mode 100644 openbsc/src/libbsc/bsc_rll.c delete mode 100644 openbsc/src/libbsc/bsc_subscriber.c delete mode 100644 openbsc/src/libbsc/bsc_vty.c delete mode 100644 openbsc/src/libbsc/bts_ericsson_rbs2000.c delete mode 100644 openbsc/src/libbsc/bts_init.c delete mode 100644 openbsc/src/libbsc/bts_ipaccess_nanobts.c delete mode 100644 openbsc/src/libbsc/bts_ipaccess_nanobts_omlattr.c delete mode 100644 openbsc/src/libbsc/bts_nokia_site.c delete mode 100644 openbsc/src/libbsc/bts_siemens_bs11.c delete mode 100644 openbsc/src/libbsc/bts_sysmobts.c delete mode 100644 openbsc/src/libbsc/bts_unknown.c delete mode 100644 openbsc/src/libbsc/chan_alloc.c delete mode 100644 openbsc/src/libbsc/e1_config.c delete mode 100644 openbsc/src/libbsc/gsm_04_08_utils.c delete mode 100644 openbsc/src/libbsc/gsm_04_80_utils.c delete mode 100644 openbsc/src/libbsc/handover_decision.c delete mode 100644 openbsc/src/libbsc/handover_logic.c delete mode 100644 openbsc/src/libbsc/meas_proc.c delete mode 100644 openbsc/src/libbsc/meas_rep.c delete mode 100644 openbsc/src/libbsc/net_init.c delete mode 100644 openbsc/src/libbsc/paging.c delete mode 100644 openbsc/src/libbsc/pcu_sock.c delete mode 100644 openbsc/src/libbsc/rest_octets.c delete mode 100644 openbsc/src/libbsc/system_information.c delete mode 100644 openbsc/src/libcommon-cs/Makefile.am delete mode 100644 openbsc/src/libcommon-cs/common_cs.c delete mode 100644 openbsc/src/libcommon-cs/common_cs_vty.c delete mode 100644 openbsc/src/libcommon/Makefile.am delete mode 100644 openbsc/src/libcommon/bsc_version.c delete mode 100644 openbsc/src/libcommon/common_vty.c delete mode 100644 openbsc/src/libcommon/debug.c delete mode 100644 openbsc/src/libcommon/gsm_data.c delete mode 100644 openbsc/src/libcommon/gsm_data_shared.c delete mode 100644 openbsc/src/libcommon/gsm_subscriber_base.c delete mode 100644 openbsc/src/libcommon/gsup_client.c delete mode 100644 openbsc/src/libcommon/gsup_test_client.c delete mode 100644 openbsc/src/libcommon/oap_client.c delete mode 100644 openbsc/src/libcommon/socket.c delete mode 100644 openbsc/src/libcommon/talloc_ctx.c delete mode 100644 openbsc/src/libfilter/Makefile.am delete mode 100644 openbsc/src/libfilter/bsc_msg_acc.c delete mode 100644 openbsc/src/libfilter/bsc_msg_filter.c delete mode 100644 openbsc/src/libfilter/bsc_msg_vty.c delete mode 100644 openbsc/src/libiu/Makefile.am delete mode 100644 openbsc/src/libiu/iu.c delete mode 100644 openbsc/src/libiu/iu_vty.c delete mode 100644 openbsc/src/libmgcp/Makefile.am delete mode 100644 openbsc/src/libmgcp/g711common.h delete mode 100644 openbsc/src/libmgcp/mgcp_network.c delete mode 100644 openbsc/src/libmgcp/mgcp_osmux.c delete mode 100644 openbsc/src/libmgcp/mgcp_protocol.c delete mode 100644 openbsc/src/libmgcp/mgcp_sdp.c delete mode 100644 openbsc/src/libmgcp/mgcp_transcode.c delete mode 100644 openbsc/src/libmgcp/mgcp_vty.c delete mode 100644 openbsc/src/libmsc/Makefile.am delete mode 100644 openbsc/src/libmsc/auth.c delete mode 100644 openbsc/src/libmsc/ctrl_commands.c delete mode 100644 openbsc/src/libmsc/db.c delete mode 100644 openbsc/src/libmsc/gsm_04_08.c delete mode 100644 openbsc/src/libmsc/gsm_04_11.c delete mode 100644 openbsc/src/libmsc/gsm_04_80.c delete mode 100644 openbsc/src/libmsc/gsm_subscriber.c delete mode 100644 openbsc/src/libmsc/meas_feed.c delete mode 100644 openbsc/src/libmsc/meas_feed.h delete mode 100644 openbsc/src/libmsc/mncc.c delete mode 100644 openbsc/src/libmsc/mncc_builtin.c delete mode 100644 openbsc/src/libmsc/mncc_sock.c delete mode 100644 openbsc/src/libmsc/osmo_msc.c delete mode 100644 openbsc/src/libmsc/rrlp.c delete mode 100644 openbsc/src/libmsc/silent_call.c delete mode 100644 openbsc/src/libmsc/smpp_openbsc.c delete mode 100644 openbsc/src/libmsc/smpp_smsc.c delete mode 100644 openbsc/src/libmsc/smpp_smsc.h delete mode 100644 openbsc/src/libmsc/smpp_utils.c delete mode 100644 openbsc/src/libmsc/smpp_vty.c delete mode 100644 openbsc/src/libmsc/sms_queue.c delete mode 100644 openbsc/src/libmsc/token_auth.c delete mode 100644 openbsc/src/libmsc/transaction.c delete mode 100644 openbsc/src/libmsc/ussd.c delete mode 100644 openbsc/src/libmsc/vty_interface_layer3.c delete mode 100644 openbsc/src/libtrau/Makefile.am delete mode 100644 openbsc/src/libtrau/rtp_proxy.c delete mode 100644 openbsc/src/libtrau/trau_mux.c delete mode 100644 openbsc/src/libtrau/trau_upqueue.c delete mode 100644 openbsc/src/osmo-bsc/Makefile.am delete mode 100644 openbsc/src/osmo-bsc/osmo_bsc_api.c delete mode 100644 openbsc/src/osmo-bsc/osmo_bsc_audio.c delete mode 100644 openbsc/src/osmo-bsc/osmo_bsc_bssap.c delete mode 100644 openbsc/src/osmo-bsc/osmo_bsc_ctrl.c delete mode 100644 openbsc/src/osmo-bsc/osmo_bsc_filter.c delete mode 100644 openbsc/src/osmo-bsc/osmo_bsc_grace.c delete mode 100644 openbsc/src/osmo-bsc/osmo_bsc_main.c delete mode 100644 openbsc/src/osmo-bsc/osmo_bsc_msc.c delete mode 100644 openbsc/src/osmo-bsc/osmo_bsc_sccp.c delete mode 100644 openbsc/src/osmo-bsc/osmo_bsc_vty.c delete mode 100644 openbsc/src/osmo-bsc_mgcp/Makefile.am delete mode 100644 openbsc/src/osmo-bsc_mgcp/mgcp_main.c delete mode 100644 openbsc/src/osmo-bsc_nat/Makefile.am delete mode 100644 openbsc/src/osmo-bsc_nat/bsc_filter.c delete mode 100644 openbsc/src/osmo-bsc_nat/bsc_mgcp_utils.c delete mode 100644 openbsc/src/osmo-bsc_nat/bsc_nat.c delete mode 100644 openbsc/src/osmo-bsc_nat/bsc_nat_ctrl.c delete mode 100644 openbsc/src/osmo-bsc_nat/bsc_nat_filter.c delete mode 100644 openbsc/src/osmo-bsc_nat/bsc_nat_rewrite.c delete mode 100644 openbsc/src/osmo-bsc_nat/bsc_nat_rewrite_trie.c delete mode 100644 openbsc/src/osmo-bsc_nat/bsc_nat_utils.c delete mode 100644 openbsc/src/osmo-bsc_nat/bsc_nat_vty.c delete mode 100644 openbsc/src/osmo-bsc_nat/bsc_sccp.c delete mode 100644 openbsc/src/osmo-bsc_nat/bsc_ussd.c delete mode 100644 openbsc/src/osmo-nitb/Makefile.am delete mode 100644 openbsc/src/osmo-nitb/bsc_hack.c delete mode 100644 openbsc/src/utils/Makefile.am delete mode 100644 openbsc/src/utils/bs11_config.c delete mode 100644 openbsc/src/utils/isdnsync.c delete mode 100644 openbsc/src/utils/meas_db.c delete mode 100644 openbsc/src/utils/meas_db.h delete mode 100644 openbsc/src/utils/meas_json.c delete mode 100644 openbsc/src/utils/meas_pcap2db.c delete mode 100644 openbsc/src/utils/meas_udp2db.c delete mode 100644 openbsc/src/utils/meas_vis.c delete mode 100644 openbsc/src/utils/smpp_mirror.c rename openbsc/osmoappdesc.py => osmoappdesc.py (100%) rename {openbsc/tests => tests}/Makefile.am (100%) rename {openbsc/tests => tests}/abis/Makefile.am (100%) rename {openbsc/tests => tests}/abis/abis_test.c (100%) rename {openbsc/tests => tests}/abis/abis_test.ok (100%) rename {openbsc/tests => tests}/atlocal.in (100%) rename {openbsc/tests => tests}/bsc-nat-trie/Makefile.am (100%) rename {openbsc/tests => tests}/bsc-nat-trie/bsc_nat_trie_test.c (100%) rename {openbsc/tests => tests}/bsc-nat-trie/bsc_nat_trie_test.ok (100%) rename {openbsc/tests => tests}/bsc-nat-trie/prefixes.csv (100%) rename {openbsc/tests => tests}/bsc-nat/Makefile.am (100%) rename {openbsc/tests => tests}/bsc-nat/barr.cfg (100%) rename {openbsc/tests => tests}/bsc-nat/barr_dup.cfg (100%) rename {openbsc/tests => tests}/bsc-nat/bsc_data.c (100%) rename {openbsc/tests => tests}/bsc-nat/bsc_nat_test.c (100%) rename {openbsc/tests => tests}/bsc-nat/bsc_nat_test.ok (100%) rename {openbsc/tests => tests}/bsc-nat/prefixes.csv (100%) rename {openbsc/tests => tests}/bsc/Makefile.am (100%) rename {openbsc/tests => tests}/bsc/bsc_test.c (100%) rename {openbsc/tests => tests}/bsc/bsc_test.ok (100%) rename {openbsc/tests => tests}/channel/Makefile.am (100%) rename {openbsc/tests => tests}/channel/channel_test.c (100%) rename {openbsc/tests => tests}/channel/channel_test.ok (100%) rename {openbsc/tests => tests}/ctrl_test_runner.py (100%) rename {openbsc/tests => tests}/db/Makefile.am (100%) rename {openbsc/tests => tests}/db/db_test.c (100%) rename {openbsc/tests => tests}/db/db_test.err (100%) rename {openbsc/tests => tests}/db/db_test.ok (100%) rename {openbsc/tests => tests}/db/hlr.sqlite3 (100%) rename {openbsc/tests => tests}/gbproxy/Makefile.am (100%) rename {openbsc/tests => tests}/gbproxy/gbproxy_test.c (100%) rename {openbsc/tests => tests}/gbproxy/gbproxy_test.ok (100%) rename {openbsc/tests => tests}/gprs/Makefile.am (100%) rename {openbsc/tests => tests}/gprs/gprs_test.c (100%) rename {openbsc/tests => tests}/gprs/gprs_test.ok (100%) rename {openbsc/tests => tests}/gsm0408/Makefile.am (100%) rename {openbsc/tests => tests}/gsm0408/gsm0408_test.c (100%) rename {openbsc/tests => tests}/gsm0408/gsm0408_test.ok (100%) rename {openbsc/tests => tests}/gtphub/Makefile.am (100%) rename {openbsc/tests => tests}/gtphub/gtphub_test.c (100%) rename {openbsc/tests => tests}/gtphub/gtphub_test.ok (100%) rename {openbsc/tests => tests}/mgcp/Makefile.am (100%) rename {openbsc/tests => tests}/mgcp/mgcp_test.c (100%) rename {openbsc/tests => tests}/mgcp/mgcp_test.ok (100%) rename {openbsc/tests => tests}/mgcp/mgcp_transcoding_test.c (100%) rename {openbsc/tests => tests}/mgcp/mgcp_transcoding_test.ok (100%) rename {openbsc/tests => tests}/mm_auth/Makefile.am (100%) rename {openbsc/tests => tests}/mm_auth/mm_auth_test.c (100%) rename {openbsc/tests => tests}/mm_auth/mm_auth_test.ok (100%) rename {openbsc/tests => tests}/nanobts_omlattr/Makefile.am (100%) rename {openbsc/tests => tests}/nanobts_omlattr/nanobts_omlattr_test.c (100%) rename {openbsc/tests => tests}/nanobts_omlattr/nanobts_omlattr_test.ok (100%) rename {openbsc/tests => tests}/oap/Makefile.am (100%) rename {openbsc/tests => tests}/oap/oap_client_test.c (100%) rename {openbsc/tests => tests}/oap/oap_client_test.err (100%) rename {openbsc/tests => tests}/oap/oap_client_test.ok (100%) rename {openbsc/tests => tests}/sgsn/Makefile.am (100%) rename {openbsc/tests => tests}/sgsn/sgsn_test.c (100%) rename {openbsc/tests => tests}/sgsn/sgsn_test.ok (100%) rename {openbsc/tests => tests}/slhc/Makefile.am (100%) rename {openbsc/tests => tests}/slhc/slhc_test.c (100%) rename {openbsc/tests => tests}/slhc/slhc_test.ok (100%) rename {openbsc/tests => tests}/smpp/Makefile.am (100%) rename {openbsc/tests => tests}/smpp/smpp_test.c (100%) rename {openbsc/tests => tests}/smpp/smpp_test.err (100%) rename {openbsc/tests => tests}/smpp/smpp_test.ok (100%) rename {openbsc/tests => tests}/smpp_test_runner.py (100%) rename {openbsc/tests => tests}/sndcp_xid/Makefile.am (100%) rename {openbsc/tests => tests}/sndcp_xid/sndcp_xid_test.c (100%) rename {openbsc/tests => tests}/sndcp_xid/sndcp_xid_test.ok (100%) rename {openbsc/tests => tests}/subscr/Makefile.am (100%) rename {openbsc/tests => tests}/subscr/bsc_subscr_test.c (100%) rename {openbsc/tests => tests}/subscr/bsc_subscr_test.err (100%) rename {openbsc/tests => tests}/subscr/bsc_subscr_test.ok (100%) rename {openbsc/tests => tests}/subscr/subscr_test.c (100%) rename {openbsc/tests => tests}/subscr/subscr_test.ok (100%) rename {openbsc/tests => tests}/testsuite.at (100%) rename {openbsc/tests => tests}/trau/Makefile.am (100%) rename {openbsc/tests => tests}/trau/trau_test.c (100%) rename {openbsc/tests => tests}/trau/trau_test.ok (100%) rename {openbsc/tests => tests}/v42bis/Makefile.am (100%) rename {openbsc/tests => tests}/v42bis/v42bis_test.c (100%) rename {openbsc/tests => tests}/v42bis/v42bis_test.ok (100%) rename {openbsc/tests => tests}/vty_test_runner.py (100%) rename {openbsc/tests => tests}/xid/Makefile.am (100%) rename {openbsc/tests => tests}/xid/xid_test.c (100%) rename {openbsc/tests => tests}/xid/xid_test.ok (100%) rename {openbsc/tools => tools}/hlrstat.pl (100%) diff --git a/openbsc/AUTHORS b/AUTHORS similarity index 100% rename from openbsc/AUTHORS rename to AUTHORS diff --git a/openbsc/COPYING b/COPYING similarity index 100% rename from openbsc/COPYING rename to COPYING diff --git a/openbsc/Makefile.am b/Makefile.am similarity index 100% rename from openbsc/Makefile.am rename to Makefile.am diff --git a/openbsc/README b/README similarity index 100% rename from openbsc/README rename to README diff --git a/openbsc/README.vty-tests b/README.vty-tests similarity index 100% rename from openbsc/README.vty-tests rename to README.vty-tests diff --git a/openbsc/configure.ac b/configure.ac similarity index 100% rename from openbsc/configure.ac rename to configure.ac diff --git a/openbsc/contrib/a-link/sccp-split-by-con.lua b/contrib/a-link/sccp-split-by-con.lua similarity index 100% rename from openbsc/contrib/a-link/sccp-split-by-con.lua rename to contrib/a-link/sccp-split-by-con.lua diff --git a/openbsc/contrib/bsc-test/README b/contrib/bsc-test/README similarity index 100% rename from openbsc/contrib/bsc-test/README rename to contrib/bsc-test/README diff --git a/openbsc/contrib/bsc-test/all_dial b/contrib/bsc-test/all_dial similarity index 100% rename from openbsc/contrib/bsc-test/all_dial rename to contrib/bsc-test/all_dial diff --git a/openbsc/contrib/bsc-test/dial.sh b/contrib/bsc-test/dial.sh similarity index 100% rename from openbsc/contrib/bsc-test/dial.sh rename to contrib/bsc-test/dial.sh diff --git a/openbsc/contrib/bsc-test/drop-oml.sh b/contrib/bsc-test/drop-oml.sh similarity index 100% rename from openbsc/contrib/bsc-test/drop-oml.sh rename to contrib/bsc-test/drop-oml.sh diff --git a/openbsc/contrib/bsc-test/drop.sh b/contrib/bsc-test/drop.sh similarity index 100% rename from openbsc/contrib/bsc-test/drop.sh rename to contrib/bsc-test/drop.sh diff --git a/openbsc/contrib/bsc-test/hangup b/contrib/bsc-test/hangup similarity index 100% rename from openbsc/contrib/bsc-test/hangup rename to contrib/bsc-test/hangup diff --git a/openbsc/contrib/bsc-test/msc.sh b/contrib/bsc-test/msc.sh similarity index 100% rename from openbsc/contrib/bsc-test/msc.sh rename to contrib/bsc-test/msc.sh diff --git a/openbsc/contrib/bsc_control.py b/contrib/bsc_control.py similarity index 100% rename from openbsc/contrib/bsc_control.py rename to contrib/bsc_control.py diff --git a/openbsc/contrib/bt.py b/contrib/bt.py similarity index 100% rename from openbsc/contrib/bt.py rename to contrib/bt.py diff --git a/openbsc/contrib/convert_to_enum.py b/contrib/convert_to_enum.py similarity index 100% rename from openbsc/contrib/convert_to_enum.py rename to contrib/convert_to_enum.py diff --git a/openbsc/contrib/ctrl2sse.py b/contrib/ctrl2sse.py similarity index 100% rename from openbsc/contrib/ctrl2sse.py rename to contrib/ctrl2sse.py diff --git a/openbsc/contrib/gprs/gb-proxy-unblock-bug.py b/contrib/gprs/gb-proxy-unblock-bug.py similarity index 100% rename from openbsc/contrib/gprs/gb-proxy-unblock-bug.py rename to contrib/gprs/gb-proxy-unblock-bug.py diff --git a/openbsc/contrib/gprs/gprs-bssgp-histogram.lua b/contrib/gprs/gprs-bssgp-histogram.lua similarity index 100% rename from openbsc/contrib/gprs/gprs-bssgp-histogram.lua rename to contrib/gprs/gprs-bssgp-histogram.lua diff --git a/openbsc/contrib/gprs/gprs-buffer-count.lua b/contrib/gprs/gprs-buffer-count.lua similarity index 100% rename from openbsc/contrib/gprs/gprs-buffer-count.lua rename to contrib/gprs/gprs-buffer-count.lua diff --git a/openbsc/contrib/gprs/gprs-split-trace-by-tlli.lua b/contrib/gprs/gprs-split-trace-by-tlli.lua similarity index 100% rename from openbsc/contrib/gprs/gprs-split-trace-by-tlli.lua rename to contrib/gprs/gprs-split-trace-by-tlli.lua diff --git a/openbsc/contrib/gprs/gprs-verify-nu.lua b/contrib/gprs/gprs-verify-nu.lua similarity index 100% rename from openbsc/contrib/gprs/gprs-verify-nu.lua rename to contrib/gprs/gprs-verify-nu.lua diff --git a/openbsc/contrib/hlr-remove-old.sql b/contrib/hlr-remove-old.sql similarity index 100% rename from openbsc/contrib/hlr-remove-old.sql rename to contrib/hlr-remove-old.sql diff --git a/openbsc/contrib/hlrsync/hlrsync.py b/contrib/hlrsync/hlrsync.py similarity index 100% rename from openbsc/contrib/hlrsync/hlrsync.py rename to contrib/hlrsync/hlrsync.py diff --git a/openbsc/contrib/ipa.py b/contrib/ipa.py similarity index 100% rename from openbsc/contrib/ipa.py rename to contrib/ipa.py diff --git a/contrib/jenkins.sh b/contrib/jenkins.sh index 068ee34e..b315b977 100755 --- a/contrib/jenkins.sh +++ b/contrib/jenkins.sh @@ -43,7 +43,6 @@ echo set -x cd "$base" -cd openbsc autoreconf --install --force ./configure --enable-osmo-bsc --enable-nat $SMPP $MGCP $IU --enable-vty-tests --enable-external-tests $MAKE $PARALLEL_MAKE diff --git a/openbsc/contrib/mgcp_server.py b/contrib/mgcp_server.py similarity index 100% rename from openbsc/contrib/mgcp_server.py rename to contrib/mgcp_server.py diff --git a/openbsc/contrib/nat/test_regexp.c b/contrib/nat/test_regexp.c similarity index 100% rename from openbsc/contrib/nat/test_regexp.c rename to contrib/nat/test_regexp.c diff --git a/openbsc/contrib/nat/ussd_example.py b/contrib/nat/ussd_example.py similarity index 100% rename from openbsc/contrib/nat/ussd_example.py rename to contrib/nat/ussd_example.py diff --git a/openbsc/contrib/rtp/gen_rtp_header.erl b/contrib/rtp/gen_rtp_header.erl similarity index 100% rename from openbsc/contrib/rtp/gen_rtp_header.erl rename to contrib/rtp/gen_rtp_header.erl diff --git a/openbsc/contrib/rtp/rtp_replay.st b/contrib/rtp/rtp_replay.st similarity index 100% rename from openbsc/contrib/rtp/rtp_replay.st rename to contrib/rtp/rtp_replay.st diff --git a/openbsc/contrib/rtp/rtp_replay_shared.st b/contrib/rtp/rtp_replay_shared.st similarity index 100% rename from openbsc/contrib/rtp/rtp_replay_shared.st rename to contrib/rtp/rtp_replay_shared.st diff --git a/openbsc/contrib/rtp/rtp_replay_sip.st b/contrib/rtp/rtp_replay_sip.st similarity index 100% rename from openbsc/contrib/rtp/rtp_replay_sip.st rename to contrib/rtp/rtp_replay_sip.st diff --git a/openbsc/contrib/rtp/timestamp_rtp.lua b/contrib/rtp/timestamp_rtp.lua similarity index 100% rename from openbsc/contrib/rtp/timestamp_rtp.lua rename to contrib/rtp/timestamp_rtp.lua diff --git a/openbsc/contrib/sms/fill-hlr.st b/contrib/sms/fill-hlr.st similarity index 100% rename from openbsc/contrib/sms/fill-hlr.st rename to contrib/sms/fill-hlr.st diff --git a/openbsc/contrib/sms/hlr-query.st b/contrib/sms/hlr-query.st similarity index 100% rename from openbsc/contrib/sms/hlr-query.st rename to contrib/sms/hlr-query.st diff --git a/openbsc/contrib/sms/sqlite-probe.tap.d b/contrib/sms/sqlite-probe.tap.d similarity index 100% rename from openbsc/contrib/sms/sqlite-probe.tap.d rename to contrib/sms/sqlite-probe.tap.d diff --git a/openbsc/contrib/soap.py b/contrib/soap.py similarity index 100% rename from openbsc/contrib/soap.py rename to contrib/soap.py diff --git a/openbsc/contrib/systemd/osmo-bsc-mgcp.service b/contrib/systemd/osmo-bsc-mgcp.service similarity index 100% rename from openbsc/contrib/systemd/osmo-bsc-mgcp.service rename to contrib/systemd/osmo-bsc-mgcp.service diff --git a/openbsc/contrib/systemd/osmo-bsc.service b/contrib/systemd/osmo-bsc.service similarity index 100% rename from openbsc/contrib/systemd/osmo-bsc.service rename to contrib/systemd/osmo-bsc.service diff --git a/openbsc/contrib/systemd/osmo-gbproxy.service b/contrib/systemd/osmo-gbproxy.service similarity index 100% rename from openbsc/contrib/systemd/osmo-gbproxy.service rename to contrib/systemd/osmo-gbproxy.service diff --git a/openbsc/contrib/systemd/osmo-nitb.service b/contrib/systemd/osmo-nitb.service similarity index 100% rename from openbsc/contrib/systemd/osmo-nitb.service rename to contrib/systemd/osmo-nitb.service diff --git a/openbsc/contrib/systemd/osmo-sgsn.service b/contrib/systemd/osmo-sgsn.service similarity index 100% rename from openbsc/contrib/systemd/osmo-sgsn.service rename to contrib/systemd/osmo-sgsn.service diff --git a/openbsc/contrib/testconv/Makefile b/contrib/testconv/Makefile similarity index 100% rename from openbsc/contrib/testconv/Makefile rename to contrib/testconv/Makefile diff --git a/openbsc/contrib/testconv/testconv_main.c b/contrib/testconv/testconv_main.c similarity index 100% rename from openbsc/contrib/testconv/testconv_main.c rename to contrib/testconv/testconv_main.c diff --git a/openbsc/contrib/twisted_ipa.py b/contrib/twisted_ipa.py similarity index 100% rename from openbsc/contrib/twisted_ipa.py rename to contrib/twisted_ipa.py diff --git a/openbsc/doc/BS11-OML.txt b/doc/BS11-OML.txt similarity index 100% rename from openbsc/doc/BS11-OML.txt rename to doc/BS11-OML.txt diff --git a/openbsc/doc/Makefile.am b/doc/Makefile.am similarity index 100% rename from openbsc/doc/Makefile.am rename to doc/Makefile.am diff --git a/openbsc/doc/call-routing.txt b/doc/call-routing.txt similarity index 100% rename from openbsc/doc/call-routing.txt rename to doc/call-routing.txt diff --git a/openbsc/doc/channel_release.txt b/doc/channel_release.txt similarity index 100% rename from openbsc/doc/channel_release.txt rename to doc/channel_release.txt diff --git a/openbsc/doc/e1-data-model.txt b/doc/e1-data-model.txt similarity index 100% rename from openbsc/doc/e1-data-model.txt rename to doc/e1-data-model.txt diff --git a/openbsc/doc/examples/Makefile.am b/doc/examples/Makefile.am similarity index 100% rename from openbsc/doc/examples/Makefile.am rename to doc/examples/Makefile.am diff --git a/openbsc/doc/examples/osmo-bsc/osmo-bsc.cfg b/doc/examples/osmo-bsc/osmo-bsc.cfg similarity index 100% rename from openbsc/doc/examples/osmo-bsc/osmo-bsc.cfg rename to doc/examples/osmo-bsc/osmo-bsc.cfg diff --git a/openbsc/doc/examples/osmo-bsc_mgcp/mgcp.cfg b/doc/examples/osmo-bsc_mgcp/mgcp.cfg similarity index 100% rename from openbsc/doc/examples/osmo-bsc_mgcp/mgcp.cfg rename to doc/examples/osmo-bsc_mgcp/mgcp.cfg diff --git a/openbsc/doc/examples/osmo-bsc_nat/black-list.cfg b/doc/examples/osmo-bsc_nat/black-list.cfg similarity index 100% rename from openbsc/doc/examples/osmo-bsc_nat/black-list.cfg rename to doc/examples/osmo-bsc_nat/black-list.cfg diff --git a/openbsc/doc/examples/osmo-bsc_nat/bscs.config b/doc/examples/osmo-bsc_nat/bscs.config similarity index 100% rename from openbsc/doc/examples/osmo-bsc_nat/bscs.config rename to doc/examples/osmo-bsc_nat/bscs.config diff --git a/openbsc/doc/examples/osmo-bsc_nat/osmo-bsc_nat.cfg b/doc/examples/osmo-bsc_nat/osmo-bsc_nat.cfg similarity index 100% rename from openbsc/doc/examples/osmo-bsc_nat/osmo-bsc_nat.cfg rename to doc/examples/osmo-bsc_nat/osmo-bsc_nat.cfg diff --git a/openbsc/doc/examples/osmo-gbproxy/osmo-gbproxy-legacy.cfg b/doc/examples/osmo-gbproxy/osmo-gbproxy-legacy.cfg similarity index 100% rename from openbsc/doc/examples/osmo-gbproxy/osmo-gbproxy-legacy.cfg rename to doc/examples/osmo-gbproxy/osmo-gbproxy-legacy.cfg diff --git a/openbsc/doc/examples/osmo-gbproxy/osmo-gbproxy.cfg b/doc/examples/osmo-gbproxy/osmo-gbproxy.cfg similarity index 100% rename from openbsc/doc/examples/osmo-gbproxy/osmo-gbproxy.cfg rename to doc/examples/osmo-gbproxy/osmo-gbproxy.cfg diff --git a/openbsc/doc/examples/osmo-gtphub/gtphub-example.txt b/doc/examples/osmo-gtphub/gtphub-example.txt similarity index 100% rename from openbsc/doc/examples/osmo-gtphub/gtphub-example.txt rename to doc/examples/osmo-gtphub/gtphub-example.txt diff --git a/openbsc/doc/examples/osmo-gtphub/osmo-gtphub-1iface.cfg b/doc/examples/osmo-gtphub/osmo-gtphub-1iface.cfg similarity index 100% rename from openbsc/doc/examples/osmo-gtphub/osmo-gtphub-1iface.cfg rename to doc/examples/osmo-gtphub/osmo-gtphub-1iface.cfg diff --git a/openbsc/doc/examples/osmo-gtphub/osmo-gtphub.cfg b/doc/examples/osmo-gtphub/osmo-gtphub.cfg similarity index 100% rename from openbsc/doc/examples/osmo-gtphub/osmo-gtphub.cfg rename to doc/examples/osmo-gtphub/osmo-gtphub.cfg diff --git a/openbsc/doc/examples/osmo-nitb/bs11/openbsc-1bts-2trx-hopping.cfg b/doc/examples/osmo-nitb/bs11/openbsc-1bts-2trx-hopping.cfg similarity index 100% rename from openbsc/doc/examples/osmo-nitb/bs11/openbsc-1bts-2trx-hopping.cfg rename to doc/examples/osmo-nitb/bs11/openbsc-1bts-2trx-hopping.cfg diff --git a/openbsc/doc/examples/osmo-nitb/bs11/openbsc-1bts-2trx.cfg b/doc/examples/osmo-nitb/bs11/openbsc-1bts-2trx.cfg similarity index 100% rename from openbsc/doc/examples/osmo-nitb/bs11/openbsc-1bts-2trx.cfg rename to doc/examples/osmo-nitb/bs11/openbsc-1bts-2trx.cfg diff --git a/openbsc/doc/examples/osmo-nitb/bs11/openbsc-2bts-2trx.cfg b/doc/examples/osmo-nitb/bs11/openbsc-2bts-2trx.cfg similarity index 100% rename from openbsc/doc/examples/osmo-nitb/bs11/openbsc-2bts-2trx.cfg rename to doc/examples/osmo-nitb/bs11/openbsc-2bts-2trx.cfg diff --git a/openbsc/doc/examples/osmo-nitb/bs11/openbsc.cfg b/doc/examples/osmo-nitb/bs11/openbsc.cfg similarity index 100% rename from openbsc/doc/examples/osmo-nitb/bs11/openbsc.cfg rename to doc/examples/osmo-nitb/bs11/openbsc.cfg diff --git a/openbsc/doc/examples/osmo-nitb/nanobts/openbsc-multitrx.cfg b/doc/examples/osmo-nitb/nanobts/openbsc-multitrx.cfg similarity index 100% rename from openbsc/doc/examples/osmo-nitb/nanobts/openbsc-multitrx.cfg rename to doc/examples/osmo-nitb/nanobts/openbsc-multitrx.cfg diff --git a/openbsc/doc/examples/osmo-nitb/nanobts/openbsc.cfg b/doc/examples/osmo-nitb/nanobts/openbsc.cfg similarity index 100% rename from openbsc/doc/examples/osmo-nitb/nanobts/openbsc.cfg rename to doc/examples/osmo-nitb/nanobts/openbsc.cfg diff --git a/openbsc/doc/examples/osmo-nitb/nokia/openbsc_nokia_3trx.cfg b/doc/examples/osmo-nitb/nokia/openbsc_nokia_3trx.cfg similarity index 100% rename from openbsc/doc/examples/osmo-nitb/nokia/openbsc_nokia_3trx.cfg rename to doc/examples/osmo-nitb/nokia/openbsc_nokia_3trx.cfg diff --git a/openbsc/doc/examples/osmo-nitb/rbs2308/openbsc.cfg b/doc/examples/osmo-nitb/rbs2308/openbsc.cfg similarity index 100% rename from openbsc/doc/examples/osmo-nitb/rbs2308/openbsc.cfg rename to doc/examples/osmo-nitb/rbs2308/openbsc.cfg diff --git a/openbsc/doc/examples/osmo-nitb/sysmobts/openbsc.cfg b/doc/examples/osmo-nitb/sysmobts/openbsc.cfg similarity index 100% rename from openbsc/doc/examples/osmo-nitb/sysmobts/openbsc.cfg rename to doc/examples/osmo-nitb/sysmobts/openbsc.cfg diff --git a/openbsc/doc/examples/osmo-sgsn/osmo-sgsn.cfg b/doc/examples/osmo-sgsn/osmo-sgsn.cfg similarity index 100% rename from openbsc/doc/examples/osmo-sgsn/osmo-sgsn.cfg rename to doc/examples/osmo-sgsn/osmo-sgsn.cfg diff --git a/openbsc/doc/gsm-hopping.txt b/doc/gsm-hopping.txt similarity index 100% rename from openbsc/doc/gsm-hopping.txt rename to doc/gsm-hopping.txt diff --git a/openbsc/doc/handover.txt b/doc/handover.txt similarity index 100% rename from openbsc/doc/handover.txt rename to doc/handover.txt diff --git a/openbsc/doc/ipa-sccp.txt b/doc/ipa-sccp.txt similarity index 100% rename from openbsc/doc/ipa-sccp.txt rename to doc/ipa-sccp.txt diff --git a/openbsc/doc/oml-interface.txt b/doc/oml-interface.txt similarity index 100% rename from openbsc/doc/oml-interface.txt rename to doc/oml-interface.txt diff --git a/openbsc/doc/osmo-nitb-data_structures.dot b/doc/osmo-nitb-data_structures.dot similarity index 100% rename from openbsc/doc/osmo-nitb-data_structures.dot rename to doc/osmo-nitb-data_structures.dot diff --git a/openbsc/doc/paging.txt b/doc/paging.txt similarity index 100% rename from openbsc/doc/paging.txt rename to doc/paging.txt diff --git a/openbsc/git-version-gen b/git-version-gen similarity index 100% rename from openbsc/git-version-gen rename to git-version-gen diff --git a/openbsc/m4/README b/m4/README similarity index 100% rename from openbsc/m4/README rename to m4/README diff --git a/openbsc/m4/ax_check_compile_flag.m4 b/m4/ax_check_compile_flag.m4 similarity index 100% rename from openbsc/m4/ax_check_compile_flag.m4 rename to m4/ax_check_compile_flag.m4 diff --git a/openbsc/openbsc.pc.in b/openbsc.pc.in similarity index 100% rename from openbsc/openbsc.pc.in rename to openbsc.pc.in diff --git a/openbsc/include/Makefile.am b/openbsc/include/Makefile.am deleted file mode 100644 index 3234e626..00000000 --- a/openbsc/include/Makefile.am +++ /dev/null @@ -1,8 +0,0 @@ -SUBDIRS = \ - openbsc \ - $(NULL) - -noinst_HEADERS = \ - mISDNif.h \ - compat_af_isdn.h \ - $(NULL) diff --git a/openbsc/include/compat_af_isdn.h b/openbsc/include/compat_af_isdn.h deleted file mode 100644 index 56cbfb3f..00000000 --- a/openbsc/include/compat_af_isdn.h +++ /dev/null @@ -1,39 +0,0 @@ -#ifdef MISDN_OLD_AF_COMPATIBILITY -#undef AF_ISDN -#undef PF_ISDN - -extern int AF_ISDN; -#define PF_ISDN AF_ISDN - -int AF_ISDN; - -#endif - -extern void init_af_isdn(void); - -#ifdef AF_COMPATIBILITY_FUNC -#ifdef MISDN_OLD_AF_COMPATIBILITY -void init_af_isdn(void) -{ - int s; - - /* test for new value */ - AF_ISDN = 34; - s = socket(AF_ISDN, SOCK_RAW, ISDN_P_BASE); - if (s >= 0) { - close(s); - return; - } - AF_ISDN = 27; - s = socket(AF_ISDN, SOCK_RAW, ISDN_P_BASE); - if (s >= 0) { - close(s); - return; - } -} -#else -void init_af_isdn(void) -{ -} -#endif -#endif diff --git a/openbsc/include/mISDNif.h b/openbsc/include/mISDNif.h deleted file mode 100644 index 8e065d24..00000000 --- a/openbsc/include/mISDNif.h +++ /dev/null @@ -1,387 +0,0 @@ -/* - * - * Author Karsten Keil - * - * Copyright 2008 by Karsten Keil - * - * This code is free software; you can redistribute it and/or modify - * it under the terms of the GNU LESSER GENERAL PUBLIC LICENSE - * version 2.1 as published by the Free Software Foundation. - * - * This code is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU LESSER GENERAL PUBLIC LICENSE for more details. - * - */ - -#ifndef mISDNIF_H -#define mISDNIF_H - -#include -#ifdef linux -#include -#include -#include -#else -#include -#include -#include -#endif - -/* - * ABI Version 32 bit - * - * <8 bit> Major version - * - changed if any interface become backwards incompatible - * - * <8 bit> Minor version - * - changed if any interface is extended but backwards compatible - * - * <16 bit> Release number - * - should be incremented on every checkin - */ -#define MISDN_MAJOR_VERSION 1 -#define MISDN_MINOR_VERSION 1 -#define MISDN_RELEASE 20 - -/* primitives for information exchange - * generell format - * <16 bit 0 > - * <8 bit command> - * BIT 8 = 1 LAYER private - * BIT 7 = 1 answer - * BIT 6 = 1 DATA - * <8 bit target layer mask> - * - * Layer = 00 is reserved for general commands - Layer = 01 L2 -> HW - Layer = 02 HW -> L2 - Layer = 04 L3 -> L2 - Layer = 08 L2 -> L3 - * Layer = FF is reserved for broadcast commands - */ - -#define MISDN_CMDMASK 0xff00 -#define MISDN_LAYERMASK 0x00ff - -/* generell commands */ -#define OPEN_CHANNEL 0x0100 -#define CLOSE_CHANNEL 0x0200 -#define CONTROL_CHANNEL 0x0300 -#define CHECK_DATA 0x0400 - -/* layer 2 -> layer 1 */ -#define PH_ACTIVATE_REQ 0x0101 -#define PH_DEACTIVATE_REQ 0x0201 -#define PH_DATA_REQ 0x2001 -#define MPH_ACTIVATE_REQ 0x0501 -#define MPH_DEACTIVATE_REQ 0x0601 -#define MPH_INFORMATION_REQ 0x0701 -#define PH_CONTROL_REQ 0x0801 - -/* layer 1 -> layer 2 */ -#define PH_ACTIVATE_IND 0x0102 -#define PH_ACTIVATE_CNF 0x4102 -#define PH_DEACTIVATE_IND 0x0202 -#define PH_DEACTIVATE_CNF 0x4202 -#define PH_DATA_IND 0x2002 -#define PH_DATA_E_IND 0x3002 -#define MPH_ACTIVATE_IND 0x0502 -#define MPH_DEACTIVATE_IND 0x0602 -#define MPH_INFORMATION_IND 0x0702 -#define PH_DATA_CNF 0x6002 -#define PH_CONTROL_IND 0x0802 -#define PH_CONTROL_CNF 0x4802 - -/* layer 3 -> layer 2 */ -#define DL_ESTABLISH_REQ 0x1004 -#define DL_RELEASE_REQ 0x1104 -#define DL_DATA_REQ 0x3004 -#define DL_UNITDATA_REQ 0x3104 -#define DL_INFORMATION_REQ 0x0004 - -/* layer 2 -> layer 3 */ -#define DL_ESTABLISH_IND 0x1008 -#define DL_ESTABLISH_CNF 0x5008 -#define DL_RELEASE_IND 0x1108 -#define DL_RELEASE_CNF 0x5108 -#define DL_DATA_IND 0x3008 -#define DL_UNITDATA_IND 0x3108 -#define DL_INFORMATION_IND 0x0008 - -/* intern layer 2 managment */ -#define MDL_ASSIGN_REQ 0x1804 -#define MDL_ASSIGN_IND 0x1904 -#define MDL_REMOVE_REQ 0x1A04 -#define MDL_REMOVE_IND 0x1B04 -#define MDL_STATUS_UP_IND 0x1C04 -#define MDL_STATUS_DOWN_IND 0x1D04 -#define MDL_STATUS_UI_IND 0x1E04 -#define MDL_ERROR_IND 0x1F04 -#define MDL_ERROR_RSP 0x5F04 - -/* DL_INFORMATION_IND types */ -#define DL_INFO_L2_CONNECT 0x0001 -#define DL_INFO_L2_REMOVED 0x0002 - -/* PH_CONTROL types */ -/* TOUCH TONE IS 0x20XX XX "0"..."9", "A","B","C","D","*","#" */ -#define DTMF_TONE_VAL 0x2000 -#define DTMF_TONE_MASK 0x007F -#define DTMF_TONE_START 0x2100 -#define DTMF_TONE_STOP 0x2200 -#define DTMF_HFC_COEF 0x4000 -#define DSP_CONF_JOIN 0x2403 -#define DSP_CONF_SPLIT 0x2404 -#define DSP_RECEIVE_OFF 0x2405 -#define DSP_RECEIVE_ON 0x2406 -#define DSP_ECHO_ON 0x2407 -#define DSP_ECHO_OFF 0x2408 -#define DSP_MIX_ON 0x2409 -#define DSP_MIX_OFF 0x240a -#define DSP_DELAY 0x240b -#define DSP_JITTER 0x240c -#define DSP_TXDATA_ON 0x240d -#define DSP_TXDATA_OFF 0x240e -#define DSP_TX_DEJITTER 0x240f -#define DSP_TX_DEJ_OFF 0x2410 -#define DSP_TONE_PATT_ON 0x2411 -#define DSP_TONE_PATT_OFF 0x2412 -#define DSP_VOL_CHANGE_TX 0x2413 -#define DSP_VOL_CHANGE_RX 0x2414 -#define DSP_BF_ENABLE_KEY 0x2415 -#define DSP_BF_DISABLE 0x2416 -#define DSP_BF_ACCEPT 0x2416 -#define DSP_BF_REJECT 0x2417 -#define DSP_PIPELINE_CFG 0x2418 -#define HFC_VOL_CHANGE_TX 0x2601 -#define HFC_VOL_CHANGE_RX 0x2602 -#define HFC_SPL_LOOP_ON 0x2603 -#define HFC_SPL_LOOP_OFF 0x2604 - -/* DSP_TONE_PATT_ON parameter */ -#define TONE_OFF 0x0000 -#define TONE_GERMAN_DIALTONE 0x0001 -#define TONE_GERMAN_OLDDIALTONE 0x0002 -#define TONE_AMERICAN_DIALTONE 0x0003 -#define TONE_GERMAN_DIALPBX 0x0004 -#define TONE_GERMAN_OLDDIALPBX 0x0005 -#define TONE_AMERICAN_DIALPBX 0x0006 -#define TONE_GERMAN_RINGING 0x0007 -#define TONE_GERMAN_OLDRINGING 0x0008 -#define TONE_AMERICAN_RINGPBX 0x000b -#define TONE_GERMAN_RINGPBX 0x000c -#define TONE_GERMAN_OLDRINGPBX 0x000d -#define TONE_AMERICAN_RINGING 0x000e -#define TONE_GERMAN_BUSY 0x000f -#define TONE_GERMAN_OLDBUSY 0x0010 -#define TONE_AMERICAN_BUSY 0x0011 -#define TONE_GERMAN_HANGUP 0x0012 -#define TONE_GERMAN_OLDHANGUP 0x0013 -#define TONE_AMERICAN_HANGUP 0x0014 -#define TONE_SPECIAL_INFO 0x0015 -#define TONE_GERMAN_GASSENBESETZT 0x0016 -#define TONE_GERMAN_AUFSCHALTTON 0x0016 - -/* MPH_INFORMATION_IND */ -#define L1_SIGNAL_LOS_OFF 0x0010 -#define L1_SIGNAL_LOS_ON 0x0011 -#define L1_SIGNAL_AIS_OFF 0x0012 -#define L1_SIGNAL_AIS_ON 0x0013 -#define L1_SIGNAL_RDI_OFF 0x0014 -#define L1_SIGNAL_RDI_ON 0x0015 -#define L1_SIGNAL_SLIP_RX 0x0020 -#define L1_SIGNAL_SLIP_TX 0x0021 - -/* - * protocol ids - * D channel 1-31 - * B channel 33 - 63 - */ - -#define ISDN_P_NONE 0 -#define ISDN_P_BASE 0 -#define ISDN_P_TE_S0 0x01 -#define ISDN_P_NT_S0 0x02 -#define ISDN_P_TE_E1 0x03 -#define ISDN_P_NT_E1 0x04 -#define ISDN_P_TE_UP0 0x05 -#define ISDN_P_NT_UP0 0x06 - -#define IS_ISDN_P_TE(p) ((p == ISDN_P_TE_S0) || (p == ISDN_P_TE_E1) || \ - (p == ISDN_P_TE_UP0) || (p == ISDN_P_LAPD_TE)) -#define IS_ISDN_P_NT(p) ((p == ISDN_P_NT_S0) || (p == ISDN_P_NT_E1) || \ - (p == ISDN_P_NT_UP0) || (p == ISDN_P_LAPD_NT)) -#define IS_ISDN_P_S0(p) ((p == ISDN_P_TE_S0) || (p == ISDN_P_NT_S0)) -#define IS_ISDN_P_E1(p) ((p == ISDN_P_TE_E1) || (p == ISDN_P_NT_E1)) -#define IS_ISDN_P_UP0(p) ((p == ISDN_P_TE_UP0) || (p == ISDN_P_NT_UP0)) - - -#define ISDN_P_LAPD_TE 0x10 -#define ISDN_P_LAPD_NT 0x11 - -#define ISDN_P_B_MASK 0x1f -#define ISDN_P_B_START 0x20 - -#define ISDN_P_B_RAW 0x21 -#define ISDN_P_B_HDLC 0x22 -#define ISDN_P_B_X75SLP 0x23 -#define ISDN_P_B_L2DTMF 0x24 -#define ISDN_P_B_L2DSP 0x25 -#define ISDN_P_B_L2DSPHDLC 0x26 - -#define OPTION_L2_PMX 1 -#define OPTION_L2_PTP 2 -#define OPTION_L2_FIXEDTEI 3 -#define OPTION_L2_CLEANUP 4 - -/* should be in sync with linux/kobject.h:KOBJ_NAME_LEN */ -#define MISDN_MAX_IDLEN 20 - -struct mISDNhead { - unsigned int prim; - unsigned int id; -} __attribute__((packed)); - -#define MISDN_HEADER_LEN sizeof(struct mISDNhead) -#define MAX_DATA_SIZE 2048 -#define MAX_DATA_MEM (MAX_DATA_SIZE + MISDN_HEADER_LEN) -#define MAX_DFRAME_LEN 260 - -#define MISDN_ID_ADDR_MASK 0xFFFF -#define MISDN_ID_TEI_MASK 0xFF00 -#define MISDN_ID_SAPI_MASK 0x00FF -#define MISDN_ID_TEI_ANY 0x7F00 - -#define MISDN_ID_ANY 0xFFFF -#define MISDN_ID_NONE 0xFFFE - -#define GROUP_TEI 127 -#define TEI_SAPI 63 -#define CTRL_SAPI 0 - -#define MISDN_MAX_CHANNEL 127 -#define MISDN_CHMAP_SIZE ((MISDN_MAX_CHANNEL + 1) >> 3) - -#define SOL_MISDN 0 - -struct sockaddr_mISDN { - sa_family_t family; - unsigned char dev; - unsigned char channel; - unsigned char sapi; - unsigned char tei; -}; - -struct mISDNversion { - unsigned char major; - unsigned char minor; - unsigned short release; -}; - -#define MAX_DEVICE_ID 63 - -struct mISDN_devinfo { - u_int id; - u_int Dprotocols; - u_int Bprotocols; - u_int protocol; - u_char channelmap[MISDN_CHMAP_SIZE]; - u_int nrbchan; - char name[MISDN_MAX_IDLEN]; -}; - -struct mISDN_devrename { - u_int id; - char name[MISDN_MAX_IDLEN]; -}; - -struct ph_info_ch { - int32_t protocol; - int64_t Flags; -}; - -struct ph_info_dch { - struct ph_info_ch ch; - int16_t state; - int16_t num_bch; -}; - -struct ph_info { - struct ph_info_dch dch; - struct ph_info_ch bch[]; -}; - -/* timer device ioctl */ -#define IMADDTIMER _IOR('I', 64, int) -#define IMDELTIMER _IOR('I', 65, int) -/* socket ioctls */ -#define IMGETVERSION _IOR('I', 66, int) -#define IMGETCOUNT _IOR('I', 67, int) -#define IMGETDEVINFO _IOR('I', 68, int) -#define IMCTRLREQ _IOR('I', 69, int) -#define IMCLEAR_L2 _IOR('I', 70, int) -#define IMSETDEVNAME _IOR('I', 71, struct mISDN_devrename) - -static inline int -test_channelmap(u_int nr, u_char *map) -{ - if (nr <= MISDN_MAX_CHANNEL) - return map[nr >> 3] & (1 << (nr & 7)); - else - return 0; -} - -static inline void -set_channelmap(u_int nr, u_char *map) -{ - map[nr >> 3] |= (1 << (nr & 7)); -} - -static inline void -clear_channelmap(u_int nr, u_char *map) -{ - map[nr >> 3] &= ~(1 << (nr & 7)); -} - -/* CONTROL_CHANNEL parameters */ -#define MISDN_CTRL_GETOP 0x0000 -#define MISDN_CTRL_LOOP 0x0001 -#define MISDN_CTRL_CONNECT 0x0002 -#define MISDN_CTRL_DISCONNECT 0x0004 -#define MISDN_CTRL_PCMCONNECT 0x0010 -#define MISDN_CTRL_PCMDISCONNECT 0x0020 -#define MISDN_CTRL_SETPEER 0x0040 -#define MISDN_CTRL_UNSETPEER 0x0080 -#define MISDN_CTRL_RX_OFF 0x0100 -#define MISDN_CTRL_FILL_EMPTY 0x0200 -#define MISDN_CTRL_GETPEER 0x0400 -#define MISDN_CTRL_HW_FEATURES_OP 0x2000 -#define MISDN_CTRL_HW_FEATURES 0x2001 -#define MISDN_CTRL_HFC_OP 0x4000 -#define MISDN_CTRL_HFC_PCM_CONN 0x4001 -#define MISDN_CTRL_HFC_PCM_DISC 0x4002 -#define MISDN_CTRL_HFC_CONF_JOIN 0x4003 -#define MISDN_CTRL_HFC_CONF_SPLIT 0x4004 -#define MISDN_CTRL_HFC_RECEIVE_OFF 0x4005 -#define MISDN_CTRL_HFC_RECEIVE_ON 0x4006 -#define MISDN_CTRL_HFC_ECHOCAN_ON 0x4007 -#define MISDN_CTRL_HFC_ECHOCAN_OFF 0x4008 - - -/* socket options */ -#define MISDN_TIME_STAMP 0x0001 - -struct mISDN_ctrl_req { - int op; - int channel; - int p1; - int p2; -}; - -/* muxer options */ -#define MISDN_OPT_ALL 1 -#define MISDN_OPT_TEIMGR 2 - -#endif /* mISDNIF_H */ diff --git a/openbsc/include/openbsc/Makefile.am b/openbsc/include/openbsc/Makefile.am deleted file mode 100644 index 2740a5d0..00000000 --- a/openbsc/include/openbsc/Makefile.am +++ /dev/null @@ -1,100 +0,0 @@ -noinst_HEADERS = \ - abis_nm.h \ - abis_om2000.h \ - abis_rsl.h \ - arfcn_range_encode.h \ - auth.h \ - bsc_msc.h \ - bsc_msg_filter.h \ - bsc_nat.h \ - bsc_nat_callstats.h \ - bsc_nat_sccp.h \ - bsc_rll.h \ - bsc_subscriber.h \ - bss.h \ - bts_ipaccess_nanobts_omlattr.h \ - chan_alloc.h \ - common_bsc.h \ - common_cs.h \ - crc24.h \ - ctrl.h \ - db.h \ - debug.h \ - e1_config.h \ - gb_proxy.h \ - gprs_gb_parse.h \ - gprs_gmm.h \ - gprs_llc.h \ - gprs_llc_xid.h \ - gprs_sgsn.h \ - gprs_sndcp.h \ - gprs_sndcp_comp.h \ - gprs_sndcp_dcomp.h \ - gprs_sndcp_pcomp.h \ - gprs_sndcp_xid.h \ - gprs_subscriber.h \ - gprs_utils.h \ - gsm_04_08.h \ - gsm_04_11.h \ - gsm_04_80.h \ - gsm_data.h \ - gsm_data_shared.h \ - gsm_subscriber.h \ - gsup_client.h \ - gtphub.h \ - handover.h \ - handover_decision.h \ - ipaccess.h \ - iu.h \ - meas_feed.h \ - meas_rep.h \ - mgcp.h \ - mgcp_internal.h \ - mgcp_transcode.h \ - misdn.h \ - mncc.h \ - mncc_int.h \ - nat_rewrite_trie.h \ - network_listen.h \ - oap_client.h \ - openbscdefines.h \ - osmo_bsc.h \ - osmo_bsc_grace.h \ - osmo_bsc_rf.h \ - osmo_msc.h \ - bsc_msc_data.h \ - osmux.h \ - paging.h \ - pcu_if.h \ - pcuif_proto.h \ - rest_octets.h \ - rrlp.h \ - rs232.h \ - rtp_proxy.h \ - sgsn.h \ - signal.h \ - silent_call.h \ - slhc.h \ - smpp.h \ - sms_queue.h \ - socket.h \ - system_information.h \ - token_auth.h \ - transaction.h \ - trau_mux.h \ - trau_upqueue.h \ - ussd.h \ - vty.h \ - v42bis.h \ - v42bis_private.h \ - $(NULL) - -openbsc_HEADERS = \ - bsc_api.h \ - gsm_04_08.h \ - meas_rep.h \ - $(NULL) - -# DO NOT add a newline and '$(NULL)' to this line. That would add a trailing -# space to the directory installed: $prefix/include/'openbsc ' -openbscdir = $(includedir)/openbsc diff --git a/openbsc/include/openbsc/abis_nm.h b/openbsc/include/openbsc/abis_nm.h deleted file mode 100644 index db2a659e..00000000 --- a/openbsc/include/openbsc/abis_nm.h +++ /dev/null @@ -1,180 +0,0 @@ -/* GSM Network Management messages on the A-bis interface - * 3GPP TS 12.21 version 8.0.0 Release 1999 / ETSI TS 100 623 V8.0.0 */ - -/* (C) 2008-2009 by Harald Welte - * All Rights Reserved - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation; either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - * - */ - -#ifndef _NM_H -#define _NM_H - -#include -#include -#include - -#include - -/* max number of attributes represented as 3GPP TS 52.021 §9.4.62 SW Description array */ -#define MAX_BTS_ATTR 5 - -struct cell_global_id { - uint16_t mcc; - uint16_t mnc; - uint16_t lac; - uint16_t ci; -}; - -/* The BCCH info from an ip.access test, in host byte order - * and already parsed... */ -struct ipac_bcch_info { - struct llist_head list; - - uint16_t info_type; - uint8_t freq_qual; - uint16_t arfcn; - uint8_t rx_lev; - uint8_t rx_qual; - int16_t freq_err; - uint16_t frame_offset; - uint32_t frame_nr_offset; - uint8_t bsic; - struct cell_global_id cgi; - uint8_t ba_list_si2[16]; - uint8_t ba_list_si2bis[16]; - uint8_t ba_list_si2ter[16]; - uint8_t ca_list_si1[16]; -}; - -/* PUBLIC */ - -struct msgb; - -struct abis_nm_cfg { - /* callback for unidirectional reports */ - int (*report_cb)(struct msgb *, - struct abis_om_fom_hdr *); - /* callback for software activate requests from BTS */ - int (*sw_act_req)(struct msgb *); -}; - -extern int abis_nm_rcvmsg(struct msgb *msg); - -int abis_nm_tlv_parse(struct tlv_parsed *tp, struct gsm_bts *bts, const uint8_t *buf, int len); -int abis_nm_rx(struct msgb *msg); -int abis_nm_opstart(struct gsm_bts *bts, uint8_t obj_class, uint8_t i0, uint8_t i1, uint8_t i2); -int abis_nm_chg_adm_state(struct gsm_bts *bts, uint8_t obj_class, uint8_t i0, - uint8_t i1, uint8_t i2, enum abis_nm_adm_state adm_state); -int abis_nm_establish_tei(struct gsm_bts *bts, uint8_t trx_nr, - uint8_t e1_port, uint8_t e1_timeslot, uint8_t e1_subslot, - uint8_t tei); -int abis_nm_conn_terr_sign(struct gsm_bts_trx *trx, - uint8_t e1_port, uint8_t e1_timeslot, uint8_t e1_subslot); -int abis_nm_conn_terr_traf(struct gsm_bts_trx_ts *ts, - uint8_t e1_port, uint8_t e1_timeslot, - uint8_t e1_subslot); -int abis_nm_get_attr(struct gsm_bts *bts, uint8_t obj_class, - uint8_t bts_nr, uint8_t trx_nr, uint8_t ts_nr, - const uint8_t *attr, uint8_t attr_len); -int abis_nm_set_bts_attr(struct gsm_bts *bts, uint8_t *attr, int attr_len); -int abis_nm_set_radio_attr(struct gsm_bts_trx *trx, uint8_t *attr, int attr_len); -int abis_nm_set_channel_attr(struct gsm_bts_trx_ts *ts, uint8_t chan_comb); -int abis_nm_sw_act_req_ack(struct gsm_bts *bts, uint8_t obj_class, uint8_t i1, - uint8_t i2, uint8_t i3, int nack, uint8_t *attr, int att_len); -int abis_nm_raw_msg(struct gsm_bts *bts, int len, uint8_t *msg); -int abis_nm_event_reports(struct gsm_bts *bts, int on); -int abis_nm_reset_resource(struct gsm_bts *bts); -int abis_nm_software_load(struct gsm_bts *bts, int trx_nr, const char *fname, - uint8_t win_size, int forced, - gsm_cbfn *cbfn, void *cb_data); -int abis_nm_software_load_status(struct gsm_bts *bts); -int abis_nm_software_activate(struct gsm_bts *bts, const char *fname, - gsm_cbfn *cbfn, void *cb_data); - -int abis_nm_conn_mdrop_link(struct gsm_bts *bts, uint8_t e1_port0, uint8_t ts0, - uint8_t e1_port1, uint8_t ts1); - -int abis_nm_perform_test(struct gsm_bts *bts, uint8_t obj_class, - uint8_t bts_nr, uint8_t trx_nr, uint8_t ts_nr, - uint8_t test_nr, uint8_t auton_report, struct msgb *msg); - -/* Siemens / BS-11 specific */ -int abis_nm_bs11_reset_resource(struct gsm_bts *bts); -int abis_nm_bs11_db_transmission(struct gsm_bts *bts, int begin); -int abis_nm_bs11_create_object(struct gsm_bts *bts, enum abis_bs11_objtype type, - uint8_t idx, uint8_t attr_len, const uint8_t *attr); -int abis_nm_bs11_create_envaBTSE(struct gsm_bts *bts, uint8_t idx); -int abis_nm_bs11_create_bport(struct gsm_bts *bts, uint8_t idx); -int abis_nm_bs11_delete_object(struct gsm_bts *bts, - enum abis_bs11_objtype type, uint8_t idx); -int abis_nm_bs11_delete_bport(struct gsm_bts *bts, uint8_t idx); -int abis_nm_bs11_conn_oml_tei(struct gsm_bts *bts, uint8_t e1_port, - uint8_t e1_timeslot, uint8_t e1_subslot, uint8_t tei); -int abis_nm_bs11_get_oml_tei_ts(struct gsm_bts *bts); -int abis_nm_bs11_get_serno(struct gsm_bts *bts); -int abis_nm_bs11_set_trx_power(struct gsm_bts_trx *trx, uint8_t level); -int abis_nm_bs11_get_trx_power(struct gsm_bts_trx *trx); -int abis_nm_bs11_logon(struct gsm_bts *bts, uint8_t level, const char *name, int on); -int abis_nm_bs11_factory_logon(struct gsm_bts *bts, int on); -int abis_nm_bs11_infield_logon(struct gsm_bts *bts, int on); -int abis_nm_bs11_set_trx1_pw(struct gsm_bts *bts, const char *password); -int abis_nm_bs11_set_pll_locked(struct gsm_bts *bts, int locked); -int abis_nm_bs11_get_pll_mode(struct gsm_bts *bts); -int abis_nm_bs11_set_pll(struct gsm_bts *bts, int value); -int abis_nm_bs11_get_cclk(struct gsm_bts *bts); -int abis_nm_bs11_get_state(struct gsm_bts *bts); -int abis_nm_bs11_load_swl(struct gsm_bts *bts, const char *fname, - uint8_t win_size, int forced, gsm_cbfn *cbfn); -int abis_nm_bs11_set_ext_time(struct gsm_bts *bts); -int abis_nm_bs11_get_bport_line_cfg(struct gsm_bts *bts, uint8_t bport); -int abis_nm_bs11_set_bport_line_cfg(struct gsm_bts *bts, uint8_t bport, enum abis_bs11_line_cfg line_cfg); -int abis_nm_bs11_bsc_disconnect(struct gsm_bts *bts, int reconnect); -int abis_nm_bs11_restart(struct gsm_bts *bts); - -/* ip.access nanoBTS specific commands */ -int abis_nm_ipaccess_msg(struct gsm_bts *bts, uint8_t msg_type, - uint8_t obj_class, uint8_t bts_nr, - uint8_t trx_nr, uint8_t ts_nr, - uint8_t *attr, int attr_len); -int abis_nm_ipaccess_set_nvattr(struct gsm_bts_trx *trx, uint8_t *attr, - int attr_len); -int abis_nm_ipaccess_restart(struct gsm_bts_trx *trx); -int abis_nm_ipaccess_set_attr(struct gsm_bts *bts, uint8_t obj_class, - uint8_t bts_nr, uint8_t trx_nr, uint8_t ts_nr, - uint8_t *attr, uint8_t attr_len); -int abis_nm_ipaccess_rsl_connect(struct gsm_bts_trx *trx, - uint32_t ip, uint16_t port, uint8_t stream); -void abis_nm_ipaccess_cgi(uint8_t *buf, struct gsm_bts *bts); -int ipac_parse_bcch_info(struct ipac_bcch_info *binf, uint8_t *buf); -const char *ipacc_testres_name(uint8_t res); - -/* Functions calling into other code parts */ -int nm_is_running(struct gsm_nm_state *s); - -int abis_nm_vty_init(void); - -void abis_nm_clear_queue(struct gsm_bts *bts); - -int _abis_nm_sendmsg(struct msgb *msg); - -void abis_nm_queue_send_next(struct gsm_bts *bts); /* for bs11_config. */ - -int abis_nm_select_newest_sw(const struct abis_nm_sw_desc *sw, const size_t len); - -/* Helper functions for updating attributes */ -int abis_nm_update_max_power_red(struct gsm_bts_trx *trx); - -#endif /* _NM_H */ diff --git a/openbsc/include/openbsc/abis_om2000.h b/openbsc/include/openbsc/abis_om2000.h deleted file mode 100644 index b093a035..00000000 --- a/openbsc/include/openbsc/abis_om2000.h +++ /dev/null @@ -1,129 +0,0 @@ -#ifndef OPENBSC_ABIS_OM2K_H -#define OPENBSC_ABIS_OM2K_H -/* Ericsson RBS 2xxx GSM O&M (OM2000) messages on the A-bis interface - * implemented based on protocol trace analysis, no formal documentation */ - -/* (C) 2010-2011 by Harald Welte - * - * All Rights Reserved - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation; either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - * - */ - -enum abis_om2k_mo_cls { - OM2K_MO_CLS_TRXC = 0x01, - OM2K_MO_CLS_TS = 0x03, - OM2K_MO_CLS_TF = 0x04, - OM2K_MO_CLS_IS = 0x05, - OM2K_MO_CLS_CON = 0x06, - OM2K_MO_CLS_DP = 0x07, - OM2K_MO_CLS_CF = 0x0a, - OM2K_MO_CLS_TX = 0x0b, - OM2K_MO_CLS_RX = 0x0c, -}; - -enum om2k_mo_state { - OM2K_MO_S_RESET = 0, - OM2K_MO_S_STARTED, - OM2K_MO_S_ENABLED, - OM2K_MO_S_DISABLED, -}; - -/* on-wire format for IS conn group */ -struct om2k_is_conn_grp { - uint16_t icp1; - uint16_t icp2; - uint8_t cont_idx; -} __attribute__ ((packed)); - -/* internal data formant for IS conn group */ -struct is_conn_group { - struct llist_head list; - uint16_t icp1; - uint16_t icp2; - uint8_t ci; -}; - -/* on-wire format for CON Path */ -struct om2k_con_path { - uint16_t ccp; - uint8_t ci; - uint8_t tag; - uint8_t tei; -} __attribute__ ((packed)); - -/* internal data format for CON group */ -struct con_group { - /* links list of CON groups in BTS */ - struct llist_head list; - struct gsm_bts *bts; - /* CON Group ID */ - uint8_t cg; - /* list of CON paths in this group */ - struct llist_head paths; -}; - -/* internal data format for CON path */ -struct con_path { - /* links with con_group.paths */ - struct llist_head list; - /* CON Connection Point */ - uint16_t ccp; - /* Contiguity Index */ - uint8_t ci; - /* Tag */ - uint8_t tag; - /* TEI */ - uint8_t tei; -}; - -extern const struct abis_om2k_mo om2k_mo_cf; -extern const struct abis_om2k_mo om2k_mo_is; -extern const struct abis_om2k_mo om2k_mo_con; -extern const struct abis_om2k_mo om2k_mo_tf; - -extern const struct value_string om2k_mo_class_short_vals[]; - -int abis_om2k_rcvmsg(struct msgb *msg); - -extern const struct abis_om2k_mo om2k_mo_cf; - -int abis_om2k_tx_reset_cmd(struct gsm_bts *bts, const struct abis_om2k_mo *mo); -int abis_om2k_tx_start_req(struct gsm_bts *bts, const struct abis_om2k_mo *mo); -int abis_om2k_tx_status_req(struct gsm_bts *bts, const struct abis_om2k_mo *mo); -int abis_om2k_tx_connect_cmd(struct gsm_bts *bts, const struct abis_om2k_mo *mo); -int abis_om2k_tx_disconnect_cmd(struct gsm_bts *bts, const struct abis_om2k_mo *mo); -int abis_om2k_tx_enable_req(struct gsm_bts *bts, const struct abis_om2k_mo *mo); -int abis_om2k_tx_disable_req(struct gsm_bts *bts, const struct abis_om2k_mo *mo); -int abis_om2k_tx_test_req(struct gsm_bts *bts, const struct abis_om2k_mo *mo); -int abis_om2k_tx_op_info(struct gsm_bts *bts, const struct abis_om2k_mo *mo, - uint8_t operational); -int abis_om2k_tx_cap_req(struct gsm_bts *bts, const struct abis_om2k_mo *mo); -int abis_om2k_tx_is_conf_req(struct gsm_bts *bts); -int abis_om2k_tx_tf_conf_req(struct gsm_bts *bts); -int abis_om2k_tx_rx_conf_req(struct gsm_bts_trx *trx); -int abis_om2k_tx_tx_conf_req(struct gsm_bts_trx *trx); -int abis_om2k_tx_ts_conf_req(struct gsm_bts_trx_ts *ts); - -struct osmo_fsm_inst *om2k_bts_fsm_start(struct gsm_bts *bts); -void abis_om2k_bts_init(struct gsm_bts *bts); -void abis_om2k_trx_init(struct gsm_bts_trx *trx); - -int abis_om2k_vty_init(void); - -struct vty; -void abis_om2k_config_write_bts(struct vty *vty, struct gsm_bts *bts); - -#endif /* OPENBCS_ABIS_OM2K_H */ diff --git a/openbsc/include/openbsc/abis_rsl.h b/openbsc/include/openbsc/abis_rsl.h deleted file mode 100644 index 400e09f6..00000000 --- a/openbsc/include/openbsc/abis_rsl.h +++ /dev/null @@ -1,118 +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 by Harald Welte - * All Rights Reserved - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation; either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - * - */ - -#ifndef _RSL_H -#define _RSL_H - -#include -#include -#include -#include -#include - -struct gsm_bts; -struct gsm_lchan; -struct gsm_subscriber; -struct gsm_bts_trx_ts; - -#define GSM48_LEN2PLEN(a) (((a) << 2) | 1) - -int rsl_bcch_info(const struct gsm_bts_trx *trx, enum osmo_sysinfo_type si_type, const uint8_t *data, int len); -int rsl_sacch_filling(struct gsm_bts_trx *trx, uint8_t type, - const uint8_t *data, int len); -int rsl_chan_activate(struct gsm_bts_trx *trx, uint8_t chan_nr, - uint8_t act_type, - struct rsl_ie_chan_mode *chan_mode, - struct rsl_ie_chan_ident *chan_ident, - uint8_t bs_power, uint8_t ms_power, - uint8_t ta); -int rsl_chan_activate_lchan(struct gsm_lchan *lchan, uint8_t act_type, - uint8_t ho_ref); -int rsl_chan_mode_modify_req(struct gsm_lchan *ts); -int rsl_encryption_cmd(struct msgb *msg); -int rsl_paging_cmd(struct gsm_bts *bts, uint8_t paging_group, uint8_t len, - uint8_t *ms_ident, uint8_t chan_needed, bool is_gprs); -int rsl_imm_assign_cmd(struct gsm_bts *bts, uint8_t len, uint8_t *val); - -int rsl_data_request(struct msgb *msg, uint8_t link_id); -int rsl_establish_request(struct gsm_lchan *lchan, uint8_t link_id); -int rsl_relase_request(struct gsm_lchan *lchan, uint8_t link_id); - -/* Ericcson vendor specific RSL extensions */ -int rsl_ericsson_imm_assign_cmd(struct gsm_bts *bts, uint32_t tlli, uint8_t len, uint8_t *val); - -/* Siemens vendor-specific RSL extensions */ -int rsl_siemens_mrpci(struct gsm_lchan *lchan, struct rsl_mrpci *mrpci); - -/* ip.access specfic RSL extensions */ -int rsl_ipacc_crcx(struct gsm_lchan *lchan); -int rsl_ipacc_mdcx(struct gsm_lchan *lchan, uint32_t ip, - uint16_t port, uint8_t rtp_payload2); -int rsl_ipacc_mdcx_to_rtpsock(struct gsm_lchan *lchan); -int rsl_ipacc_pdch_activate(struct gsm_bts_trx_ts *ts, int act); - -int abis_rsl_rcvmsg(struct msgb *msg); - -uint64_t str_to_imsi(const char *imsi_str); -int rsl_release_request(struct gsm_lchan *lchan, uint8_t link_id, - enum rsl_rel_mode release_mode); - -int rsl_lchan_set_state(struct gsm_lchan *lchan, int); -int rsl_lchan_mark_broken(struct gsm_lchan *lchan, const char *broken); - -/* to be provided by external code */ -int rsl_deact_sacch(struct gsm_lchan *lchan); - -/* BCCH related code */ -int rsl_ccch_conf_to_bs_cc_chans(int ccch_conf); -int rsl_ccch_conf_to_bs_ccch_sdcch_comb(int ccch_conf); - -int rsl_sacch_info_modify(struct gsm_lchan *lchan, uint8_t type, - const uint8_t *data, int len); - -int rsl_chan_bs_power_ctrl(struct gsm_lchan *lchan, unsigned int fpc, int db); -int rsl_chan_ms_power_ctrl(struct gsm_lchan *lchan, unsigned int fpc, int dbm); - -/* SMSCB functionality */ -int rsl_sms_cb_command(struct gsm_bts *bts, uint8_t chan_number, - struct rsl_ie_cb_cmd_type cb_command, - const uint8_t *data, int len); - -/* some Nokia specific stuff */ -int rsl_nokia_si_begin(struct gsm_bts_trx *trx); -int rsl_nokia_si_end(struct gsm_bts_trx *trx); - -/* required for Nokia BTS power control */ -int rsl_bs_power_control(struct gsm_bts_trx *trx, uint8_t channel, uint8_t reduction); - - -int rsl_release_sapis_from(struct gsm_lchan *lchan, int start, - enum rsl_rel_mode release_mode); -int rsl_start_t3109(struct gsm_lchan *lchan); - -int rsl_direct_rf_release(struct gsm_lchan *lchan); - -void dyn_ts_init(struct gsm_bts_trx_ts *ts); -int dyn_ts_switchover_start(struct gsm_bts_trx_ts *ts, - enum gsm_phys_chan_config to_pchan); - -#endif /* RSL_MT_H */ - diff --git a/openbsc/include/openbsc/arfcn_range_encode.h b/openbsc/include/openbsc/arfcn_range_encode.h deleted file mode 100644 index 7ec710c3..00000000 --- a/openbsc/include/openbsc/arfcn_range_encode.h +++ /dev/null @@ -1,26 +0,0 @@ -#ifndef ARFCN_RANGE_ENCODE_H -#define ARFCN_RANGE_ENCODE_H - -#include - -enum gsm48_range { - ARFCN_RANGE_INVALID = -1, - ARFCN_RANGE_128 = 127, - ARFCN_RANGE_256 = 255, - ARFCN_RANGE_512 = 511, - ARFCN_RANGE_1024 = 1023, -}; - -#define RANGE_ENC_MAX_ARFCNS 29 - -int range_enc_determine_range(const int *arfcns, int size, int *f0_out); -int range_enc_arfcns(enum gsm48_range rng, const int *arfcns, int sze, int *out, int idx); -int range_enc_find_index(enum gsm48_range rng, const int *arfcns, int size); -int range_enc_filter_arfcns(int *arfcns, const int sze, const int f0, int *f0_included); - -int range_enc_range128(uint8_t *chan_list, int f0, int *w); -int range_enc_range256(uint8_t *chan_list, int f0, int *w); -int range_enc_range512(uint8_t *chan_list, int f0, int *w); -int range_enc_range1024(uint8_t *chan_list, int f0, int f0_incl, int *w); - -#endif diff --git a/openbsc/include/openbsc/auth.h b/openbsc/include/openbsc/auth.h deleted file mode 100644 index 61811316..00000000 --- a/openbsc/include/openbsc/auth.h +++ /dev/null @@ -1,26 +0,0 @@ -#ifndef _AUTH_H -#define _AUTH_H - -#include - -struct gsm_auth_tuple; -struct gsm_subscriber; - -enum auth_action { - AUTH_ERROR = -1, /* Internal error */ - AUTH_NOT_AVAIL = 0, /* No auth tuple available */ - AUTH_DO_AUTH_THEN_CIPH = 1, /* Firsth authenticate, then cipher */ - AUTH_DO_CIPH = 2, /* Only ciphering */ - AUTH_DO_AUTH = 3, /* Only authentication, no ciphering */ -}; - -extern const struct value_string auth_action_names[]; -static inline const char *auth_action_str(enum auth_action a) -{ - return get_value_string(auth_action_names, a); -} - -int auth_get_tuple_for_subscr(struct gsm_auth_tuple *atuple, - struct gsm_subscriber *subscr, int key_seq); - -#endif /* _AUTH_H */ diff --git a/openbsc/include/openbsc/bsc_api.h b/openbsc/include/openbsc/bsc_api.h deleted file mode 100644 index 3a931199..00000000 --- a/openbsc/include/openbsc/bsc_api.h +++ /dev/null @@ -1,55 +0,0 @@ -/* GSM 08.08 like API for OpenBSC */ - -#ifndef OPENBSC_BSC_API_H -#define OPENBSC_BSC_API_H - -#include "gsm_data.h" - -#define BSC_API_CONN_POL_ACCEPT 0 -#define BSC_API_CONN_POL_REJECT 1 - -struct bsc_api { - /*! \brief BTS->MSC: tell MSC a SAPI was not established */ - void (*sapi_n_reject)(struct gsm_subscriber_connection *conn, int dlci); - /*! \brief MS->MSC: Tell MSC that ciphering has been enabled */ - void (*cipher_mode_compl)(struct gsm_subscriber_connection *conn, - struct msgb *msg, uint8_t chosen_encr); - /*! \brief MS->MSC: New MM context with L3 payload */ - int (*compl_l3)(struct gsm_subscriber_connection *conn, - struct msgb *msg, uint16_t chosen_channel); - /*! \brief MS->BSC/MSC: Um L3 message */ - void (*dtap)(struct gsm_subscriber_connection *conn, uint8_t link_id, - struct msgb *msg); - /*! \brief BSC->MSC: Assignment of lchan successful */ - void (*assign_compl)(struct gsm_subscriber_connection *conn, - uint8_t rr_cause, uint8_t chosen_channel, - uint8_t encr_alg_id, uint8_t speech_mode); - /*! \brief BSC->MSC: Assignment of lchan failed */ - void (*assign_fail)(struct gsm_subscriber_connection *conn, - uint8_t cause, uint8_t *rr_cause); - /*! \brief BSC->MSC: RR conn has been cleared */ - int (*clear_request)(struct gsm_subscriber_connection *conn, - uint32_t cause); - /*! \brief BSC->MSC: Classmark Update */ - void (*classmark_chg)(struct gsm_subscriber_connection *conn, - const uint8_t *cm2, uint8_t cm2_len, - const uint8_t *cm3, uint8_t cm3_len); - - /** - * Configure the multirate setting on this channel. If it is - * not implemented AMR5.9 will be used. - */ - void (*mr_config)(struct gsm_subscriber_connection *conn, - struct gsm_lchan *lchan, int full_rate); -}; - -int bsc_api_init(struct gsm_network *network, struct bsc_api *api); -int gsm0808_submit_dtap(struct gsm_subscriber_connection *conn, struct msgb *msg, int link_id, int allow_sacch); -int gsm0808_assign_req(struct gsm_subscriber_connection *conn, int chan_mode, int full_rate); -int gsm0808_cipher_mode(struct gsm_subscriber_connection *conn, int cipher, - const uint8_t *key, int len, int include_imeisv); -int gsm0808_page(struct gsm_bts *bts, unsigned int page_group, - unsigned int mi_len, uint8_t *mi, int chan_type); -int gsm0808_clear(struct gsm_subscriber_connection *conn); - -#endif diff --git a/openbsc/include/openbsc/bsc_msc.h b/openbsc/include/openbsc/bsc_msc.h deleted file mode 100644 index 39258d36..00000000 --- a/openbsc/include/openbsc/bsc_msc.h +++ /dev/null @@ -1,65 +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 . - * - */ - -#ifndef BSC_MSC_H -#define BSC_MSC_H - -#include -#include - -#include - -struct bsc_msc_dest { - struct llist_head list; - - char *ip; - int port; - int dscp; -}; - - -struct bsc_msc_connection { - struct osmo_wqueue write_queue; - int is_connected; - int is_authenticated; - int first_contact; - - struct llist_head *dests; - - const char *name; - - void (*connection_loss) (struct bsc_msc_connection *); - void (*connected) (struct bsc_msc_connection *); - struct osmo_timer_list reconnect_timer; - struct osmo_timer_list timeout_timer; - - struct msgb *pending_msg; -}; - -struct bsc_msc_connection *bsc_msc_create(void *ctx, struct llist_head *dest); -int bsc_msc_connect(struct bsc_msc_connection *); -void bsc_msc_schedule_connect(struct bsc_msc_connection *); - -void bsc_msc_lost(struct bsc_msc_connection *); - -struct msgb *bsc_msc_id_get_resp(int fixed, const char *token, const uint8_t *res, int len); - -#endif diff --git a/openbsc/include/openbsc/bsc_msc_data.h b/openbsc/include/openbsc/bsc_msc_data.h deleted file mode 100644 index 38e87cfb..00000000 --- a/openbsc/include/openbsc/bsc_msc_data.h +++ /dev/null @@ -1,142 +0,0 @@ -/* - * Data for the true BSC - * - * (C) 2010-2015 by Holger Hans Peter Freyther - * (C) 2010-2015 by On-Waves - * All Rights Reserved - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation; either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - * - */ - -/* - * NOTE: This is about a *remote* MSC for OsmoBSC and is not part of libmsc. - */ - -#ifndef _OSMO_MSC_DATA_H -#define _OSMO_MSC_DATA_H - -#include "bsc_msc.h" - -#include -#include - -#include - -struct osmo_bsc_rf; -struct gsm_network; - -struct gsm_audio_support { - uint8_t hr : 1, - ver : 7; -}; - -enum { - MSC_CON_TYPE_NORMAL, - MSC_CON_TYPE_LOCAL, -}; - -/*! /brief Information on a remote MSC for libbsc. - */ -struct bsc_msc_data { - struct llist_head entry; - - /* Back pointer */ - struct gsm_network *network; - - int allow_emerg; - int type; - - /* local call routing */ - char *local_pref; - regex_t local_pref_reg; - - - /* Connection data */ - char *bsc_token; - uint8_t bsc_key[16]; - uint8_t bsc_key_present; - - int ping_timeout; - int pong_timeout; - struct osmo_timer_list ping_timer; - struct osmo_timer_list pong_timer; - int advanced_ping; - struct bsc_msc_connection *msc_con; - int core_mnc; - int core_mcc; - int core_lac; - int core_ci; - int rtp_base; - - /* audio codecs */ - struct gsm48_multi_rate_conf amr_conf; - struct gsm_audio_support **audio_support; - int audio_length; - - /* destinations */ - struct llist_head dests; - - /* ussd welcome text */ - char *ussd_welcome_txt; - - /* mgcp agent */ - struct osmo_wqueue mgcp_agent; - - int nr; - - /* ussd msc connection lost text */ - char *ussd_msc_lost_txt; - - /* ussd text when MSC has entered the grace period */ - char *ussd_grace_txt; - - char *acc_lst_name; -}; - -/* - * Per BSC data. - */ -struct osmo_bsc_data { - struct gsm_network *network; - - /* msc configuration */ - struct llist_head mscs; - - /* rf ctl related bits */ - char *mid_call_txt; - int mid_call_timeout; - char *rf_ctrl_name; - struct osmo_bsc_rf *rf_ctrl; - int auto_off_timeout; - - /* ussd text when there is no MSC available */ - char *ussd_no_msc_txt; - - char *acc_lst_name; -}; - - -int osmo_bsc_msc_init(struct bsc_msc_data *msc); -int osmo_bsc_sccp_init(struct gsm_network *gsmnet); -int msc_queue_write(struct bsc_msc_connection *conn, struct msgb *msg, int proto); -int msc_queue_write_with_ping(struct bsc_msc_connection *, struct msgb *msg, int proto); - -int osmo_bsc_audio_init(struct gsm_network *network); - -struct bsc_msc_data *osmo_msc_data_find(struct gsm_network *, int); -struct bsc_msc_data *osmo_msc_data_alloc(struct gsm_network *, int); - - -#endif diff --git a/openbsc/include/openbsc/bsc_msg_filter.h b/openbsc/include/openbsc/bsc_msg_filter.h deleted file mode 100644 index a9dedf43..00000000 --- a/openbsc/include/openbsc/bsc_msg_filter.h +++ /dev/null @@ -1,107 +0,0 @@ -#pragma once - -#include -#include -#include -#include - -#include - -struct vty; -struct gsm48_hdr; - -struct bsc_filter_reject_cause { - int lu_reject_cause; - int cm_reject_cause; -}; - -struct bsc_filter_barr_entry { - struct rb_node node; - - char *imsi; - int cm_reject_cause; - int lu_reject_cause; -}; - -enum bsc_filter_acc_ctr { - ACC_LIST_LOCAL_FILTER, - ACC_LIST_GLOBAL_FILTER, -}; - -struct bsc_msg_acc_lst { - struct llist_head list; - - /* counter */ - struct rate_ctr_group *stats; - - /* the name of the list */ - const char *name; - struct llist_head fltr_list; -}; - -struct bsc_msg_acc_lst_entry { - struct llist_head list; - - /* the filter */ - char *imsi_allow; - regex_t imsi_allow_re; - char *imsi_deny; - regex_t imsi_deny_re; - - /* reject reasons for the access lists */ - int cm_reject_cause; - int lu_reject_cause; -}; - -enum { - FLT_CON_TYPE_NONE, - FLT_CON_TYPE_LU, - FLT_CON_TYPE_CM_SERV_REQ, - FLT_CON_TYPE_PAG_RESP, - FLT_CON_TYPE_SSA, - FLT_CON_TYPE_LOCAL_REJECT, - FLT_CON_TYPE_OTHER, -}; - - -struct bsc_filter_state { - char *imsi; - int imsi_checked; - int con_type; -}; - -struct bsc_filter_request { - void *ctx; - struct rb_root *black_list; - struct llist_head *access_lists; - const char *local_lst_name; - const char *global_lst_name; - int bsc_nr; -}; - - -int bsc_filter_barr_adapt(void *ctx, struct rb_root *rbtree, const struct osmo_config_list *); -int bsc_filter_barr_find(struct rb_root *root, const char *imsi, int *cm, int *lu); - -/** - * Content filtering. - */ -int bsc_msg_filter_initial(struct gsm48_hdr *hdr, size_t size, - struct bsc_filter_request *req, - int *con_type, char **imsi, - struct bsc_filter_reject_cause *cause); -int bsc_msg_filter_data(struct gsm48_hdr *hdr, size_t size, - struct bsc_filter_request *req, - struct bsc_filter_state *state, - struct bsc_filter_reject_cause *cause); - -/* IMSI allow/deny handling */ -struct bsc_msg_acc_lst *bsc_msg_acc_lst_find(struct llist_head *lst, const char *name); -struct bsc_msg_acc_lst *bsc_msg_acc_lst_get(void *ctx, struct llist_head *lst, const char *name); -void bsc_msg_acc_lst_delete(struct bsc_msg_acc_lst *lst); - -struct bsc_msg_acc_lst_entry *bsc_msg_acc_lst_entry_create(struct bsc_msg_acc_lst *); -int bsc_msg_acc_lst_check_allow(struct bsc_msg_acc_lst *lst, const char *imsi); - -void bsc_msg_lst_vty_init(void *ctx, struct llist_head *lst, int node); -void bsc_msg_acc_lst_write(struct vty *vty, struct bsc_msg_acc_lst *lst); diff --git a/openbsc/include/openbsc/bsc_nat.h b/openbsc/include/openbsc/bsc_nat.h deleted file mode 100644 index 94ab0e5f..00000000 --- a/openbsc/include/openbsc/bsc_nat.h +++ /dev/null @@ -1,462 +0,0 @@ -/* - * (C) 2010-2012 by Holger Hans Peter Freyther - * (C) 2010-2012 by On-Waves - * All Rights Reserved - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation; either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - * - */ - -#ifndef BSC_NAT_H -#define BSC_NAT_H - -#include "mgcp.h" -#include "bsc_msg_filter.h" - - -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include - -#define DIR_BSC 1 -#define DIR_MSC 2 - -#define PAGIN_GROUP_UNASSIGNED -1 - -struct sccp_source_reference; -struct nat_sccp_connection; -struct bsc_nat_parsed; -struct bsc_nat; -struct bsc_nat_ussd_con; -struct nat_rewrite_rule; - -/* - * Is this terminated to the MSC, to the local machine (release - * handling for IMSI filtering) or to a USSD provider? - */ -enum { - NAT_CON_END_MSC, - NAT_CON_END_LOCAL, - NAT_CON_END_USSD, -}; - -/* - * Pending command entry - */ -struct bsc_cmd_list { - struct llist_head list_entry; - - struct osmo_timer_list timeout; - - /* The NATed ID used on the bsc_con*/ - int nat_id; - - /* The control connection from which the command originated */ - struct ctrl_connection *ccon; - - /* The command from the control connection */ - struct ctrl_cmd *cmd; -}; - -/* - * Per BSC data structure - */ -struct bsc_connection { - struct llist_head list_entry; - - /* do we know anything about this BSC? */ - int authenticated; - uint8_t last_rand[16]; - - /* the fd we use to communicate */ - struct osmo_wqueue write_queue; - - /* incoming message buffer */ - struct msgb *pending_msg; - - /* the BSS associated */ - struct bsc_config *cfg; - - /* a timeout node */ - struct osmo_timer_list id_timeout; - - /* pong timeout */ - struct osmo_timer_list ping_timeout; - struct osmo_timer_list pong_timeout; - - /* mgcp related code */ - char *_endpoint_status; - int number_multiplexes; - int max_endpoints; - int last_endpoint; - int next_transaction; - uint32_t pending_dlcx_count; - struct llist_head pending_dlcx; - - /* track the pending commands for this BSC */ - struct llist_head cmd_pending; - int last_id; - - /* a back pointer */ - struct bsc_nat *nat; -}; - -/** - * Stats per BSC - */ -struct bsc_config_stats { - struct rate_ctr_group *ctrg; -}; - -enum bsc_cfg_ctr { - BCFG_CTR_SCCP_CONN, - BCFG_CTR_SCCP_CALLS, - BCFG_CTR_NET_RECONN, - BCFG_CTR_DROPPED_SCCP, - BCFG_CTR_DROPPED_CALLS, - BCFG_CTR_REJECTED_CR, - BCFG_CTR_REJECTED_MSG, - BCFG_CTR_ILL_PACKET, - BCFG_CTR_CON_TYPE_LU, - BCFG_CTR_CON_CMSERV_RQ, - BCFG_CTR_CON_PAG_RESP, - BCFG_CTR_CON_SSA, - BCFG_CTR_CON_OTHER, -}; - -/** - * One BSC entry in the config - */ -struct bsc_config { - struct llist_head entry; - - uint8_t key[16]; - uint8_t key_present; - char *token; - int nr; - - char *description; - - /* imsi white and blacklist */ - char *acc_lst_name; - - int forbid_paging; - int paging_group; - - /* audio handling */ - int max_endpoints; - - /* used internally for reload handling */ - bool remove; - bool token_updated; - - /* backpointer */ - struct bsc_nat *nat; - - struct bsc_config_stats stats; - - struct llist_head lac_list; - - /* Osmux is enabled/disabled per BSC */ - int osmux; -}; - -struct bsc_lac_entry { - struct llist_head entry; - uint16_t lac; -}; - -struct bsc_nat_paging_group { - struct llist_head entry; - - /* list of lac entries */ - struct llist_head lists; - int nr; -}; - -/** - * BSCs point of view of endpoints - */ -struct bsc_endpoint { - /* the operation that is carried out */ - int transaction_state; - /* the pending transaction id */ - char *transaction_id; - /* the bsc we are talking to */ - struct bsc_connection *bsc; -}; - -/** - * Statistic for the nat. - */ -struct bsc_nat_statistics { - struct { - struct osmo_counter *conn; - struct osmo_counter *calls; - } sccp; - - struct { - struct osmo_counter *reconn; - struct osmo_counter *auth_fail; - } bsc; - - struct { - struct osmo_counter *reconn; - } msc; - - struct { - struct osmo_counter *reconn; - } ussd; -}; - -/** - * the structure of the "nat" network - */ -struct bsc_nat { - /* active SCCP connections that need patching */ - struct llist_head sccp_connections; - - /* active BSC connections that need patching */ - struct llist_head bsc_connections; - - /* access lists */ - struct llist_head access_lists; - - /* paging groups */ - struct llist_head paging_groups; - - /* known BSC's */ - struct llist_head bsc_configs; - int num_bsc; - int bsc_ip_dscp; - - /* MGCP config */ - struct mgcp_config *mgcp_cfg; - uint8_t mgcp_msg[4096]; - int mgcp_length; - int mgcp_ipa; - int sdp_ensure_amr_mode_set; - - /* msc things */ - struct llist_head dests; - struct bsc_msc_dest *main_dest; - struct bsc_msc_connection *msc_con; - char *token; - - /* timeouts */ - int auth_timeout; - int ping_timeout; - int pong_timeout; - - struct bsc_endpoint *bsc_endpoints; - - /* path to file with BSC config */ - char *include_file; - char *include_base; - char *resolved_path; - - /* filter */ - char *acc_lst_name; - - /* Barring of subscribers with a rb tree */ - struct rb_root imsi_black_list; - char *imsi_black_list_fn; - - /* number rewriting */ - char *num_rewr_name; - struct llist_head num_rewr; - char *num_rewr_post_name; - struct llist_head num_rewr_post; - - char *smsc_rewr_name; - struct llist_head smsc_rewr; - char *tpdest_match_name; - struct llist_head tpdest_match; - char *sms_clear_tp_srr_name; - struct llist_head sms_clear_tp_srr; - char *sms_num_rewr_name; - struct llist_head sms_num_rewr; - - /* more rewriting */ - char *num_rewr_trie_name; - struct nat_rewrite *num_rewr_trie; - - /* USSD messages we want to match */ - char *ussd_lst_name; - char *ussd_query; - regex_t ussd_query_re; - char *ussd_token; - char *ussd_local; - struct osmo_fd ussd_listen; - struct bsc_nat_ussd_con *ussd_con; - - /* for maintainenance */ - int blocked; - - /* statistics */ - struct bsc_nat_statistics stats; - - /* control interface */ - struct ctrl_handle *ctrl; -}; - -struct bsc_nat_ussd_con { - struct osmo_wqueue queue; - struct bsc_nat *nat; - int authorized; - - struct msgb *pending_msg; - - struct osmo_timer_list auth_timeout; -}; - -/* create and init the structures */ -struct bsc_config *bsc_config_alloc(struct bsc_nat *nat, const char *token, - unsigned int number); -struct bsc_config *bsc_config_num(struct bsc_nat *nat, int num); -struct bsc_config *bsc_config_by_token(struct bsc_nat *nat, const char *token, int len); -void bsc_config_free(struct bsc_config *); -void bsc_config_add_lac(struct bsc_config *cfg, int lac); -void bsc_config_del_lac(struct bsc_config *cfg, int lac); -int bsc_config_handles_lac(struct bsc_config *cfg, int lac); - -struct bsc_nat *bsc_nat_alloc(void); -struct bsc_connection *bsc_connection_alloc(struct bsc_nat *nat); -void bsc_nat_set_msc_ip(struct bsc_nat *bsc, const char *ip); - -void sccp_connection_destroy(struct nat_sccp_connection *); -void bsc_close_connection(struct bsc_connection *); - -const char *bsc_con_type_to_string(int type); - -/** - * parse the given message into the above structure - */ -struct bsc_nat_parsed *bsc_nat_parse(struct msgb *msg); - -/** - * filter based on IP Access header in both directions - */ -int bsc_nat_filter_ipa(int direction, struct msgb *msg, struct bsc_nat_parsed *parsed); -int bsc_nat_vty_init(struct bsc_nat *nat); -int bsc_nat_find_paging(struct msgb *msg, const uint8_t **,int *len); - -/** - * SCCP patching and handling - */ -struct nat_sccp_connection *create_sccp_src_ref(struct bsc_connection *bsc, struct bsc_nat_parsed *parsed); -int update_sccp_src_ref(struct nat_sccp_connection *sccp, struct bsc_nat_parsed *parsed); -void remove_sccp_src_ref(struct bsc_connection *bsc, struct msgb *msg, struct bsc_nat_parsed *parsed); -struct nat_sccp_connection *patch_sccp_src_ref_to_bsc(struct msgb *, struct bsc_nat_parsed *, struct bsc_nat *); -struct nat_sccp_connection *patch_sccp_src_ref_to_msc(struct msgb *, struct bsc_nat_parsed *, struct bsc_connection *); -struct nat_sccp_connection *bsc_nat_find_con_by_bsc(struct bsc_nat *, struct sccp_source_reference *); - -/** - * MGCP/Audio handling - */ -int bsc_mgcp_nr_multiplexes(int max_endpoints); -int bsc_write_mgcp(struct bsc_connection *bsc, const uint8_t *data, unsigned int length); -int bsc_mgcp_assign_patch(struct nat_sccp_connection *, struct msgb *msg); -void bsc_mgcp_init(struct nat_sccp_connection *); -void bsc_mgcp_dlcx(struct nat_sccp_connection *); -void bsc_mgcp_free_endpoints(struct bsc_nat *nat); -int bsc_mgcp_nat_init(struct bsc_nat *nat); - -struct nat_sccp_connection *bsc_mgcp_find_con(struct bsc_nat *, int endpoint_number); -struct msgb *bsc_mgcp_rewrite(char *input, int length, int endp, const char *ip, - int port, int osmux, int *first_payload_type, int mode_set); -void bsc_mgcp_forward(struct bsc_connection *bsc, struct msgb *msg); - -void bsc_mgcp_clear_endpoints_for(struct bsc_connection *bsc); -int bsc_mgcp_parse_response(const char *str, int *code, char transaction[60]); -uint32_t bsc_mgcp_extract_ci(const char *resp); - - -int bsc_write(struct bsc_connection *bsc, struct msgb *msg, int id); -int bsc_do_write(struct osmo_wqueue *queue, struct msgb *msg, int id); -int bsc_write_msg(struct osmo_wqueue *queue, struct msgb *msg); -int bsc_write_cb(struct osmo_fd *bfd, struct msgb *msg); - -int bsc_nat_msc_is_connected(struct bsc_nat *nat); - -int bsc_conn_type_to_ctr(struct nat_sccp_connection *conn); - -struct gsm48_hdr *bsc_unpack_dtap(struct bsc_nat_parsed *parsed, struct msgb *msg, uint32_t *len); - -/** USSD filtering */ -int bsc_ussd_init(struct bsc_nat *nat); -int bsc_ussd_check(struct nat_sccp_connection *con, struct bsc_nat_parsed *parsed, struct msgb *msg); -int bsc_ussd_close_connections(struct bsc_nat *nat); - -struct msgb *bsc_nat_rewrite_msg(struct bsc_nat *nat, struct msgb *msg, struct bsc_nat_parsed *, const char *imsi); - -/** paging group handling */ -struct bsc_nat_paging_group *bsc_nat_paging_group_num(struct bsc_nat *nat, int group); -struct bsc_nat_paging_group *bsc_nat_paging_group_create(struct bsc_nat *nat, int group); -void bsc_nat_paging_group_delete(struct bsc_nat_paging_group *); -void bsc_nat_paging_group_add_lac(struct bsc_nat_paging_group *grp, int lac); -void bsc_nat_paging_group_del_lac(struct bsc_nat_paging_group *grp, int lac); - -/** - * Number rewriting support below - */ -struct bsc_nat_num_rewr_entry { - struct llist_head list; - - regex_t msisdn_reg; - regex_t num_reg; - - char *replace; - uint8_t is_prefix_lookup; -}; - -void bsc_nat_num_rewr_entry_adapt(void *ctx, struct llist_head *head, const struct osmo_config_list *); - -void bsc_nat_send_mgcp_to_msc(struct bsc_nat *bsc_nat, struct msgb *msg); -void bsc_nat_handle_mgcp(struct bsc_nat *bsc, struct msgb *msg); - -struct ctrl_handle *bsc_nat_controlif_setup(struct bsc_nat *nat, - const char *bind_addr, int port); -void bsc_nat_ctrl_del_pending(struct bsc_cmd_list *pending); -int bsc_nat_handle_ctrlif_msg(struct bsc_connection *bsc, struct msgb *msg); - -int bsc_nat_extract_lac(struct bsc_connection *bsc, struct nat_sccp_connection *con, - struct bsc_nat_parsed *parsed, struct msgb *msg); - -int bsc_nat_filter_sccp_cr(struct bsc_connection *bsc, struct msgb *msg, - struct bsc_nat_parsed *, int *con_type, char **imsi, - struct bsc_filter_reject_cause *cause); -int bsc_nat_filter_dt(struct bsc_connection *bsc, struct msgb *msg, - struct nat_sccp_connection *con, struct bsc_nat_parsed *parsed, - struct bsc_filter_reject_cause *cause); - -/** - * CTRL interface helper - */ -void bsc_nat_inform_reject(struct bsc_connection *bsc, const char *imsi); - -/* - * Use for testing - */ -void bsc_nat_free(struct bsc_nat *nat); - -#endif diff --git a/openbsc/include/openbsc/bsc_nat_callstats.h b/openbsc/include/openbsc/bsc_nat_callstats.h deleted file mode 100644 index 64f9bfc0..00000000 --- a/openbsc/include/openbsc/bsc_nat_callstats.h +++ /dev/null @@ -1,55 +0,0 @@ -/* - * (C) 2010-2012 by Holger Hans Peter Freyther - * (C) 2010-2012 by On-Waves - * All Rights Reserved - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation; either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - * - */ - -#ifndef BSC_NAT_CALLSTATS_H -#define BSC_NAT_CALLSTATS_H - -#include - -#include - -struct bsc_nat_call_stats { - struct llist_head entry; - - struct sccp_source_reference remote_ref; - struct sccp_source_reference src_ref; /* as seen by the MSC */ - - /* mgcp options */ - uint32_t ci; - int bts_rtp_port; - int net_rtp_port; - struct in_addr bts_addr; - struct in_addr net_addr; - - - /* as witnessed by the NAT */ - uint32_t net_ps; - uint32_t net_os; - uint32_t bts_pr; - uint32_t bts_or; - uint32_t bts_expected; - uint32_t bts_jitter; - int bts_loss; - - uint32_t trans_id; - int msc_endpoint; -}; - -#endif diff --git a/openbsc/include/openbsc/bsc_nat_sccp.h b/openbsc/include/openbsc/bsc_nat_sccp.h deleted file mode 100644 index 08246640..00000000 --- a/openbsc/include/openbsc/bsc_nat_sccp.h +++ /dev/null @@ -1,105 +0,0 @@ -/* NAT utilities using SCCP types */ -/* - * (C) 2010 by Holger Hans Peter Freyther - * (C) 2010 by On-Waves - * All Rights Reserved - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation; either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - * - */ - -#ifndef BSC_NAT_SCCP_H -#define BSC_NAT_SCCP_H - -#include "bsc_msg_filter.h" - -#include - -/* - * For the NAT we will need to analyze and later patch - * the received message. This would require us to parse - * the IPA and SCCP header twice. Instead of doing this - * we will have one analyze structure and have the patching - * and filter operate on the same structure. - */ -struct bsc_nat_parsed { - /* ip access prototype */ - int ipa_proto; - - /* source local reference */ - struct sccp_source_reference *src_local_ref; - - /* destination local reference */ - struct sccp_source_reference *dest_local_ref; - - /* original value */ - struct sccp_source_reference original_dest_ref; - - /* called ssn number */ - int called_ssn; - - /* calling ssn number */ - int calling_ssn; - - /* sccp message type */ - int sccp_type; - - /* bssap type, e.g. 0 for BSS Management */ - int bssap; - - /* the gsm0808 message type */ - int gsm_type; -}; - -/* - * Per SCCP source local reference patch table. It needs to - * be updated on new SCCP connections, connection confirm and reject, - * and on the loss of the BSC connection. - */ -struct nat_sccp_connection { - struct llist_head list_entry; - - struct bsc_connection *bsc; - struct bsc_msc_connection *msc_con; - - struct sccp_source_reference real_ref; - struct sccp_source_reference patched_ref; - struct sccp_source_reference remote_ref; - int has_remote_ref; - - /* status */ - int con_local; - int authorized; - - struct bsc_filter_state filter_state; - - uint16_t lac; - uint16_t ci; - - /* remember which Transactions we run over the bypass */ - char ussd_ti[8]; - - /* - * audio handling. Remember if we have ever send a CRCX, - * remember the endpoint used by the MSC and BSC. - */ - int msc_endp; - int bsc_endp; - - /* timeout handling */ - struct timespec creation_time; -}; - - -#endif diff --git a/openbsc/include/openbsc/bsc_rll.h b/openbsc/include/openbsc/bsc_rll.h deleted file mode 100644 index 729ba603..00000000 --- a/openbsc/include/openbsc/bsc_rll.h +++ /dev/null @@ -1,19 +0,0 @@ -#ifndef _BSC_RLL_H -#define _BSC_RLL_H - -#include - -enum bsc_rllr_ind { - BSC_RLLR_IND_EST_CONF, - BSC_RLLR_IND_REL_IND, - BSC_RLLR_IND_ERR_IND, - BSC_RLLR_IND_TIMEOUT, -}; - -int rll_establish(struct gsm_lchan *lchan, uint8_t link_id, - void (*cb)(struct gsm_lchan *, uint8_t, void *, - enum bsc_rllr_ind), - void *data); -void rll_indication(struct gsm_lchan *lchan, uint8_t link_id, uint8_t type); - -#endif /* _BSC_RLL_H */ diff --git a/openbsc/include/openbsc/bsc_subscriber.h b/openbsc/include/openbsc/bsc_subscriber.h deleted file mode 100644 index 324734f9..00000000 --- a/openbsc/include/openbsc/bsc_subscriber.h +++ /dev/null @@ -1,43 +0,0 @@ -/* GSM subscriber details for use in BSC land */ - -#pragma once - -#include - -#include -#include - -struct log_target; - -struct bsc_subscr { - struct llist_head entry; - int use_count; - - char imsi[GSM23003_IMSI_MAX_DIGITS+1]; - uint32_t tmsi; - uint16_t lac; -}; - -const char *bsc_subscr_name(struct bsc_subscr *bsub); - -struct bsc_subscr *bsc_subscr_find_or_create_by_imsi(struct llist_head *list, - const char *imsi); -struct bsc_subscr *bsc_subscr_find_or_create_by_tmsi(struct llist_head *list, - uint32_t tmsi); - -struct bsc_subscr *bsc_subscr_find_by_imsi(struct llist_head *list, - const char *imsi); -struct bsc_subscr *bsc_subscr_find_by_tmsi(struct llist_head *list, - uint32_t tmsi); - -void bsc_subscr_set_imsi(struct bsc_subscr *bsub, const char *imsi); - -struct bsc_subscr *_bsc_subscr_get(struct bsc_subscr *bsub, - const char *file, int line); -struct bsc_subscr *_bsc_subscr_put(struct bsc_subscr *bsub, - const char *file, int line); -#define bsc_subscr_get(bsub) _bsc_subscr_get(bsub, __BASE_FILE__, __LINE__) -#define bsc_subscr_put(bsub) _bsc_subscr_put(bsub, __BASE_FILE__, __LINE__) - -void log_set_filter_bsc_subscr(struct log_target *target, - struct bsc_subscr *bsub); diff --git a/openbsc/include/openbsc/bss.h b/openbsc/include/openbsc/bss.h deleted file mode 100644 index 9f16bf7d..00000000 --- a/openbsc/include/openbsc/bss.h +++ /dev/null @@ -1,20 +0,0 @@ -#ifndef _BSS_H_ -#define _BSS_H_ - -#include - -struct msgb; - -/* start and stop network */ -extern int bsc_network_alloc(mncc_recv_cb_t mncc_recv); -extern int bsc_network_configure(const char *cfg_file); -extern int bsc_shutdown_net(struct gsm_network *net); - -/* register all supported BTS */ -extern int bts_init(void); -extern int bts_model_bs11_init(void); -extern int bts_model_rbs2k_init(void); -extern int bts_model_nanobts_init(void); -extern int bts_model_nokia_site_init(void); -extern int bts_model_sysmobts_init(void); -#endif diff --git a/openbsc/include/openbsc/bts_ipaccess_nanobts_omlattr.h b/openbsc/include/openbsc/bts_ipaccess_nanobts_omlattr.h deleted file mode 100644 index bc7860b2..00000000 --- a/openbsc/include/openbsc/bts_ipaccess_nanobts_omlattr.h +++ /dev/null @@ -1,32 +0,0 @@ -/* OML attribute table generator for ipaccess nanobts */ - -/* (C) 2016 by sysmocom s.f.m.c. GmbH - * All Rights Reserved - * - * Author: Philipp Maier - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation; either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - */ - -#pragma once - -#include -#include - -struct msgb *nanobts_attr_bts_get(struct gsm_bts *bts); -struct msgb *nanobts_attr_nse_get(struct gsm_bts *bts); -struct msgb *nanobts_attr_cell_get(struct gsm_bts *bts); -struct msgb *nanobts_attr_nscv_get(struct gsm_bts *bts); -struct msgb *nanobts_attr_radio_get(struct gsm_bts *bts, - struct gsm_bts_trx *trx); diff --git a/openbsc/include/openbsc/chan_alloc.h b/openbsc/include/openbsc/chan_alloc.h deleted file mode 100644 index 78242e5b..00000000 --- a/openbsc/include/openbsc/chan_alloc.h +++ /dev/null @@ -1,54 +0,0 @@ -/* Management functions to allocate/release struct gsm_lchan */ -/* (C) 2008 by Harald Welte - * (C) 2009 by Holger Hans Peter Freyther - * All Rights Reserved - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation; either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - * - */ -#ifndef _CHAN_ALLOC_H -#define _CHAN_ALLOC_H - -#include "gsm_data.h" - -struct gsm_subscriber_connection; - -/* Find an allocated channel for a specified subscriber */ -struct gsm_subscriber_connection *connection_for_subscr(struct gsm_subscriber *subscr); - -/* Allocate a logical channel (SDCCH, TCH, ...) */ -struct gsm_lchan *lchan_alloc(struct gsm_bts *bts, enum gsm_chan_t type, int allow_bigger); - -/* Free a logical channel (SDCCH, TCH, ...) */ -void lchan_free(struct gsm_lchan *lchan); -void lchan_reset(struct gsm_lchan *lchan); - -/* Release the given lchan */ -int lchan_release(struct gsm_lchan *lchan, int sacch_deact, enum rsl_rel_mode release_mode); - -struct load_counter { - unsigned int total; - unsigned int used; -}; - -struct pchan_load { - struct load_counter pchan[_GSM_PCHAN_MAX]; -}; - -void bts_chan_load(struct pchan_load *cl, const struct gsm_bts *bts); -void network_chan_load(struct pchan_load *pl, struct gsm_network *net); - -int trx_is_usable(struct gsm_bts_trx *trx); - -#endif /* _CHAN_ALLOC_H */ diff --git a/openbsc/include/openbsc/common_bsc.h b/openbsc/include/openbsc/common_bsc.h deleted file mode 100644 index 79603832..00000000 --- a/openbsc/include/openbsc/common_bsc.h +++ /dev/null @@ -1,9 +0,0 @@ -#pragma once - -#include -#include - -struct gsm_network *bsc_network_init(void *ctx, - uint16_t country_code, - uint16_t network_code, - mncc_recv_cb_t mncc_recv); diff --git a/openbsc/include/openbsc/common_cs.h b/openbsc/include/openbsc/common_cs.h deleted file mode 100644 index 6dc956f8..00000000 --- a/openbsc/include/openbsc/common_cs.h +++ /dev/null @@ -1,27 +0,0 @@ -#pragma once - -#include - -struct msgb; -struct gsm_network; - -typedef int (*mncc_recv_cb_t)(struct gsm_network *, struct msgb *); - -struct vty; - -#define MAX_A5_KEY_LEN (128/8) - -struct gsm_encr { - uint8_t alg_id; - uint8_t key_len; - uint8_t key[MAX_A5_KEY_LEN]; -}; - -struct gsm_network *gsm_network_init(void *ctx, - uint16_t country_code, - uint16_t network_code, - mncc_recv_cb_t mncc_recv); - -int common_cs_vty_init(struct gsm_network *network, - int (* config_write_net )(struct vty *)); -struct gsm_network *gsmnet_from_vty(struct vty *v); diff --git a/openbsc/include/openbsc/crc24.h b/openbsc/include/openbsc/crc24.h deleted file mode 100644 index 756638c0..00000000 --- a/openbsc/include/openbsc/crc24.h +++ /dev/null @@ -1,10 +0,0 @@ -#ifndef _CRC24_H -#define _CRC24_H - -#include - -#define INIT_CRC24 0xffffff - -uint32_t crc24_calc(uint32_t fcs, uint8_t *cp, unsigned int len); - -#endif diff --git a/openbsc/include/openbsc/ctrl.h b/openbsc/include/openbsc/ctrl.h deleted file mode 100644 index c5ac2109..00000000 --- a/openbsc/include/openbsc/ctrl.h +++ /dev/null @@ -1,4 +0,0 @@ -#pragma once - -struct ctrl_handle *bsc_controlif_setup(struct gsm_network *net, - const char *bind_addr, uint16_t port); diff --git a/openbsc/include/openbsc/db.h b/openbsc/include/openbsc/db.h deleted file mode 100644 index bb90705a..00000000 --- a/openbsc/include/openbsc/db.h +++ /dev/null @@ -1,86 +0,0 @@ -/* (C) 2008 by Jan Luebbe - * (C) 2009 by Holger Hans Peter Freyther - * All Rights Reserved - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation; either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - * - */ - -#ifndef _DB_H -#define _DB_H - -#include - -#include "gsm_subscriber.h" - -struct gsm_equipment; -struct gsm_network; -struct gsm_auth_info; -struct gsm_auth_tuple; -struct gsm_sms; -struct gsm_subscriber; - -/* one time initialisation */ -int db_init(const char *name); -int db_prepare(void); -int db_fini(void); - -/* subscriber management */ -struct gsm_subscriber *db_create_subscriber(const char *imsi, uint64_t smin, - uint64_t smax, bool alloc_exten); -struct gsm_subscriber *db_get_subscriber(enum gsm_subscriber_field field, - const char *subscr); -int db_sync_subscriber(struct gsm_subscriber *subscriber); -int db_subscriber_expire(void *priv, void (*callback)(void *priv, long long unsigned int id)); -int db_subscriber_alloc_tmsi(struct gsm_subscriber *subscriber); -int db_subscriber_alloc_exten(struct gsm_subscriber *subscriber, uint64_t smin, - uint64_t smax); -int db_subscriber_alloc_token(struct gsm_subscriber *subscriber, uint32_t* token); -int db_subscriber_assoc_imei(struct gsm_subscriber *subscriber, char *imei); -int db_subscriber_delete(struct gsm_subscriber *subscriber); -int db_sync_equipment(struct gsm_equipment *equip); -int db_subscriber_update(struct gsm_subscriber *subscriber); -int db_subscriber_list_active(void (*list_cb)(struct gsm_subscriber*,void*), void*); - -/* auth info */ -int db_get_authinfo_for_subscr(struct gsm_auth_info *ainfo, - struct gsm_subscriber *subscr); -int db_sync_authinfo_for_subscr(struct gsm_auth_info *ainfo, - struct gsm_subscriber *subscr); -int db_get_lastauthtuple_for_subscr(struct gsm_auth_tuple *atuple, - struct gsm_subscriber *subscr); -int db_sync_lastauthtuple_for_subscr(struct gsm_auth_tuple *atuple, - struct gsm_subscriber *subscr); - -/* SMS store-and-forward */ -int db_sms_store(struct gsm_sms *sms); -struct gsm_sms *db_sms_get(struct gsm_network *net, unsigned long long id); -struct gsm_sms *db_sms_get_unsent(struct gsm_network *net, unsigned long long min_id); -struct gsm_sms *db_sms_get_unsent_by_subscr(struct gsm_network *net, unsigned long long min_subscr_id, unsigned int failed); -struct gsm_sms *db_sms_get_unsent_for_subscr(struct gsm_subscriber *subscr); -int db_sms_mark_delivered(struct gsm_sms *sms); -int db_sms_inc_deliver_attempts(struct gsm_sms *sms); - -/* APDU blob storage */ -int db_apdu_blob_store(struct gsm_subscriber *subscr, - uint8_t apdu_id_flags, uint8_t len, - uint8_t *apdu); - -/* Statistics counter storage */ -struct osmo_counter; -int db_store_counter(struct osmo_counter *ctr); -struct rate_ctr_group; -int db_store_rate_ctr_group(struct rate_ctr_group *ctrg); - -#endif /* _DB_H */ diff --git a/openbsc/include/openbsc/debug.h b/openbsc/include/openbsc/debug.h deleted file mode 100644 index 8a4247b6..00000000 --- a/openbsc/include/openbsc/debug.h +++ /dev/null @@ -1,49 +0,0 @@ -#pragma once - -#include -#include - -#define DEBUG -#include - -/* Debug Areas of the code */ -enum { - DRLL, - DCC, - DMM, - DRR, - DRSL, - DNM, - DMNCC, - DPAG, - DMEAS, - DSCCP, - DMSC, - DMGCP, - DHO, - DDB, - DREF, - DGPRS, - DNS, - DBSSGP, - DLLC, - DSNDCP, - DSLHC, - DNAT, - DCTRL, - DSMPP, - DFILTER, - DGTPHUB, - DRANAP, - DSUA, - DV42BIS, - DPCU, - Debug_LastEntry, -}; - -struct gsm_subscriber; - -void log_set_filter_vlr_subscr(struct log_target *target, - struct gsm_subscriber *vlr_subscr); - -extern const struct log_info log_info; diff --git a/openbsc/include/openbsc/e1_config.h b/openbsc/include/openbsc/e1_config.h deleted file mode 100644 index 538c0b09..00000000 --- a/openbsc/include/openbsc/e1_config.h +++ /dev/null @@ -1,11 +0,0 @@ -#ifndef _E1_CONFIG_H -#define _E1_CONFIG_H - -#include - -int e1_reconfig_ts(struct gsm_bts_trx_ts *ts); -int e1_reconfig_trx(struct gsm_bts_trx *trx); -int e1_reconfig_bts(struct gsm_bts *bts); - -#endif /* _E1_CONFIG_H */ - diff --git a/openbsc/include/openbsc/gb_proxy.h b/openbsc/include/openbsc/gb_proxy.h deleted file mode 100644 index e10894fc..00000000 --- a/openbsc/include/openbsc/gb_proxy.h +++ /dev/null @@ -1,288 +0,0 @@ -#ifndef _GB_PROXY_H -#define _GB_PROXY_H - - -#include - -#include -#include - -#include -#include - -#define GBPROXY_INIT_VU_GEN_TX 256 - -struct rate_ctr_group; -struct gprs_gb_parse_context; -struct tlv_parsed; - -enum gbproxy_global_ctr { - GBPROX_GLOB_CTR_INV_BVCI, - GBPROX_GLOB_CTR_INV_LAI, - GBPROX_GLOB_CTR_INV_RAI, - GBPROX_GLOB_CTR_INV_NSEI, - GBPROX_GLOB_CTR_PROTO_ERR_BSS, - GBPROX_GLOB_CTR_PROTO_ERR_SGSN, - GBPROX_GLOB_CTR_NOT_SUPPORTED_BSS, - GBPROX_GLOB_CTR_NOT_SUPPORTED_SGSN, - GBPROX_GLOB_CTR_RESTART_RESET_SGSN, - GBPROX_GLOB_CTR_TX_ERR_SGSN, - GBPROX_GLOB_CTR_OTHER_ERR, - GBPROX_GLOB_CTR_PATCH_PEER_ERR, -}; - -enum gbproxy_peer_ctr { - GBPROX_PEER_CTR_BLOCKED, - GBPROX_PEER_CTR_UNBLOCKED, - GBPROX_PEER_CTR_DROPPED, - GBPROX_PEER_CTR_INV_NSEI, - GBPROX_PEER_CTR_TX_ERR, - GBPROX_PEER_CTR_RAID_PATCHED_BSS, - GBPROX_PEER_CTR_RAID_PATCHED_SGSN, - GBPROX_PEER_CTR_APN_PATCHED, - GBPROX_PEER_CTR_TLLI_PATCHED_BSS, - GBPROX_PEER_CTR_TLLI_PATCHED_SGSN, - GBPROX_PEER_CTR_PTMSI_PATCHED_BSS, - GBPROX_PEER_CTR_PTMSI_PATCHED_SGSN, - GBPROX_PEER_CTR_PATCH_CRYPT_ERR, - GBPROX_PEER_CTR_PATCH_ERR, - GBPROX_PEER_CTR_ATTACH_REQS, - GBPROX_PEER_CTR_ATTACH_REJS, - GBPROX_PEER_CTR_ATTACH_ACKS, - GBPROX_PEER_CTR_ATTACH_COMPLS, - GBPROX_PEER_CTR_RA_UPD_REQS, - GBPROX_PEER_CTR_RA_UPD_REJS, - GBPROX_PEER_CTR_RA_UPD_ACKS, - GBPROX_PEER_CTR_RA_UPD_COMPLS, - GBPROX_PEER_CTR_GMM_STATUS_BSS, - GBPROX_PEER_CTR_GMM_STATUS_SGSN, - GBPROX_PEER_CTR_DETACH_REQS, - GBPROX_PEER_CTR_DETACH_ACKS, - GBPROX_PEER_CTR_PDP_ACT_REQS, - GBPROX_PEER_CTR_PDP_ACT_REJS, - GBPROX_PEER_CTR_PDP_ACT_ACKS, - GBPROX_PEER_CTR_PDP_DEACT_REQS, - GBPROX_PEER_CTR_PDP_DEACT_ACKS, - GBPROX_PEER_CTR_TLLI_UNKNOWN, - GBPROX_PEER_CTR_TLLI_CACHE_SIZE, - GBPROX_PEER_CTR_LAST, -}; - -enum gbproxy_keep_mode { - GBPROX_KEEP_NEVER, - GBPROX_KEEP_REATTACH, - GBPROX_KEEP_IDENTIFIED, - GBPROX_KEEP_ALWAYS, -}; - -enum gbproxy_match_id { - GBPROX_MATCH_PATCHING, - GBPROX_MATCH_ROUTING, - GBPROX_MATCH_LAST -}; - -struct gbproxy_match { - int enable; - char *re_str; - regex_t re_comp; -}; - -struct gbproxy_config { - /* parsed from config file */ - uint16_t nsip_sgsn_nsei; - - /* misc */ - struct gprs_ns_inst *nsi; - - /* Linked list of all Gb peers (except SGSN) */ - struct llist_head bts_peers; - - /* Counter */ - struct rate_ctr_group *ctrg; - - /* force mcc/mnc */ - int core_mnc; - int core_mcc; - uint8_t* core_apn; - size_t core_apn_size; - int tlli_max_age; - int tlli_max_len; - - /* Experimental config */ - int patch_ptmsi; - int acquire_imsi; - int route_to_sgsn2; - uint16_t nsip_sgsn2_nsei; - enum gbproxy_keep_mode keep_link_infos; - - /* IMSI checking/matching */ - struct gbproxy_match matches[GBPROX_MATCH_LAST]; -}; - -struct gbproxy_patch_state { - int local_mnc; - int local_mcc; - - /* List of TLLIs for which patching is enabled */ - struct llist_head logical_links; - int logical_link_count; -}; - -struct gbproxy_peer { - struct llist_head list; - - /* point back to the config */ - struct gbproxy_config *cfg; - - /* NSEI of the peer entity */ - uint16_t nsei; - - /* BVCI used for Point-to-Point to this peer */ - uint16_t bvci; - int blocked; - - /* Routeing Area that this peer is part of (raw 04.08 encoding) */ - uint8_t ra[6]; - - /* Counter */ - struct rate_ctr_group *ctrg; - - struct gbproxy_patch_state patch_state; -}; - -struct gbproxy_tlli_state { - uint32_t current; - uint32_t assigned; - int bss_validated; - int net_validated; - - uint32_t ptmsi; -}; - -struct gbproxy_link_info { - struct llist_head list; - - struct gbproxy_tlli_state tlli; - struct gbproxy_tlli_state sgsn_tlli; - uint32_t sgsn_nsei; - - time_t timestamp; - uint8_t *imsi; - size_t imsi_len; - - int imsi_acq_pending; - struct llist_head stored_msgs; - unsigned vu_gen_tx_bss; - - int is_deregistered; - - int is_matching[GBPROX_MATCH_LAST]; -}; - - -/* gb_proxy_vty .c */ - -int gbproxy_vty_init(void); -int gbproxy_parse_config(const char *config_file, struct gbproxy_config *cfg); - - -/* gb_proxy.c */ -int gbproxy_init_config(struct gbproxy_config *cfg); - -/* Main input function for Gb proxy */ -int gbprox_rcvmsg(struct gbproxy_config *cfg, struct msgb *msg, uint16_t nsei, uint16_t ns_bvci, uint16_t nsvci); - -int gbprox_signal(unsigned int subsys, unsigned int signal, - void *handler_data, void *signal_data); - -/* Reset all persistent NS-VC's */ -int gbprox_reset_persistent_nsvcs(struct gprs_ns_inst *nsi); - -void gbprox_reset(struct gbproxy_config *cfg); - -/* TLLI info handling */ -void gbproxy_delete_link_infos(struct gbproxy_peer *peer); -struct gbproxy_link_info *gbproxy_update_link_state_ul( - struct gbproxy_peer *peer, time_t now, - struct gprs_gb_parse_context *parse_ctx); -struct gbproxy_link_info *gbproxy_update_link_state_dl( - struct gbproxy_peer *peer, time_t now, - struct gprs_gb_parse_context *parse_ctx); -int gbproxy_update_link_state_after( - struct gbproxy_peer *peer, struct gbproxy_link_info *link_info, - time_t now, struct gprs_gb_parse_context *parse_ctx); -int gbproxy_remove_stale_link_infos(struct gbproxy_peer *peer, time_t now); -void gbproxy_delete_link_info(struct gbproxy_peer *peer, - struct gbproxy_link_info *link_info); -void gbproxy_link_info_discard_messages(struct gbproxy_link_info *link_info); - -void gbproxy_attach_link_info(struct gbproxy_peer *peer, time_t now, - struct gbproxy_link_info *link_info); -void gbproxy_update_link_info(struct gbproxy_link_info *link_info, - const uint8_t *imsi, size_t imsi_len); -void gbproxy_detach_link_info(struct gbproxy_peer *peer, - struct gbproxy_link_info *link_info); -struct gbproxy_link_info *gbproxy_link_info_alloc( struct gbproxy_peer *peer); - -struct gbproxy_link_info *gbproxy_link_info_by_tlli( - struct gbproxy_peer *peer, uint32_t tlli); -struct gbproxy_link_info *gbproxy_link_info_by_imsi( - struct gbproxy_peer *peer, const uint8_t *imsi, size_t imsi_len); -struct gbproxy_link_info *gbproxy_link_info_by_any_sgsn_tlli( - struct gbproxy_peer *peer, uint32_t tlli); -struct gbproxy_link_info *gbproxy_link_info_by_sgsn_tlli( - struct gbproxy_peer *peer, - uint32_t tlli, uint32_t sgsn_nsei); -struct gbproxy_link_info *gbproxy_link_info_by_ptmsi( - struct gbproxy_peer *peer, - uint32_t ptmsi); - -int gbproxy_imsi_matches( - struct gbproxy_config *cfg, - enum gbproxy_match_id match_id, - struct gbproxy_link_info *link_info); -uint32_t gbproxy_map_tlli( - uint32_t other_tlli, struct gbproxy_link_info *link_info, int to_bss); - -/* needed by gb_proxy_tlli.h */ -uint32_t gbproxy_make_bss_ptmsi(struct gbproxy_peer *peer, uint32_t sgsn_ptmsi); -uint32_t gbproxy_make_sgsn_tlli( - struct gbproxy_peer *peer, struct gbproxy_link_info *link_info, - uint32_t bss_tlli); -void gbproxy_reset_link(struct gbproxy_link_info *link_info); -int gbproxy_check_imsi( - struct gbproxy_match *match, const uint8_t *imsi, size_t imsi_len); - -/* Message patching */ -void gbproxy_patch_bssgp( - struct msgb *msg, uint8_t *bssgp, size_t bssgp_len, - struct gbproxy_peer *peer, struct gbproxy_link_info *link_info, - int *len_change, struct gprs_gb_parse_context *parse_ctx); - -int gbproxy_patch_llc( - struct msgb *msg, uint8_t *llc, size_t llc_len, - struct gbproxy_peer *peer, struct gbproxy_link_info *link_info, - int *len_change, struct gprs_gb_parse_context *parse_ctx); - -int gbproxy_set_patch_filter( - struct gbproxy_match *match, const char *filter, const char **err_msg); -void gbproxy_clear_patch_filter(struct gbproxy_match *match); - -/* Peer handling */ -struct gbproxy_peer *gbproxy_peer_by_bvci( - struct gbproxy_config *cfg, uint16_t bvci); -struct gbproxy_peer *gbproxy_peer_by_nsei( - struct gbproxy_config *cfg, uint16_t nsei); -struct gbproxy_peer *gbproxy_peer_by_rai( - struct gbproxy_config *cfg, const uint8_t *ra); -struct gbproxy_peer *gbproxy_peer_by_lai( - struct gbproxy_config *cfg, const uint8_t *la); -struct gbproxy_peer *gbproxy_peer_by_lac( - struct gbproxy_config *cfg, const uint8_t *la); -struct gbproxy_peer *gbproxy_peer_by_bssgp_tlv( - struct gbproxy_config *cfg, struct tlv_parsed *tp); -struct gbproxy_peer *gbproxy_peer_alloc(struct gbproxy_config *cfg, uint16_t bvci); -void gbproxy_peer_free(struct gbproxy_peer *peer); -int gbproxy_cleanup_peers(struct gbproxy_config *cfg, uint16_t nsei, uint16_t bvci); - -#endif diff --git a/openbsc/include/openbsc/gprs_gb_parse.h b/openbsc/include/openbsc/gprs_gb_parse.h deleted file mode 100644 index 24683928..00000000 --- a/openbsc/include/openbsc/gprs_gb_parse.h +++ /dev/null @@ -1,59 +0,0 @@ -#pragma once - -#include - -#include - -struct gprs_gb_parse_context { - /* Pointer to protocol specific parts */ - struct gsm48_hdr *g48_hdr; - struct bssgp_normal_hdr *bgp_hdr; - struct bssgp_ud_hdr *bud_hdr; - uint8_t *bssgp_data; - size_t bssgp_data_len; - uint8_t *llc; - size_t llc_len; - - /* Extracted information */ - struct gprs_llc_hdr_parsed llc_hdr_parsed; - struct tlv_parsed bssgp_tp; - int to_bss; - uint8_t *tlli_enc; - uint8_t *old_tlli_enc; - uint8_t *imsi; - size_t imsi_len; - uint8_t *apn_ie; - size_t apn_ie_len; - uint8_t *ptmsi_enc; - uint8_t *new_ptmsi_enc; - uint8_t *raid_enc; - uint8_t *old_raid_enc; - uint8_t *bssgp_raid_enc; - uint8_t *bssgp_ptmsi_enc; - - /* General info */ - const char *llc_msg_name; - int invalidate_tlli; - int await_reattach; - int need_decryption; - uint32_t tlli; - int pdu_type; - int old_raid_is_foreign; - int peer_nsei; -}; - -int gprs_gb_parse_dtap(uint8_t *data, size_t data_len, - struct gprs_gb_parse_context *parse_ctx); - -int gprs_gb_parse_llc(uint8_t *llc, size_t llc_len, - struct gprs_gb_parse_context *parse_ctx); - -int gprs_gb_parse_bssgp(uint8_t *bssgp, size_t bssgp_len, - struct gprs_gb_parse_context *parse_ctx); - -const char *gprs_gb_message_name(const struct gprs_gb_parse_context *parse_ctx, - const char *default_msg_name); - -void gprs_gb_log_parse_context(int log_level, - struct gprs_gb_parse_context *parse_ctx, - const char *default_msg_name); diff --git a/openbsc/include/openbsc/gprs_gmm.h b/openbsc/include/openbsc/gprs_gmm.h deleted file mode 100644 index d210a354..00000000 --- a/openbsc/include/openbsc/gprs_gmm.h +++ /dev/null @@ -1,35 +0,0 @@ -#ifndef _GPRS_GMM_H -#define _GPRS_GMM_H - -#include -#include - -#include - -int gsm48_tx_gsm_deact_pdp_req(struct sgsn_pdp_ctx *pdp, uint8_t sm_cause); -int gsm48_tx_gsm_act_pdp_rej(struct sgsn_mm_ctx *mm, uint8_t tid, - uint8_t cause, uint8_t pco_len, uint8_t *pco_v); -int gsm48_tx_gsm_act_pdp_acc(struct sgsn_pdp_ctx *pdp); -int gsm48_tx_gsm_deact_pdp_acc(struct sgsn_pdp_ctx *pdp); - -int gsm0408_gprs_rcvmsg_gb(struct msgb *msg, struct gprs_llc_llme *llme, - bool drop_cipherable); -int gsm0408_gprs_rcvmsg_iu(struct msgb *msg, struct gprs_ra_id *ra_id, - uint16_t *sai); -int gsm0408_gprs_force_reattach(struct sgsn_mm_ctx *mmctx); -int gsm0408_gprs_force_reattach_oldmsg(struct msgb *msg, - struct gprs_llc_llme *llme); -void gsm0408_gprs_access_granted(struct sgsn_mm_ctx *mmctx); -void gsm0408_gprs_access_denied(struct sgsn_mm_ctx *mmctx, int gmm_cause); -void gsm0408_gprs_access_cancelled(struct sgsn_mm_ctx *mmctx, int gmm_cause); -void gsm0408_gprs_authenticate(struct sgsn_mm_ctx *mmctx); - -int gprs_gmm_rx_suspend(struct gprs_ra_id *raid, uint32_t tlli); -int gprs_gmm_rx_resume(struct gprs_ra_id *raid, uint32_t tlli, - uint8_t suspend_ref); - -time_t gprs_max_time_to_idle(void); - -int iu_rab_act_ps(uint8_t rab_id, struct sgsn_pdp_ctx *pdp, bool use_x213_nsap); - -#endif /* _GPRS_GMM_H */ diff --git a/openbsc/include/openbsc/gprs_llc.h b/openbsc/include/openbsc/gprs_llc.h deleted file mode 100644 index 8bc22678..00000000 --- a/openbsc/include/openbsc/gprs_llc.h +++ /dev/null @@ -1,284 +0,0 @@ -#ifndef _GPRS_LLC_H -#define _GPRS_LLC_H - -#include -#include -#include -#include - -/* Section 4.7 LLC Layer Structure */ -enum gprs_llc_sapi { - GPRS_SAPI_GMM = 1, - GPRS_SAPI_TOM2 = 2, - GPRS_SAPI_SNDCP3 = 3, - GPRS_SAPI_SNDCP5 = 5, - GPRS_SAPI_SMS = 7, - GPRS_SAPI_TOM8 = 8, - GPRS_SAPI_SNDCP9 = 9, - GPRS_SAPI_SNDCP11 = 11, -}; - -/* Section 6.4 Commands and Responses */ -enum gprs_llc_u_cmd { - GPRS_LLC_U_DM_RESP = 0x01, - GPRS_LLC_U_DISC_CMD = 0x04, - GPRS_LLC_U_UA_RESP = 0x06, - GPRS_LLC_U_SABM_CMD = 0x07, - GPRS_LLC_U_FRMR_RESP = 0x08, - GPRS_LLC_U_XID = 0x0b, - GPRS_LLC_U_NULL_CMD = 0x00, -}; - -/* Section 6.4.1.6 / Table 6 */ -enum gprs_llc_xid_type { - GPRS_LLC_XID_T_VERSION = 0, - GPRS_LLC_XID_T_IOV_UI = 1, - GPRS_LLC_XID_T_IOV_I = 2, - GPRS_LLC_XID_T_T200 = 3, - GPRS_LLC_XID_T_N200 = 4, - GPRS_LLC_XID_T_N201_U = 5, - GPRS_LLC_XID_T_N201_I = 6, - GPRS_LLC_XID_T_mD = 7, - GPRS_LLC_XID_T_mU = 8, - GPRS_LLC_XID_T_kD = 9, - GPRS_LLC_XID_T_kU = 10, - GPRS_LLC_XID_T_L3_PAR = 11, - GPRS_LLC_XID_T_RESET = 12, -}; - -extern const struct value_string gprs_llc_xid_type_names[]; - -/* TS 04.64 Section 7.1.2 Table 7: LLC layer primitives (GMM/SNDCP/SMS/TOM) */ -/* TS 04.65 Section 5.1.2 Table 2: Service primitives used by SNDCP */ -enum gprs_llc_primitive { - /* GMM <-> LLME */ - LLGMM_ASSIGN_REQ, /* GMM tells us new TLLI: TLLI old, TLLI new, Kc, CiphAlg */ - LLGMM_RESET_REQ, /* GMM tells us to perform XID negotiation: TLLI */ - LLGMM_RESET_CNF, /* LLC informs GMM that XID has completed: TLLI */ - LLGMM_SUSPEND_REQ, /* GMM tells us MS has suspended: TLLI, Page */ - LLGMM_RESUME_REQ, /* GMM tells us MS has resumed: TLLI */ - LLGMM_PAGE_IND, /* LLC asks GMM to page MS: TLLI */ - LLGMM_IOV_REQ, /* GMM tells us to perform XID: TLLI */ - LLGMM_STATUS_IND, /* LLC informs GMM about error: TLLI, Cause */ - /* LLE <-> (GMM/SNDCP/SMS/TOM) */ - LL_RESET_IND, /* TLLI */ - LL_ESTABLISH_REQ, /* TLLI, XID Req */ - LL_ESTABLISH_IND, /* TLLI, XID Req, N201-I, N201-U */ - LL_ESTABLISH_RESP, /* TLLI, XID Negotiated */ - LL_ESTABLISH_CONF, /* TLLI, XID Neg, N201-i, N201-U */ - LL_RELEASE_REQ, /* TLLI, Local */ - LL_RELEASE_IND, /* TLLI, Cause */ - LL_RELEASE_CONF, /* TLLI */ - LL_XID_REQ, /* TLLI, XID Requested */ - LL_XID_IND, /* TLLI, XID Req, N201-I, N201-U */ - LL_XID_RESP, /* TLLI, XID Negotiated */ - LL_XID_CONF, /* TLLI, XID Neg, N201-I, N201-U */ - LL_DATA_REQ, /* TLLI, SN-PDU, Ref, QoS, Radio Prio, Ciph */ - LL_DATA_IND, /* TLLI, SN-PDU */ - LL_DATA_CONF, /* TLLI, Ref */ - LL_UNITDATA_REQ, /* TLLI, SN-PDU, Ref, QoS, Radio Prio, Ciph */ - LL_UNITDATA_IND, /* TLLI, SN-PDU */ - LL_STATUS_IND, /* TLLI, Cause */ -}; - -/* Section 4.5.2 Logical Link States + Annex C.2 */ -enum gprs_llc_lle_state { - GPRS_LLES_UNASSIGNED = 1, /* No TLLI yet */ - GPRS_LLES_ASSIGNED_ADM = 2, /* TLLI assigned */ - GPRS_LLES_LOCAL_EST = 3, /* Local Establishment */ - GPRS_LLES_REMOTE_EST = 4, /* Remote Establishment */ - GPRS_LLES_ABM = 5, - GPRS_LLES_LOCAL_REL = 6, /* Local Release */ - GPRS_LLES_TIMER_REC = 7, /* Timer Recovery */ -}; - -enum gprs_llc_llme_state { - GPRS_LLMS_UNASSIGNED = 1, /* No TLLI yet */ - GPRS_LLMS_ASSIGNED = 2, /* TLLI assigned */ -}; - -/* Section 8.9.9 LLC layer parameter default values */ -struct gprs_llc_params { - uint16_t iov_i_exp; - uint16_t t200_201; - uint16_t n200; - uint16_t n201_u; - uint16_t n201_i; - uint16_t mD; - uint16_t mU; - uint16_t kD; - uint16_t kU; -}; - -/* Section 4.7.1: Logical Link Entity: One per DLCI (TLLI + SAPI) */ -struct gprs_llc_lle { - struct llist_head list; - - uint32_t sapi; - - struct gprs_llc_llme *llme; - - enum gprs_llc_lle_state state; - - struct osmo_timer_list t200; - struct osmo_timer_list t201; /* wait for acknowledgement */ - - uint16_t v_sent; - uint16_t v_ack; - uint16_t v_recv; - - uint16_t vu_send; - uint16_t vu_recv; - - /* non-standard LLC state */ - uint16_t vu_recv_last; - uint16_t vu_recv_duplicates; - - /* Overflow Counter for ABM */ - uint32_t oc_i_send; - uint32_t oc_i_recv; - - /* Overflow Counter for unconfirmed transfer */ - uint32_t oc_ui_send; - uint32_t oc_ui_recv; - - unsigned int retrans_ctr; - - struct gprs_llc_params params; -}; - -#define NUM_SAPIS 16 - -struct gprs_llc_llme { - struct llist_head list; - - enum gprs_llc_llme_state state; - - uint32_t tlli; - uint32_t old_tlli; - - /* Crypto parameters */ - enum gprs_ciph_algo algo; - uint8_t kc[16]; - uint8_t cksn; - /* 3GPP TS 44.064 § 8.9.2: */ - uint32_t iov_ui; - - /* over which BSSGP BTS ctx do we need to transmit */ - uint16_t bvci; - uint16_t nsei; - struct gprs_llc_lle lle[NUM_SAPIS]; - - /* Copy of the XID fields we have sent with the last - * network originated XID-Request. Since the phone - * may strip the optional fields in the confirmation - * we need to remeber those fields in order to be - * able to create the compression entity. */ - struct llist_head *xid; - - /* Compression entities */ - struct { - /* In these two list_heads we will store the - * data and protocol compression entities, - * together with their compression states */ - struct llist_head *proto; - struct llist_head *data; - } comp; - - /* Internal management */ - uint32_t age_timestamp; -}; - -#define GPRS_LLME_RESET_AGE (0) - -extern struct llist_head gprs_llc_llmes; - -/* LLC low level types */ - -enum gprs_llc_cmd { - GPRS_LLC_NULL, - GPRS_LLC_RR, - GPRS_LLC_ACK, - GPRS_LLC_RNR, - GPRS_LLC_SACK, - GPRS_LLC_DM, - GPRS_LLC_DISC, - GPRS_LLC_UA, - GPRS_LLC_SABM, - GPRS_LLC_FRMR, - GPRS_LLC_XID, - GPRS_LLC_UI, -}; - -struct gprs_llc_hdr_parsed { - uint8_t sapi; - uint8_t is_cmd:1, - ack_req:1, - is_encrypted:1; - uint32_t seq_rx; - uint32_t seq_tx; - uint32_t fcs; - uint32_t fcs_calc; - uint8_t *data; - uint16_t data_len; - uint16_t crc_length; - enum gprs_llc_cmd cmd; -}; - - -/* BSSGP-UL-UNITDATA.ind */ -int gprs_llc_rcvmsg(struct msgb *msg, struct tlv_parsed *tv); - -/* LL-UNITDATA.req */ -int gprs_llc_tx_ui(struct msgb *msg, uint8_t sapi, int command, - struct sgsn_mm_ctx *mmctx, bool encryptable); - -/* Chapter 7.2.1.2 LLGMM-RESET.req */ -int gprs_llgmm_reset(struct gprs_llc_llme *llme); -int gprs_llgmm_reset_oldmsg(struct msgb* oldmsg, uint8_t sapi, - struct gprs_llc_llme *llme); - -/* Set of LL-XID negotiation (See also: TS 101 351, Section 7.2.2.4) */ -int gprs_ll_xid_req(struct gprs_llc_lle *lle, - struct gprs_llc_xid_field *l3_xid_field); - -/* 04.64 Chapter 7.2.1.1 LLGMM-ASSIGN */ -int gprs_llgmm_assign(struct gprs_llc_llme *llme, - uint32_t old_tlli, uint32_t new_tlli); -int gprs_llgmm_unassign(struct gprs_llc_llme *llme); - -int gprs_llc_init(const char *cipher_plugin_path); -int gprs_llc_vty_init(void); - -/** - * \short Check if N(U) should be considered a retransmit - * - * Implements the range check as of GSM 04.64 8.4.2 - * Receipt of unacknowledged information. - * - * @returns Returns 1 if (V(UR)-32) <= N(U) < V(UR) - * @param nu N(U) unconfirmed sequence number of the UI frame - * @param vur V(UR) unconfirmend received state variable - */ -static inline int gprs_llc_is_retransmit(uint16_t nu, uint16_t vur) -{ - int delta = (vur - nu) & 0x1ff; - return 0 < delta && delta < 32; -} - -/* LLC low level functions */ -void gprs_llme_copy_key(struct sgsn_mm_ctx *mm, struct gprs_llc_llme *llme); - -/* parse a GPRS LLC header, also check for invalid frames */ -int gprs_llc_hdr_parse(struct gprs_llc_hdr_parsed *ghp, - uint8_t *llc_hdr, int len); -void gprs_llc_hdr_dump(struct gprs_llc_hdr_parsed *gph, struct gprs_llc_lle *lle); -int gprs_llc_fcs(uint8_t *data, unsigned int len); - - -/* LLME handling routines */ -struct llist_head *gprs_llme_list(void); -struct gprs_llc_lle *gprs_lle_get_or_create(const uint32_t tlli, uint8_t sapi); - - -#endif diff --git a/openbsc/include/openbsc/gprs_llc_xid.h b/openbsc/include/openbsc/gprs_llc_xid.h deleted file mode 100644 index d340d40b..00000000 --- a/openbsc/include/openbsc/gprs_llc_xid.h +++ /dev/null @@ -1,57 +0,0 @@ -/* GPRS LLC XID field encoding/decoding as per 3GPP TS 44.064 */ - -/* (C) 2016 by sysmocom s.f.m.c. GmbH - * All Rights Reserved - * - * Author: Philipp Maier - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation; either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - */ - -#pragma once - -#include -#include - -/* 3GPP TS 44.064 6.4.1.6 Exchange Identification (XID) - command/response parameter field */ -struct gprs_llc_xid_field { - struct llist_head list; - uint8_t type; /* See also Table 6: LLC layer parameter - negotiation */ - uint8_t *data; /* Payload data (memory is owned by the - * creator of the struct) */ - unsigned int data_len; /* Payload length */ -}; - -/* Transform a list with XID fields into a XID message (dst) */ -int gprs_llc_compile_xid(uint8_t *dst, int dst_maxlen, - const struct llist_head *xid_fields); - -/* Transform a XID message (dst) into a list of XID fields */ -struct llist_head *gprs_llc_parse_xid(const void *ctx, const uint8_t *src, - int src_len); - -/* Create a duplicate of an XID-Field */ -struct gprs_llc_xid_field *gprs_llc_dup_xid_field(const void *ctx, - const struct gprs_llc_xid_field *xid_field); - -/* Copy an llist with xid fields */ -struct llist_head *gprs_llc_copy_xid(const void *ctx, - const struct llist_head *xid_fields); - -/* Dump a list with XID fields (Debug) */ -void gprs_llc_dump_xid_fields(const struct llist_head *xid_fields, - unsigned int logl); - diff --git a/openbsc/include/openbsc/gprs_sgsn.h b/openbsc/include/openbsc/gprs_sgsn.h deleted file mode 100644 index fd86174b..00000000 --- a/openbsc/include/openbsc/gprs_sgsn.h +++ /dev/null @@ -1,473 +0,0 @@ -#ifndef _GPRS_SGSN_H -#define _GPRS_SGSN_H - -#include -#include - -#include - -#include - -#include -#include - -#include - -#define GSM_EXTENSION_LENGTH 15 -#define GSM_APN_LENGTH 102 - -struct gprs_llc_lle; -struct ctrl_handle; -struct gprs_subscr; - -enum gsm48_gsm_cause; - -/* TS 04.08 4.1.3.3 GMM mobility management states on the network side */ -enum gprs_gmm_state { - GMM_DEREGISTERED, /* 4.1.3.3.1.1 */ - GMM_COMMON_PROC_INIT, /* 4.1.3.3.1.2 */ - GMM_REGISTERED_NORMAL, /* 4.1.3.3.2.1 */ - GMM_REGISTERED_SUSPENDED, /* 4.1.3.3.2.2 */ - GMM_DEREGISTERED_INIT, /* 4.1.3.3.1.4 */ -}; - -/* TS 23.060 6.1.1 and 6.1.2 Mobility management states A/Gb and Iu mode */ -enum gprs_pmm_state { - PMM_DETACHED, - PMM_CONNECTED, - PMM_IDLE, - MM_IDLE, - MM_READY, - MM_STANDBY, -}; - -enum gprs_mm_ctr { - GMM_CTR_PKTS_SIG_IN, - GMM_CTR_PKTS_SIG_OUT, - GMM_CTR_PKTS_UDATA_IN, - GMM_CTR_PKTS_UDATA_OUT, - GMM_CTR_BYTES_UDATA_IN, - GMM_CTR_BYTES_UDATA_OUT, - GMM_CTR_PDP_CTX_ACT, - GMM_CTR_SUSPEND, - GMM_CTR_PAGING_PS, - GMM_CTR_PAGING_CS, - GMM_CTR_RA_UPDATE, -}; - -enum gprs_pdp_ctx { - PDP_CTR_PKTS_UDATA_IN, - PDP_CTR_PKTS_UDATA_OUT, - PDP_CTR_BYTES_UDATA_IN, - PDP_CTR_BYTES_UDATA_OUT, -}; - -enum gprs_t3350_mode { - GMM_T3350_MODE_NONE, - GMM_T3350_MODE_ATT, - GMM_T3350_MODE_RAU, - GMM_T3350_MODE_PTMSI_REALL, -}; - -/* Authorization/ACL handling */ -enum sgsn_auth_state { - SGSN_AUTH_UNKNOWN, - SGSN_AUTH_AUTHENTICATE, - SGSN_AUTH_UMTS_RESYNC, - SGSN_AUTH_ACCEPTED, - SGSN_AUTH_REJECTED -}; - -#define MS_RADIO_ACCESS_CAPA - -enum sgsn_ggsn_lookup_state { - SGSN_GGSN_2DIGIT, - SGSN_GGSN_3DIGIT, -}; - -struct sgsn_ggsn_lookup { - int state; - - struct sgsn_mm_ctx *mmctx; - - /* APN string */ - char apn_str[GSM_APN_LENGTH]; - - /* the original data */ - struct msgb *orig_msg; - struct tlv_parsed tp; - - /* for dealing with re-transmissions */ - uint8_t nsapi; - uint8_t sapi; - uint8_t ti; -}; - -enum sgsn_ran_type { - /* GPRS/EDGE via Gb */ - MM_CTX_T_GERAN_Gb, - /* UMTS via Iu */ - MM_CTX_T_UTRAN_Iu, - /* GPRS/EDGE via Iu */ - MM_CTX_T_GERAN_Iu, -}; - -struct service_info { - uint8_t type; - uint16_t pdp_status; -}; - -struct ue_conn_ctx; - -/* According to TS 03.60, Table 5: SGSN MM and PDP Contexts */ -/* Extended by 3GPP TS 23.060, Table 6: SGSN MM and PDP Contexts */ -struct sgsn_mm_ctx { - struct llist_head list; - - enum sgsn_ran_type ran_type; - - char imsi[GSM23003_IMSI_MAX_DIGITS+1]; - enum gprs_gmm_state gmm_state; - enum gprs_pmm_state pmm_state; /* Iu: page when in PMM-IDLE mode */ - uint32_t p_tmsi; - uint32_t p_tmsi_old; /* old P-TMSI before new is confirmed */ - uint32_t p_tmsi_sig; - char imei[GSM23003_IMEISV_NUM_DIGITS+1]; - /* Opt: Software Version Numbber / TS 23.195 */ - char msisdn[GSM_EXTENSION_LENGTH]; - struct gprs_ra_id ra; - struct { - uint16_t cell_id; /* Gb only */ - uint32_t cell_id_age; /* Gb only */ - uint8_t radio_prio_sms; - - /* Additional bits not present in the GSM TS */ - uint16_t nsei; - uint16_t bvci; - struct gprs_llc_llme *llme; - uint32_t tlli; - uint32_t tlli_new; - } gb; - struct { - int new_key; - uint16_t sac; /* Iu: Service Area Code */ - uint32_t sac_age; /* Iu: Service Area Code age */ - /* CSG ID */ - /* CSG Membership */ - /* Access Mode */ - /* Seelected CN Operator ID (TS 23.251) */ - /* CSG Subscription Data */ - /* LIPA Allowed */ - /* Voice Support Match Indicator */ - struct ue_conn_ctx *ue_ctx; - struct service_info service; - } iu; - /* VLR number */ - uint32_t new_sgsn_addr; - /* Authentication Triplet */ - struct gsm_auth_tuple auth_triplet; - /* Kc */ - /* Iu: CK, IK, KSI */ - /* CKSN */ - enum gprs_ciph_algo ciph_algo; - /* Auth & Ciphering Request reference from 3GPP TS 24.008 § 10.5.5.19: */ - uint8_t ac_ref_nr_used; - - struct { - uint8_t len; - uint8_t buf[50]; /* GSM 04.08 10.5.5.12a, extended in TS 24.008 */ - } ms_radio_access_capa; - /* Supported Codecs (SRVCC) */ - struct { - uint8_t len; - uint8_t buf[8]; /* GSM 04.08 10.5.5.12, extended in TS 24.008 */ - } ms_network_capa; - /* UE Netowrk Capability (E-UTRAN) */ - uint16_t drx_parms; - /* Active Time value for PSM */ - int mnrg; /* MS reported to HLR? */ - int ngaf; /* MS reported to MSC/VLR? */ - int ppf; /* paging for GPRS + non-GPRS? */ - /* Subscribed Charging Characteristics */ - /* Trace Reference */ - /* Trace Type */ - /* Trigger ID */ - /* OMC Identity */ - /* SMS Parameters */ - int recovery; - /* Access Restriction */ - /* GPRS CSI (CAMEL) */ - /* MG-CSI (CAMEL) */ - /* Subscribed UE-AMBR */ - /* UE-AMBR */ - /* APN Subscribed */ - - struct llist_head pdp_list; - - struct rate_ctr_group *ctrg; - struct osmo_timer_list timer; - unsigned int T; /* Txxxx number */ - unsigned int num_T_exp; /* number of consecutive T expirations */ - - enum gprs_t3350_mode t3350_mode; - uint8_t t3370_id_type; - uint8_t pending_req; /* the request's message type */ - /* TODO: There isn't much semantic difference between t3350_mode - * (refers to the timer) and pending_req (refers to the procedure), - * where mm->T == 3350 => mm->t3350_mode == f(mm->pending_req). Check - * whether one of them can be dropped. */ - - enum sgsn_auth_state auth_state; - int is_authenticated; - - /* the string representation of the current hlr */ - char hlr[GSM_EXTENSION_LENGTH]; - - /* the current GGSN look-up operation */ - struct sgsn_ggsn_lookup *ggsn_lookup; - - struct gprs_subscr *subscr; -}; - -#define LOGMMCTXP(level, mm, fmt, args...) \ - LOGP(DMM, level, "MM(%s/%08x) " fmt, (mm) ? (mm)->imsi : "---", \ - (mm) ? (mm)->p_tmsi : GSM_RESERVED_TMSI, ## args) - -/* look-up a SGSN MM context based on TLLI + RAI */ -struct sgsn_mm_ctx *sgsn_mm_ctx_by_tlli(uint32_t tlli, - const struct gprs_ra_id *raid); -struct sgsn_mm_ctx *sgsn_mm_ctx_by_ptmsi(uint32_t tmsi); -struct sgsn_mm_ctx *sgsn_mm_ctx_by_imsi(const char *imsi); -struct sgsn_mm_ctx *sgsn_mm_ctx_by_ue_ctx(const void *uectx); - -/* look-up by matching TLLI and P-TMSI (think twice before using this) */ -struct sgsn_mm_ctx *sgsn_mm_ctx_by_tlli_and_ptmsi(uint32_t tlli, - const struct gprs_ra_id *raid); - -/* Allocate a new SGSN MM context */ -struct sgsn_mm_ctx *sgsn_mm_ctx_alloc_gb(uint32_t tlli, - const struct gprs_ra_id *raid); -struct sgsn_mm_ctx *sgsn_mm_ctx_alloc_iu(void *uectx); - -void sgsn_mm_ctx_cleanup_free(struct sgsn_mm_ctx *ctx); - -struct sgsn_ggsn_ctx *sgsn_mm_ctx_find_ggsn_ctx(struct sgsn_mm_ctx *mmctx, - struct tlv_parsed *tp, - enum gsm48_gsm_cause *gsm_cause, - char *apn_str); - -enum pdp_ctx_state { - PDP_STATE_NONE, - PDP_STATE_CR_REQ, - PDP_STATE_CR_CONF, - - /* 04.08 / Figure 6.2 / 6.1.2.2 */ - PDP_STATE_INACT_PEND, - PDP_STATE_INACTIVE = PDP_STATE_NONE, -}; - -enum pdp_type { - PDP_TYPE_NONE, - PDP_TYPE_ETSI_PPP, - PDP_TYPE_IANA_IPv4, - PDP_TYPE_IANA_IPv6, -}; - -struct sgsn_pdp_ctx { - struct llist_head list; /* list_head for mmctx->pdp_list */ - struct llist_head g_list; /* list_head for global list */ - struct sgsn_mm_ctx *mm; /* back pointer to MM CTX */ - int destroy_ggsn; /* destroy it on destruction */ - struct sgsn_ggsn_ctx *ggsn; /* which GGSN serves this PDP */ - struct rate_ctr_group *ctrg; - - //unsigned int id; - struct pdp_t *lib; /* pointer to libgtp PDP ctx */ - enum pdp_ctx_state state; - enum pdp_type type; - uint32_t address; - char *apn_subscribed; - //char *apn_used; - uint16_t nsapi; /* SNDCP */ - uint16_t sapi; /* LLC */ - uint8_t ti; /* transaction identifier */ - int vplmn_allowed; - uint32_t qos_profile_subscr; - //uint32_t qos_profile_req; - //uint32_t qos_profile_neg; - uint8_t radio_prio; - //uint32_t charging_id; - - struct osmo_timer_list timer; - unsigned int T; /* Txxxx number */ - unsigned int num_T_exp; /* number of consecutive T expirations */ - - struct osmo_timer_list cdr_timer; /* CDR record wird timer */ - struct timespec cdr_start; /* The start of the CDR */ - uint64_t cdr_bytes_in; - uint64_t cdr_bytes_out; - uint32_t cdr_charging_id; -}; - -#define LOGPDPCTXP(level, pdp, fmt, args...) \ - LOGP(DGPRS, level, "PDP(%s/%u) " \ - fmt, (pdp)->mm ? (pdp)->mm->imsi : "---", (pdp)->ti, ## args) - -/* look up PDP context by MM context and NSAPI */ -struct sgsn_pdp_ctx *sgsn_pdp_ctx_by_nsapi(const struct sgsn_mm_ctx *mm, - uint8_t nsapi); -/* look up PDP context by MM context and transaction ID */ -struct sgsn_pdp_ctx *sgsn_pdp_ctx_by_tid(const struct sgsn_mm_ctx *mm, - uint8_t tid); - -struct sgsn_pdp_ctx *sgsn_pdp_ctx_alloc(struct sgsn_mm_ctx *mm, - uint8_t nsapi); -void sgsn_pdp_ctx_terminate(struct sgsn_pdp_ctx *pdp); -void sgsn_pdp_ctx_free(struct sgsn_pdp_ctx *pdp); - - -struct sgsn_ggsn_ctx { - struct llist_head list; - uint32_t id; - unsigned int gtp_version; - struct in_addr remote_addr; - int remote_restart_ctr; - struct gsn_t *gsn; -}; -struct sgsn_ggsn_ctx *sgsn_ggsn_ctx_alloc(uint32_t id); -void sgsn_ggsn_ctx_free(struct sgsn_ggsn_ctx *ggc); -struct sgsn_ggsn_ctx *sgsn_ggsn_ctx_by_id(uint32_t id); -struct sgsn_ggsn_ctx *sgsn_ggsn_ctx_by_addr(struct in_addr *addr); -struct sgsn_ggsn_ctx *sgsn_ggsn_ctx_find_alloc(uint32_t id); - -struct apn_ctx { - struct llist_head list; - struct sgsn_ggsn_ctx *ggsn; - char *name; - char *imsi_prefix; - char *description; -}; - -struct apn_ctx *sgsn_apn_ctx_find_alloc(const char *name, const char *imsi_prefix); -void sgsn_apn_ctx_free(struct apn_ctx *actx); -struct apn_ctx *sgsn_apn_ctx_by_name(const char *name, const char *imsi_prefix); -struct apn_ctx *sgsn_apn_ctx_match(const char *name, const char *imsi_prefix); - -extern struct llist_head sgsn_mm_ctxts; -extern struct llist_head sgsn_ggsn_ctxts; -extern struct llist_head sgsn_apn_ctxts; -extern struct llist_head sgsn_pdp_ctxts; - -uint32_t sgsn_alloc_ptmsi(void); -void sgsn_inst_init(void); - -/* High-level function to be called in case a GGSN has disappeared or - * ottherwise lost state (recovery procedure) */ -int drop_all_pdp_for_ggsn(struct sgsn_ggsn_ctx *ggsn); - -char *gprs_pdpaddr2str(uint8_t *pdpa, uint8_t len); - -/* - * ctrl interface related work - */ -struct gsm_network; -struct ctrl_handle *sgsn_controlif_setup(struct gsm_network *, - const char *bind_addr, uint16_t port); -int sgsn_ctrl_cmds_install(void); - -/* - * Authorization/ACL handling - */ -struct imsi_acl_entry { - struct llist_head list; - char imsi[16+1]; -}; - -/* see GSM 09.02, 17.7.1, PDP-Context and GPRSSubscriptionData */ -/* see GSM 09.02, B.1, gprsSubscriptionData */ -struct sgsn_subscriber_pdp_data { - struct llist_head list; - - unsigned int context_id; - uint16_t pdp_type; - char apn_str[GSM_APN_LENGTH]; - uint8_t qos_subscribed[20]; - size_t qos_subscribed_len; -}; - -struct sgsn_subscriber_data { - struct sgsn_mm_ctx *mm; - struct gsm_auth_tuple auth_triplets[5]; - int auth_triplets_updated; - struct llist_head pdp_list; - int error_cause; - - uint8_t msisdn[9]; - size_t msisdn_len; - - uint8_t hlr[9]; - size_t hlr_len; -}; - -#define SGSN_ERROR_CAUSE_NONE (-1) - -#define LOGGSUBSCRP(level, subscr, fmt, args...) \ - LOGP(DGPRS, level, "SUBSCR(%s) " fmt, \ - (subscr) ? (subscr)->imsi : "---", \ - ## args) - -struct sgsn_config; -struct sgsn_instance; -extern const struct value_string *sgsn_auth_state_names; - -void sgsn_auth_init(void); -struct imsi_acl_entry *sgsn_acl_lookup(const char *imsi, struct sgsn_config *cfg); -int sgsn_acl_add(const char *imsi, struct sgsn_config *cfg); -int sgsn_acl_del(const char *imsi, struct sgsn_config *cfg); -/* Request authorization */ -int sgsn_auth_request(struct sgsn_mm_ctx *mm); -enum sgsn_auth_state sgsn_auth_state(struct sgsn_mm_ctx *mm); -void sgsn_auth_update(struct sgsn_mm_ctx *mm); -struct gsm_auth_tuple *sgsn_auth_get_tuple(struct sgsn_mm_ctx *mmctx, - unsigned key_seq); - -/* - * GPRS subscriber data - */ -#define GPRS_SUBSCRIBER_FIRST_CONTACT 0x00000001 -#define GPRS_SUBSCRIBER_UPDATE_AUTH_INFO_PENDING (1 << 16) -#define GPRS_SUBSCRIBER_UPDATE_LOCATION_PENDING (1 << 17) -#define GPRS_SUBSCRIBER_CANCELLED (1 << 18) -#define GPRS_SUBSCRIBER_ENABLE_PURGE (1 << 19) - -#define GPRS_SUBSCRIBER_UPDATE_PENDING_MASK ( \ - GPRS_SUBSCRIBER_UPDATE_LOCATION_PENDING | \ - GPRS_SUBSCRIBER_UPDATE_AUTH_INFO_PENDING \ -) - -int gprs_subscr_init(struct sgsn_instance *sgi); -int gprs_subscr_request_update_location(struct sgsn_mm_ctx *mmctx); -int gprs_subscr_request_auth_info(struct sgsn_mm_ctx *mmctx, - const uint8_t *auts, - const uint8_t *auts_rand); -int gprs_subscr_auth_sync(struct gprs_subscr *subscr, - const uint8_t *auts, const uint8_t *auts_rand); -void gprs_subscr_cleanup(struct gprs_subscr *subscr); -struct gprs_subscr *gprs_subscr_get_or_create(const char *imsi); -struct gprs_subscr *gprs_subscr_get_or_create_by_mmctx( struct sgsn_mm_ctx *mmctx); -struct gprs_subscr *gprs_subscr_get_by_imsi(const char *imsi); -void gprs_subscr_cancel(struct gprs_subscr *subscr); -void gprs_subscr_update(struct gprs_subscr *subscr); -void gprs_subscr_update_auth_info(struct gprs_subscr *subscr); -int gprs_subscr_rx_gsup_message(struct msgb *msg); - -/* Called on subscriber data updates */ -void sgsn_update_subscriber_data(struct sgsn_mm_ctx *mmctx); - -int gprs_sndcp_vty_init(void); -struct sgsn_instance; -int sgsn_gtp_init(struct sgsn_instance *sgi); - -void sgsn_rate_ctr_init(); - -#endif /* _GPRS_SGSN_H */ diff --git a/openbsc/include/openbsc/gprs_sndcp.h b/openbsc/include/openbsc/gprs_sndcp.h deleted file mode 100644 index d970240e..00000000 --- a/openbsc/include/openbsc/gprs_sndcp.h +++ /dev/null @@ -1,79 +0,0 @@ -#ifndef _INT_SNDCP_H -#define _INT_SNDCP_H - -#include -#include - -/* A fragment queue header, maintaining list of fragments for one N-PDU */ -struct defrag_state { - /* PDU number for which the defragmentation state applies */ - uint16_t npdu; - /* highest segment number we have received so far */ - uint8_t highest_seg; - /* bitmask of the segments we already have */ - uint32_t seg_have; - /* do we still expect more segments? */ - unsigned int no_more; - /* total length of all segments together */ - unsigned int tot_len; - - /* linked list of defrag_queue_entry: one for each fragment */ - struct llist_head frag_list; - - struct osmo_timer_list timer; - - /* Holds state to know which compression mode is used - * when the packet is re-assembled */ - uint8_t pcomp; - uint8_t dcomp; - - /* Holds the pointers to the compression entity list - * that is used when the re-assembled packet is decompressed */ - struct llist_head *proto; - struct llist_head *data; -}; - -/* See 6.7.1.2 Reassembly */ -enum sndcp_rx_state { - SNDCP_RX_S_FIRST, - SNDCP_RX_S_SUBSEQ, - SNDCP_RX_S_DISCARD, -}; - -struct gprs_sndcp_entity { - struct llist_head list; - - /* FIXME: move this RA_ID up to the LLME or even higher */ - struct gprs_ra_id ra_id; - /* reference to the LLC Entity below this SNDCP entity */ - struct gprs_llc_lle *lle; - /* The NSAPI we shall use on top of LLC */ - uint8_t nsapi; - - /* NPDU number for the GTP->SNDCP side */ - uint16_t tx_npdu_nr; - /* SNDCP eeceiver state */ - enum sndcp_rx_state rx_state; - /* The defragmentation queue */ - struct defrag_state defrag; -}; - -extern struct llist_head gprs_sndcp_entities; - -/* Set of SNDCP-XID negotiation (See also: TS 144 065, - * Section 6.8 XID parameter negotiation) */ -int sndcp_sn_xid_req(struct gprs_llc_lle *lle, uint8_t nsapi); - -/* Process SNDCP-XID indication (See also: TS 144 065, - * Section 6.8 XID parameter negotiation) */ -int sndcp_sn_xid_ind(struct gprs_llc_xid_field *xid_field_indication, - struct gprs_llc_xid_field *xid_field_response, - struct gprs_llc_lle *lle); - -/* Process SNDCP-XID indication - * (See also: TS 144 065, Section 6.8 XID parameter negotiation) */ -int sndcp_sn_xid_conf(struct gprs_llc_xid_field *xid_field_conf, - struct gprs_llc_xid_field *xid_field_request, - struct gprs_llc_lle *lle); - -#endif /* INT_SNDCP_H */ diff --git a/openbsc/include/openbsc/gprs_sndcp_comp.h b/openbsc/include/openbsc/gprs_sndcp_comp.h deleted file mode 100644 index 87ab6382..00000000 --- a/openbsc/include/openbsc/gprs_sndcp_comp.h +++ /dev/null @@ -1,82 +0,0 @@ -/* GPRS SNDCP header compression entity management tools */ - -/* (C) 2016 by sysmocom s.f.m.c. GmbH - * All Rights Reserved - * - * Author: Philipp Maier - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation; either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - */ - -#pragma once - -#include -#include -#include - -/* Header / Data compression entity */ -struct gprs_sndcp_comp { - struct llist_head list; - - /* Serves as an ID in case we want to delete this entity later */ - unsigned int entity; /* see also: 6.5.1.1.3 and 6.6.1.1.3 */ - - /* Specifies to which NSAPIs the compression entity is assigned */ - uint8_t nsapi_len; /* Number of applicable NSAPIs (default 0) */ - uint8_t nsapi[MAX_NSAPI]; /* Applicable NSAPIs (default 0) */ - - /* Assigned pcomp values */ - uint8_t comp_len; /* Number of contained PCOMP / DCOMP values */ - uint8_t comp[MAX_COMP]; /* see also: 6.5.1.1.5 and 6.6.1.1.5 */ - - /* Algorithm parameters */ - int algo; /* Algorithm type (see gprs_sndcp_xid.h) */ - int compclass; /* See gprs_sndcp_xid.h/c */ - void *state; /* Algorithm status and parameters */ -}; - -#define MAX_COMP 16 /* Maximum number of possible pcomp/dcomp values */ -#define MAX_NSAPI 11 /* Maximum number usable NSAPIs */ - -/* Allocate a compression enitiy list */ -struct llist_head *gprs_sndcp_comp_alloc(const void *ctx); - -/* Free a compression entitiy list */ -void gprs_sndcp_comp_free(struct llist_head *comp_entities); - -/* Delete a compression entity */ -void gprs_sndcp_comp_delete(struct llist_head *comp_entities, unsigned int entity); - -/* Create and Add a new compression entity - * (returns a pointer to the compression entity that has just been created) */ -struct gprs_sndcp_comp *gprs_sndcp_comp_add(const void *ctx, - struct llist_head *comp_entities, - const struct gprs_sndcp_comp_field - *comp_field); - -/* Find which compression entity handles the specified pcomp/dcomp */ -struct gprs_sndcp_comp *gprs_sndcp_comp_by_comp(const struct llist_head - *comp_entities, uint8_t comp); - -/* Find which compression entity handles the specified nsapi */ -struct gprs_sndcp_comp *gprs_sndcp_comp_by_nsapi(const struct llist_head - *comp_entities, uint8_t nsapi); - -/* Find a comp_index for a given pcomp/dcomp value */ -uint8_t gprs_sndcp_comp_get_idx(const struct gprs_sndcp_comp *comp_entity, - uint8_t comp); - -/* Find a pcomp/dcomp value for a given comp_index */ -uint8_t gprs_sndcp_comp_get_comp(const struct gprs_sndcp_comp *comp_entity, - uint8_t comp_index); diff --git a/openbsc/include/openbsc/gprs_sndcp_dcomp.h b/openbsc/include/openbsc/gprs_sndcp_dcomp.h deleted file mode 100644 index a76b4a4b..00000000 --- a/openbsc/include/openbsc/gprs_sndcp_dcomp.h +++ /dev/null @@ -1,53 +0,0 @@ -/* GPRS SNDCP data compression handler */ - -/* (C) 2016 by sysmocom s.f.m.c. GmbH - * All Rights Reserved - * - * Author: Philipp Maier - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation; either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - */ - -#pragma once - -#include -#include -#include - -/* Note: The decompressed packet may have a maximum size of: - * Return value * MAX_DATADECOMPR_FAC */ -#define MAX_DATADECOMPR_FAC 10 - -/* Note: In unacknowledged mode (SN_UNITDATA), the comression state is reset - * for every NPDU. The compressor needs a reasonably large payload to operate - * effectively (yield positive compression gain). For packets shorter than 100 - * byte, no positive compression gain can be expected so we will skip the - * compression for short packets. */ -#define MIN_COMPR_PAYLOAD 100 - -/* Initalize data compression */ -int gprs_sndcp_dcomp_init(const void *ctx, struct gprs_sndcp_comp *comp_entity, - const struct gprs_sndcp_comp_field *comp_field); - -/* Terminate data compression */ -void gprs_sndcp_dcomp_term(struct gprs_sndcp_comp *comp_entity); - -/* Expand packet */ -int gprs_sndcp_dcomp_expand(uint8_t *data, unsigned int len, uint8_t pcomp, - const struct llist_head *comp_entities); - -/* Compress packet */ -int gprs_sndcp_dcomp_compress(uint8_t *data, unsigned int len, uint8_t *pcomp, - const struct llist_head *comp_entities, - uint8_t nsapi); diff --git a/openbsc/include/openbsc/gprs_sndcp_pcomp.h b/openbsc/include/openbsc/gprs_sndcp_pcomp.h deleted file mode 100644 index 4e15b9be..00000000 --- a/openbsc/include/openbsc/gprs_sndcp_pcomp.h +++ /dev/null @@ -1,46 +0,0 @@ -/* GPRS SNDCP header compression handler */ - -/* (C) 2016 by sysmocom s.f.m.c. GmbH - * All Rights Reserved - * - * Author: Philipp Maier - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation; either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - */ - -#pragma once - -#include -#include -#include - -/* Note: The decompressed packet may have a maximum size of: - * Return value + MAX_DECOMPR_INCR */ -#define MAX_HDRDECOMPR_INCR 64 - -/* Initalize header compression */ -int gprs_sndcp_pcomp_init(const void *ctx, struct gprs_sndcp_comp *comp_entity, - const struct gprs_sndcp_comp_field *comp_field); - -/* Terminate header compression */ -void gprs_sndcp_pcomp_term(struct gprs_sndcp_comp *comp_entity); - -/* Expand packet header */ -int gprs_sndcp_pcomp_expand(uint8_t *data, unsigned int len, uint8_t pcomp, - const struct llist_head *comp_entities); - -/* Compress packet header */ -int gprs_sndcp_pcomp_compress(uint8_t *data, unsigned int len, uint8_t *pcomp, - const struct llist_head *comp_entities, - uint8_t nsapi); diff --git a/openbsc/include/openbsc/gprs_sndcp_xid.h b/openbsc/include/openbsc/gprs_sndcp_xid.h deleted file mode 100644 index e64bc523..00000000 --- a/openbsc/include/openbsc/gprs_sndcp_xid.h +++ /dev/null @@ -1,218 +0,0 @@ -/* GPRS SNDCP XID field encoding/decoding as per 3GPP TS 44.065 */ - -/* (C) 2016 by sysmocom s.f.m.c. GmbH - * All Rights Reserved - * - * Author: Philipp Maier - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation; either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - */ - -#pragma once - -#include -#include - -#define DEFAULT_SNDCP_VERSION 0 /* See 3GPP TS 44.065, clause 8 */ -#define MAX_ENTITIES 32 /* 3GPP TS 44.065 reserves 5 bit - * for compression enitity number */ - -#define MAX_COMP 16 /* Maximum number of possible pcomp/dcomp values */ -#define MAX_NSAPI 11 /* Maximum number usable NSAPIs */ -#define MAX_ROHC 16 /* Maximum number of ROHC compression profiles */ - -/* According to: 3GPP TS 44.065, 6.5.1.1 Format of the protocol control - * information compression field (Figure 7) and 3GPP TS 44.065, - * 6.6.1.1 Format of the data compression field (Figure 9) */ -struct gprs_sndcp_comp_field { - struct llist_head list; - - /* Propose bit (P), see also: 6.5.1.1.2 and 6.6.1.1.2 */ - unsigned int p; - - /* Entity number, see also: 6.5.1.1.3 and 6.6.1.1.3 */ - unsigned int entity; - - /* Algorithm identifier, see also: 6.5.1.1.4 and 6.6.1.1.4 */ - int algo; - - /* Number of contained PCOMP / DCOMP values */ - uint8_t comp_len; - - /* PCOMP / DCOMP values, see also: 6.5.1.1.5 and 6.6.1.1.5 */ - uint8_t comp[MAX_COMP]; - - /* Note: Only one of the following struct pointers may, - be used. Unused pointers must be set to NULL! */ - struct gprs_sndcp_pcomp_rfc1144_params *rfc1144_params; - struct gprs_sndcp_pcomp_rfc2507_params *rfc2507_params; - struct gprs_sndcp_pcomp_rohc_params *rohc_params; - struct gprs_sndcp_dcomp_v42bis_params *v42bis_params; - struct gprs_sndcp_dcomp_v44_params *v44_params; -}; - -/* According to: 3GPP TS 44.065, 6.5.1.1.4 Algorithm identifier */ -enum gprs_sndcp_hdr_comp_algo { - RFC_1144, /* TCP/IP header compression, see also 6.5.2 */ - RFC_2507, /* TCP/UDP/IP header compression, see also: 6.5.3 */ - ROHC /* Robust Header Compression, see also 6.5.4 */ -}; - -/* According to: 3GPP TS 44.065, 6.5.1.1.4 Algorithm identifier */ -enum gprs_sndcp_data_comp_algo { - V42BIS, /* V.42bis data compression, see also 6.6.2 */ - V44 /* V44 data compression, see also: 6.6.3 */ -}; - -/* According to: 3GPP TS 44.065, 8 SNDCP XID parameters */ -enum gprs_sndcp_xid_param_types { - SNDCP_XID_VERSION_NUMBER, - SNDCP_XID_DATA_COMPRESSION, /* See also: subclause 6.6.1 */ - SNDCP_XID_PROTOCOL_COMPRESSION, /* See also: subclause 6.5.1 */ -}; - -/* According to: 3GPP TS 44.065, 6.5.2.1 Parameters (Table 5) */ -struct gprs_sndcp_pcomp_rfc1144_params { - uint8_t nsapi_len; /* Number of applicable NSAPIs - * (default 0) */ - uint8_t nsapi[MAX_NSAPI]; /* Applicable NSAPIs (default 0) */ - int s01; /* (default 15) */ -}; - -/* According to: 3GPP TS 44.065, 6.5.2.2 Assignment of PCOMP values */ -enum gprs_sndcp_pcomp_rfc1144_pcomp { - RFC1144_PCOMP1, /* Uncompressed TCP */ - RFC1144_PCOMP2, /* Compressed TCP */ - RFC1144_PCOMP_NUM /* Number of pcomp values */ -}; - -/* According to: 3GPP TS 44.065, 6.5.3.1 Parameters (Table 6) */ -struct gprs_sndcp_pcomp_rfc2507_params { - uint8_t nsapi_len; /* Number of applicable NSAPIs - * (default 0) */ - uint8_t nsapi[MAX_NSAPI]; /* Applicable NSAPIs (default 0) */ - int f_max_period; /* (default 256) */ - int f_max_time; /* (default 5) */ - int max_header; /* (default 168) */ - int tcp_space; /* (default 15) */ - int non_tcp_space; /* (default 15) */ -}; - -/* According to: 3GPP TS 44.065, 6.5.3.2 Assignment of PCOMP values for RFC2507 */ -enum gprs_sndcp_pcomp_rfc2507_pcomp { - RFC2507_PCOMP1, /* Full Header */ - RFC2507_PCOMP2, /* Compressed TCP */ - RFC2507_PCOMP3, /* Compressed TCP non delta */ - RFC2507_PCOMP4, /* Compressed non TCP */ - RFC2507_PCOMP5, /* Context state */ - RFC2507_PCOMP_NUM /* Number of pcomp values */ -}; - -/* According to: 3GPP TS 44.065, 6.5.4.1 Parameter (Table 10) */ -struct gprs_sndcp_pcomp_rohc_params { - uint8_t nsapi_len; /* Number of applicable NSAPIs - * (default 0) */ - uint8_t nsapi[MAX_NSAPI]; /* Applicable NSAPIs (default 0) */ - int max_cid; /* (default 15) */ - int max_header; /* (default 168) */ - uint8_t profile_len; /* (default 1) */ - uint16_t profile[MAX_ROHC]; /* (default 0, ROHC uncompressed) */ -}; - -/* According to: 3GPP TS 44.065, 6.5.4.2 Assignment of PCOMP values for ROHC */ -enum gprs_sndcp_pcomp_rohc_pcomp { - ROHC_PCOMP1, /* ROHC small CIDs */ - ROHC_PCOMP2, /* ROHC large CIDs */ - ROHC_PCOMP_NUM /* Number of pcomp values */ -}; - -/* ROHC compression profiles, see also: - http://www.iana.org/assignments/rohc-pro-ids/rohc-pro-ids.xhtml */ -enum gprs_sndcp_xid_rohc_profiles { - ROHC_UNCOMPRESSED = 0x0000, /* ROHC uncompressed [RFC5795] */ - ROHC_RTP = 0x0001, /* ROHC RTP [RFC3095] */ - ROHCV2_RTP = 0x0101, /* ROHCv2 RTP [RFC5225] */ - ROHC_UDP = 0x0002, /* ROHC UDP [RFC3095] */ - ROHCv2_UDP = 0x0102, /* ROHCv2 UDP [RFC5225] */ - ROHC_ESP = 0x0003, /* ROHC ESP [RFC3095] */ - ROHCV2_ESP = 0x0103, /* ROHCv2 ESP [RFC5225] */ - ROHC_IP = 0x0004, /* ROHC IP [RFC3843] */ - ROHCV2_IP = 0x0104, /* ROHCv2 IP [RFC5225] */ - ROHC_LLA = 0x0005, /* ROHC LLA [RFC4362] */ - ROHC_LLA_WITH_R_MODE = 0x0105, /* ROHC LLA with R-mode [RFC3408] */ - ROHC_TCP = 0x0006, /* ROHC TCP [RFC6846] */ - ROHC_RTP_UDP_LITE = 0x0007, /* ROHC RTP/UDP-Lite [RFC4019] */ - ROHCV2_RTP_UDP_LITE = 0x0107, /* ROHCv2 RTP/UDP-Lite [RFC5225] */ - ROHC_UDP_LITE = 0x0008, /* ROHC UDP-Lite [RFC4019] */ - ROHCV2_UDP_LITE = 0x0108, /* ROHCv2 UDP-Lite [RFC5225] */ -}; - -/* According to: 3GPP TS 44.065, 6.6.2.1 Parameters (Table 7a) */ -struct gprs_sndcp_dcomp_v42bis_params { - uint8_t nsapi_len; /* Number of applicable NSAPIs - * (default 0) */ - uint8_t nsapi[MAX_NSAPI]; /* Applicable NSAPIs (default 0) */ - int p0; /* (default 3) */ - int p1; /* (default 2048) */ - int p2; /* (default 20) */ - -}; - -/* According to: 3GPP TS 44.065, 6.6.2.2 Assignment of DCOMP values */ -enum gprs_sndcp_dcomp_v42bis_dcomp { - V42BIS_DCOMP1, /* V.42bis enabled */ - V42BIS_DCOMP_NUM /* Number of dcomp values */ -}; - -/* According to: 3GPP TS 44.065, 6.6.3.1 Parameters (Table 7c) */ -struct gprs_sndcp_dcomp_v44_params { - uint8_t nsapi_len; /* Number of applicable NSAPIs - * (default 0) */ - uint8_t nsapi[MAX_NSAPI]; /* Applicable NSAPIs (default 0) */ - int c0; /* (default 10000000) */ - int p0; /* (default 3) */ - int p1t; /* Refer to subclause 6.6.3.1.4 */ - int p1r; /* Refer to subclause 6.6.3.1.5 */ - int p3t; /* (default 3 x p1t) */ - int p3r; /* (default 3 x p1r) */ -}; - -/* According to: 3GPP TS 44.065, 6.6.3.2 Assignment of DCOMP values */ -enum gprs_sndcp_dcomp_v44_dcomp { - V44_DCOMP1, /* Packet method compressed */ - V44_DCOMP2, /* Multi packet method compressed */ - V44_DCOMP_NUM /* Number of dcomp values */ -}; - -/* Transform a list with compression fields into an SNDCP-XID message (dst) */ -int gprs_sndcp_compile_xid(uint8_t *dst, unsigned int dst_maxlen, - const struct llist_head *comp_fields, int version); - -/* Transform an SNDCP-XID message (src) into a list of SNDCP-XID fields */ -struct llist_head *gprs_sndcp_parse_xid(int *version, - const void *ctx, - const uint8_t *src, - unsigned int src_len, - const struct llist_head - *comp_fields_req); - -/* Find out to which compression class the specified comp-field belongs - * (header compression or data compression?) */ -int gprs_sndcp_get_compression_class( - const struct gprs_sndcp_comp_field *comp_field); - -/* Dump a list with SNDCP-XID fields (Debug) */ -void gprs_sndcp_dump_comp_fields(const struct llist_head *comp_fields, - unsigned int logl); - diff --git a/openbsc/include/openbsc/gprs_subscriber.h b/openbsc/include/openbsc/gprs_subscriber.h deleted file mode 100644 index be78febf..00000000 --- a/openbsc/include/openbsc/gprs_subscriber.h +++ /dev/null @@ -1,31 +0,0 @@ -/* GPRS subscriber details for use in SGSN land */ -#pragma once - -#include - -#include -#include - -extern struct llist_head * const gprs_subscribers; - -struct gprs_subscr { - struct llist_head entry; - int use_count; - - char imsi[GSM23003_IMSI_MAX_DIGITS+1]; - uint32_t tmsi; - char imei[GSM23003_IMEISV_NUM_DIGITS+1]; - bool authorized; - bool keep_in_ram; - uint32_t flags; - uint16_t lac; - - struct sgsn_subscriber_data *sgsn_data; -}; - -struct gprs_subscr *_gprs_subscr_get(struct gprs_subscr *gsub, - const char *file, int line); -struct gprs_subscr *_gprs_subscr_put(struct gprs_subscr *gsub, - const char *file, int line); -#define gprs_subscr_get(gsub) _gprs_subscr_get(gsub, __BASE_FILE__, __LINE__) -#define gprs_subscr_put(gsub) _gprs_subscr_put(gsub, __BASE_FILE__, __LINE__) diff --git a/openbsc/include/openbsc/gprs_utils.h b/openbsc/include/openbsc/gprs_utils.h deleted file mode 100644 index 603605c7..00000000 --- a/openbsc/include/openbsc/gprs_utils.h +++ /dev/null @@ -1,45 +0,0 @@ -/* GPRS utility functions */ - -/* (C) 2010 by Harald Welte - * (C) 2010-2014 by On-Waves - * (C) 2013 by Holger Hans Peter Freyther - * All Rights Reserved - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation; either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - * - */ -#pragma once - -#include -#include - -struct msgb; -struct gprs_ra_id; - -struct msgb *gprs_msgb_copy(const struct msgb *msg, const char *name); -int gprs_msgb_resize_area(struct msgb *msg, uint8_t *area, - size_t old_size, size_t new_size); -char *gprs_apn_to_str(char *out_str, const uint8_t *apn_enc, size_t rest_chars); -int gprs_str_to_apn(uint8_t *apn_enc, size_t max_len, const char *str); - -/* GSM 04.08, 10.5.7.3 GPRS Timer */ -int gprs_tmr_to_secs(uint8_t tmr); -uint8_t gprs_secs_to_tmr_floor(int secs); - -int gprs_is_mi_tmsi(const uint8_t *value, size_t value_len); -int gprs_is_mi_imsi(const uint8_t *value, size_t value_len); -int gprs_parse_mi_tmsi(const uint8_t *value, size_t value_len, uint32_t *tmsi); -void gprs_parse_tmsi(const uint8_t *value, uint32_t *tmsi); - -int gprs_ra_id_equals(const struct gprs_ra_id *id1, const struct gprs_ra_id *id2); diff --git a/openbsc/include/openbsc/gsm_04_08.h b/openbsc/include/openbsc/gsm_04_08.h deleted file mode 100644 index a8b2de95..00000000 --- a/openbsc/include/openbsc/gsm_04_08.h +++ /dev/null @@ -1,85 +0,0 @@ -#ifndef _GSM_04_08_H -#define _GSM_04_08_H - -#include -#include -#include - -#include - -struct msgb; -struct gsm_bts; -struct gsm_subscriber; -struct gsm_network; -struct gsm_trans; -struct gsm_subscriber_connection; -struct amr_multirate_conf; -struct amr_mode; -struct bsc_subscr; - -#define GSM48_ALLOC_SIZE 2048 -#define GSM48_ALLOC_HEADROOM 256 - -static inline struct msgb *gsm48_msgb_alloc_name(const char *name) -{ - return msgb_alloc_headroom(GSM48_ALLOC_SIZE, GSM48_ALLOC_HEADROOM, - name); -} - -/* config options controlling the behaviour of the lower leves */ -void gsm0408_allow_everyone(int allow); -void gsm0408_clear_request(struct gsm_subscriber_connection *conn, uint32_t cause); -void gsm0408_clear_all_trans(struct gsm_network *net, int protocol); -int gsm0408_dispatch(struct gsm_subscriber_connection *conn, struct msgb *msg); - -int gsm0408_rcvmsg(struct msgb *msg, uint8_t link_id); -int gsm0408_new_conn(struct gsm_subscriber_connection *conn); -enum gsm_chan_t get_ctype_by_chreq(struct gsm_network *bts, uint8_t ra); -/* don't use "enum gsm_chreq_reason_t" to avoid circular dependency */ -int get_reason_by_chreq(uint8_t ra, int neci); -void gsm_net_update_ctype(struct gsm_network *net); - -int gsm48_tx_mm_info(struct gsm_subscriber_connection *conn); -int gsm48_tx_mm_auth_req(struct gsm_subscriber_connection *conn, uint8_t *rand, - uint8_t *autn, int key_seq); -int gsm48_tx_mm_auth_rej(struct gsm_subscriber_connection *conn); -int gsm48_tx_mm_serv_ack(struct gsm_subscriber_connection *conn); -int gsm48_tx_mm_serv_rej(struct gsm_subscriber_connection *conn, - enum gsm48_reject_value value); -int gsm48_send_rr_release(struct gsm_lchan *lchan); -int gsm48_send_rr_ciph_mode(struct gsm_lchan *lchan, int want_imeisv); -int gsm48_send_rr_app_info(struct gsm_subscriber_connection *conn, uint8_t apdu_id, - uint8_t apdu_len, const uint8_t *apdu); -int gsm48_send_rr_ass_cmd(struct gsm_lchan *dest_lchan, struct gsm_lchan *lchan, uint8_t power_class); -int gsm48_send_ho_cmd(struct gsm_lchan *old_lchan, struct gsm_lchan *new_lchan, - uint8_t power_command, uint8_t ho_ref); - -int mncc_tx_to_cc(struct gsm_network *net, int msg_type, void *arg); - -/* convert a ASCII phone number to call-control BCD */ -int encode_bcd_number(uint8_t *bcd_lv, uint8_t max_len, - int h_len, const char *input); -int decode_bcd_number(char *output, int output_len, const uint8_t *bcd_lv, - int h_len); - -int send_siemens_mrpci(struct gsm_lchan *lchan, uint8_t *classmark2_lv); -int gsm48_extract_mi(uint8_t *classmark2, int length, char *mi_string, uint8_t *mi_type); -int gsm48_paging_extract_mi(struct gsm48_pag_resp *pag, int length, char *mi_string, uint8_t *mi_type); -int gsm48_handle_paging_resp(struct gsm_subscriber_connection *conn, - struct msgb *msg, struct bsc_subscr *bsub); - -int gsm48_lchan_modify(struct gsm_lchan *lchan, uint8_t lchan_mode); -int gsm48_rx_rr_modif_ack(struct msgb *msg); -int gsm48_parse_meas_rep(struct gsm_meas_rep *rep, struct msgb *msg); - -struct msgb *gsm48_create_mm_serv_rej(enum gsm48_reject_value value); -struct msgb *gsm48_create_loc_upd_rej(uint8_t cause); -void gsm48_lchan2chan_desc(struct gsm48_chan_desc *cd, - const struct gsm_lchan *lchan); - -void release_security_operation(struct gsm_subscriber_connection *conn); -void allocate_security_operation(struct gsm_subscriber_connection *conn); - -int gsm48_multirate_config(uint8_t *lv, const struct amr_multirate_conf *mr, const struct amr_mode *modes); - -#endif diff --git a/openbsc/include/openbsc/gsm_04_11.h b/openbsc/include/openbsc/gsm_04_11.h deleted file mode 100644 index 017c8876..00000000 --- a/openbsc/include/openbsc/gsm_04_11.h +++ /dev/null @@ -1,47 +0,0 @@ -#ifndef _GSM_04_11_H -#define _GSM_04_11_H - -#include - -#define UM_SAPI_SMS 3 /* See GSM 04.05/04.06 */ - -/* SMS deliver PDU */ -struct sms_deliver { - uint8_t mti:2; /* message type indicator */ - uint8_t mms:1; /* more messages to send */ - uint8_t rp:1; /* reply path */ - uint8_t udhi:1; /* user data header indicator */ - uint8_t sri:1; /* status report indication */ - uint8_t *orig_addr; /* originating address */ - uint8_t pid; /* protocol identifier */ - uint8_t dcs; /* data coding scheme */ - /* service centre time stamp */ - uint8_t ud_len; /* user data length */ - uint8_t *user_data; /* user data */ - - uint8_t msg_ref; /* message reference */ - uint8_t *smsc; -}; - -struct msgb; - -int gsm0411_rcv_sms(struct gsm_subscriber_connection *conn, struct msgb *msg); - -struct gsm_sms *sms_alloc(void); -void sms_free(struct gsm_sms *sms); -struct gsm_sms *sms_from_text(struct gsm_subscriber *receiver, struct gsm_subscriber *sender, int dcs, const char *text); - -void _gsm411_sms_trans_free(struct gsm_trans *trans); -int gsm411_send_sms_subscr(struct gsm_subscriber *subscr, - struct gsm_sms *sms); -int gsm411_send_sms(struct gsm_subscriber_connection *conn, - struct gsm_sms *sms); -void gsm411_sapi_n_reject(struct gsm_subscriber_connection *conn); - -uint8_t sms_next_rp_msg_ref(uint8_t *next_rp_ref); - -int gsm411_send_rp_ack(struct gsm_trans *trans, uint8_t msg_ref); -int gsm411_send_rp_error(struct gsm_trans *trans, uint8_t msg_ref, - uint8_t cause); - -#endif diff --git a/openbsc/include/openbsc/gsm_04_80.h b/openbsc/include/openbsc/gsm_04_80.h deleted file mode 100644 index d65f640b..00000000 --- a/openbsc/include/openbsc/gsm_04_80.h +++ /dev/null @@ -1,25 +0,0 @@ -#ifndef _GSM_04_80_H -#define _GSM_04_80_H - -#include -#include -#include - -struct gsm_subscriber_connection; - -int gsm0480_send_ussd_response(struct gsm_subscriber_connection *conn, - const struct msgb *in_msg, const char* response_text, - const struct ss_request *req); -int gsm0480_send_ussd_reject(struct gsm_subscriber_connection *conn, - const struct msgb *msg, - const struct ss_request *request); - -int msc_send_ussd_notify(struct gsm_subscriber_connection *conn, int level, - const char *text); -int msc_send_ussd_release_complete(struct gsm_subscriber_connection *conn); - -int bsc_send_ussd_notify(struct gsm_subscriber_connection *conn, int level, - const char *text); -int bsc_send_ussd_release_complete(struct gsm_subscriber_connection *conn); - -#endif diff --git a/openbsc/include/openbsc/gsm_data.h b/openbsc/include/openbsc/gsm_data.h deleted file mode 100644 index a9740510..00000000 --- a/openbsc/include/openbsc/gsm_data.h +++ /dev/null @@ -1,596 +0,0 @@ -#ifndef _GSM_DATA_H -#define _GSM_DATA_H - -#include -#include -#include -#include - -#include -#include -#include -#include - -#include - -#include -#include - -/** annotations for msgb ownership */ -#define __uses - -#define OBSC_NM_W_ACK_CB(__msgb) (__msgb)->cb[3] - -struct mncc_sock_state; -struct gsm_subscriber_group; -struct bsc_subscr; - -#define OBSC_LINKID_CB(__msgb) (__msgb)->cb[3] - -#define tmsi_from_string(str) strtoul(str, NULL, 10) - -/* 3-bit long values */ -#define EARFCN_PRIO_INVALID 8 -#define EARFCN_MEAS_BW_INVALID 8 -/* 5-bit long values */ -#define EARFCN_QRXLV_INVALID 32 -#define EARFCN_THRESH_LOW_INVALID 32 - -enum gsm_security_event { - GSM_SECURITY_NOAVAIL, - GSM_SECURITY_AUTH_FAILED, - GSM_SECURITY_SUCCEEDED, - GSM_SECURITY_ALREADY, -}; - -struct msgb; -typedef int gsm_cbfn(unsigned int hooknum, - unsigned int event, - struct msgb *msg, - void *data, void *param); - -/* Real authentication information containing Ki */ -enum gsm_auth_algo { - AUTH_ALGO_NONE, - AUTH_ALGO_XOR, - AUTH_ALGO_COMP128v1, -}; - -struct gsm_auth_info { - enum gsm_auth_algo auth_algo; - unsigned int a3a8_ki_len; - uint8_t a3a8_ki[16]; -}; - -struct gsm_auth_tuple { - int use_count; - int key_seq; - struct osmo_auth_vector vec; -}; -#define GSM_KEY_SEQ_INVAL 7 /* GSM 04.08 - 10.5.1.2 */ - -/* - * LOCATION UPDATING REQUEST state - * - * Our current operation is: - * - Get imei/tmsi - * - Accept/Reject according to global policy - */ -struct gsm_loc_updating_operation { - struct osmo_timer_list updating_timer; - unsigned int waiting_for_imsi : 1; - unsigned int waiting_for_imei : 1; - unsigned int key_seq : 4; -}; - -/* - * AUTHENTICATION/CIPHERING state - */ -struct gsm_security_operation { - struct gsm_auth_tuple atuple; - gsm_cbfn *cb; - void *cb_data; -}; - -/* - * A dummy to keep a connection up for at least - * a couple of seconds to work around MSC issues. - */ -struct gsm_anchor_operation { - struct osmo_timer_list timeout; -}; - -/* Maximum number of neighbor cells whose average we track */ -#define MAX_NEIGH_MEAS 10 -/* Maximum size of the averaging window for neighbor cells */ -#define MAX_WIN_NEIGH_AVG 10 - -/* processed neighbor measurements for one cell */ -struct neigh_meas_proc { - uint16_t arfcn; - uint8_t bsic; - uint8_t rxlev[MAX_WIN_NEIGH_AVG]; - unsigned int rxlev_cnt; - uint8_t last_seen_nr; -}; - -enum ran_type { - RAN_UNKNOWN, - RAN_GERAN_A, /* 2G / A-interface */ - RAN_UTRAN_IU, /* 3G / Iu-interface (IuCS or IuPS) */ -}; - -/* active radio connection of a mobile subscriber */ -struct gsm_subscriber_connection { - struct llist_head entry; - - /* To whom we are allocated at the moment */ - struct gsm_subscriber *subscr; - - /* libbsc subscriber information */ - struct bsc_subscr *bsub; - - /* LU expiration handling */ - uint8_t expire_timer_stopped; - /* SMS helpers for libmsc */ - uint8_t next_rp_ref; - - /* - * Operations that have a state and might be pending - */ - struct gsm_loc_updating_operation *loc_operation; - struct gsm_security_operation *sec_operation; - struct gsm_anchor_operation *anch_operation; - - /* Are we part of a special "silent" call */ - int silent_call; - - /* MNCC rtp bridge markers */ - int mncc_rtp_bridge; - int mncc_rtp_create_pending; - int mncc_rtp_connect_pending; - - /* bsc structures */ - struct osmo_bsc_sccp_con *sccp_con; /* BSC */ - - /* back pointers */ - struct gsm_network *network; - - int in_release; - struct gsm_lchan *lchan; /* BSC */ - struct gsm_lchan *ho_lchan; /* BSC */ - struct gsm_bts *bts; /* BSC */ - - /* for assignment handling */ - struct osmo_timer_list T10; /* BSC */ - struct gsm_lchan *secondary_lchan; /* BSC */ - - /* connected via 2G or 3G? */ - enum ran_type via_ran; -}; - - -#define ROLE_BSC -#include "gsm_data_shared.h" - - -enum { - BSC_CTR_CHREQ_TOTAL, - BSC_CTR_CHREQ_NO_CHANNEL, - BSC_CTR_HANDOVER_ATTEMPTED, - BSC_CTR_HANDOVER_NO_CHANNEL, - BSC_CTR_HANDOVER_TIMEOUT, - BSC_CTR_HANDOVER_COMPLETED, - BSC_CTR_HANDOVER_FAILED, - BSC_CTR_PAGING_ATTEMPTED, - BSC_CTR_PAGING_DETACHED, - BSC_CTR_PAGING_COMPLETED, - BSC_CTR_PAGING_EXPIRED, - BSC_CTR_CHAN_RF_FAIL, - BSC_CTR_CHAN_RLL_ERR, - BSC_CTR_BTS_OML_FAIL, - BSC_CTR_BTS_RSL_FAIL, - BSC_CTR_CODEC_AMR_F, - BSC_CTR_CODEC_AMR_H, - BSC_CTR_CODEC_EFR, - BSC_CTR_CODEC_V1_FR, - BSC_CTR_CODEC_V1_HR, -}; - -static const struct rate_ctr_desc bsc_ctr_description[] = { - [BSC_CTR_CHREQ_TOTAL] = {"chreq.total", "Received channel requests."}, - [BSC_CTR_CHREQ_NO_CHANNEL] = {"chreq.no_channel", "Sent to MS no channel available."}, - [BSC_CTR_HANDOVER_ATTEMPTED] = {"handover.attempted", "Received handover attempts."}, - [BSC_CTR_HANDOVER_NO_CHANNEL] = {"handover.no_channel", "Sent no channel available responses."}, - [BSC_CTR_HANDOVER_TIMEOUT] = {"handover.timeout", "Count the amount of timeouts of timer T3103."}, - [BSC_CTR_HANDOVER_COMPLETED] = {"handover.completed", "Received handover completed."}, - [BSC_CTR_HANDOVER_FAILED] = {"handover.failed", "Receive HO FAIL messages."}, - [BSC_CTR_PAGING_ATTEMPTED] = {"paging.attempted", "Paging attempts for a MS."}, - [BSC_CTR_PAGING_DETACHED] = {"paging.detached", "Counts the amount of paging attempts which couldn't sent out any paging request because no responsible bts found."}, - [BSC_CTR_PAGING_COMPLETED] = {"paging.completed", "Paging successful completed."}, - [BSC_CTR_PAGING_EXPIRED] = {"paging.expired", "Paging Request expired because of timeout T3113."}, - [BSC_CTR_CHAN_RF_FAIL] = {"chan.rf_fail", "Received a RF failure indication from BTS."}, - [BSC_CTR_CHAN_RLL_ERR] = {"chan.rll_err", "Received a RLL failure with T200 cause from BTS."}, - [BSC_CTR_BTS_OML_FAIL] = {"bts.oml_fail", "Received a TEI down on a OML link."}, - [BSC_CTR_BTS_RSL_FAIL] = {"bts.rsl_fail", "Received a TEI down on a OML link."}, - [BSC_CTR_CODEC_AMR_F] = {"bts.codec_amr_f", "Count the usage of AMR/F codec by channel mode requested."}, - [BSC_CTR_CODEC_AMR_H] = {"bts.codec_amr_h", "Count the usage of AMR/H codec by channel mode requested."}, - [BSC_CTR_CODEC_EFR] = {"bts.codec_efr", "Count the usage of EFR codec by channel mode requested."}, - [BSC_CTR_CODEC_V1_FR] = {"bts.codec_fr", "Count the usage of FR codec by channel mode requested."}, - [BSC_CTR_CODEC_V1_HR] = {"bts.codec_hr", "Count the usage of HR codec by channel mode requested."}, -}; - -enum { - MSC_CTR_LOC_UPDATE_TYPE_ATTACH, - MSC_CTR_LOC_UPDATE_TYPE_NORMAL, - MSC_CTR_LOC_UPDATE_TYPE_PERIODIC, - MSC_CTR_LOC_UPDATE_TYPE_DETACH, - MSC_CTR_LOC_UPDATE_FAILED, - MSC_CTR_LOC_UPDATE_COMPLETED, - MSC_CTR_SMS_SUBMITTED, - MSC_CTR_SMS_NO_RECEIVER, - MSC_CTR_SMS_DELIVERED, - MSC_CTR_SMS_RP_ERR_MEM, - MSC_CTR_SMS_RP_ERR_OTHER, - MSC_CTR_SMS_DELIVER_UNKNOWN_ERROR, - MSC_CTR_CALL_MO_SETUP, - MSC_CTR_CALL_MO_CONNECT_ACK, - MSC_CTR_CALL_MT_SETUP, - MSC_CTR_CALL_MT_CONNECT, - MSC_CTR_CALL_ACTIVE, - MSC_CTR_CALL_COMPLETE, - MSC_CTR_CALL_INCOMPLETE, -}; - -static const struct rate_ctr_desc msc_ctr_description[] = { - [MSC_CTR_LOC_UPDATE_TYPE_ATTACH] = {"loc_update_type.attach", "Received location update imsi attach requests."}, - [MSC_CTR_LOC_UPDATE_TYPE_NORMAL] = {"loc_update_type.normal", "Received location update normal requests."}, - [MSC_CTR_LOC_UPDATE_TYPE_PERIODIC] = {"loc_update_type.periodic", "Received location update periodic requests."}, - [MSC_CTR_LOC_UPDATE_TYPE_DETACH] = {"loc_update_type.detach", "Received location update detach indication."}, - [MSC_CTR_LOC_UPDATE_FAILED] = {"loc_update_resp.failed", "Rejected location updates."}, - [MSC_CTR_LOC_UPDATE_COMPLETED] = {"loc_update_resp.completed", "Successful location updates."}, - [MSC_CTR_SMS_SUBMITTED] = {"sms.submitted", "Received a RPDU from a MS (MO)."}, - [MSC_CTR_SMS_NO_RECEIVER] = {"sms.no_receiver", "Counts SMS which couldn't routed because no receiver found."}, - [MSC_CTR_SMS_DELIVERED] = {"sms.delivered", "Global SMS Deliver attempts."}, - [MSC_CTR_SMS_RP_ERR_MEM] = {"sms.rp_err_mem", "CAUSE_MT_MEM_EXCEEDED errors of MS responses on a sms deliver attempt."}, - [MSC_CTR_SMS_RP_ERR_OTHER] = {"sms.rp_err_other", "Other error of MS responses on a sms delive attempt."}, - [MSC_CTR_SMS_DELIVER_UNKNOWN_ERROR] = {"sms.deliver_unknown_error", "Unknown error occured during sms delivery."}, - /* FIXME: count also sms delivered */ - [MSC_CTR_CALL_MO_SETUP] = {"call.mo_setup", "Received setup requests from a MS to init a MO call."}, - [MSC_CTR_CALL_MO_CONNECT_ACK] = {"call.mo_connect_ack", "Received a connect ack from MS of a MO call. Call is now succesful connected up."}, - [MSC_CTR_CALL_MT_SETUP] = {"call.mt_setup", "Sent setup requests to the MS (MT)."}, - [MSC_CTR_CALL_MT_CONNECT] = {"call.mt_connect", "Sent a connect to the MS (MT)."}, - [MSC_CTR_CALL_ACTIVE] = {"call.active", "Count total amount of calls that ever reached active state."}, - [MSC_CTR_CALL_COMPLETE] = {"call.complete", "Count total amount of calls which got terminated by disconnect req or ind after reaching active state."}, - [MSC_CTR_CALL_INCOMPLETE] = {"call.incomplete", "Count total amount of call which got terminated by any other reason after reaching active state."}, -}; - - -static const struct rate_ctr_group_desc bsc_ctrg_desc = { - "bsc", - "base station controller", - OSMO_STATS_CLASS_GLOBAL, - ARRAY_SIZE(bsc_ctr_description), - bsc_ctr_description, -}; - -static const struct rate_ctr_group_desc msc_ctrg_desc = { - "msc", - "mobile switching center", - OSMO_STATS_CLASS_GLOBAL, - ARRAY_SIZE(msc_ctr_description), - msc_ctr_description, -}; - -enum gsm_auth_policy { - GSM_AUTH_POLICY_CLOSED, /* only subscribers authorized in DB */ - GSM_AUTH_POLICY_ACCEPT_ALL, /* accept everyone, even if not authorized in DB */ - GSM_AUTH_POLICY_TOKEN, /* accept first, send token per sms, then revoke authorization */ - GSM_AUTH_POLICY_REGEXP, /* accept IMSIs matching given regexp */ -}; - -#define GSM_T3101_DEFAULT 10 -#define GSM_T3105_DEFAULT 40 -#define GSM_T3113_DEFAULT 60 -#define GSM_T3122_DEFAULT 10 - -struct gsm_tz { - int override; /* if 0, use system's time zone instead. */ - int hr; /* hour */ - int mn; /* minute */ - int dst; /* daylight savings */ -}; - -struct gsm_network { - /* global parameters */ - uint16_t country_code; - uint16_t network_code; - char *name_long; - char *name_short; - enum gsm_auth_policy auth_policy; - regex_t authorized_regexp; - char *authorized_reg_str; - enum gsm48_reject_value reject_cause; - int a5_encryption; - int neci; - int send_mm_info; - struct { - int active; - /* Window RXLEV averaging */ - unsigned int win_rxlev_avg; /* number of SACCH frames */ - /* Window RXQUAL averaging */ - unsigned int win_rxqual_avg; /* number of SACCH frames */ - /* Window RXLEV neighbouring cells averaging */ - unsigned int win_rxlev_avg_neigh; /* number of SACCH frames */ - - /* how often should we check for power budget HO */ - unsigned int pwr_interval; /* SACCH frames */ - /* how much better does a neighbor cell have to be ? */ - unsigned int pwr_hysteresis; /* dBm */ - /* maximum distacne before we try a handover */ - unsigned int max_distance; /* TA values */ - } handover; - - struct rate_ctr_group *bsc_ctrs; - struct rate_ctr_group *msc_ctrs; - struct osmo_counter *active_calls; - - /* layer 4 */ - struct mncc_sock_state *mncc_state; - mncc_recv_cb_t mncc_recv; - struct llist_head upqueue; - struct llist_head trans_list; - struct bsc_api *bsc_api; - - unsigned int num_bts; - struct llist_head bts_list; - - /* timer values */ - int T3101; - int T3103; - int T3105; - int T3107; - int T3109; - int T3111; - int T3113; - int T3115; - int T3117; - int T3119; - int T3122; - int T3141; - - /* timer to expire old location updates */ - struct osmo_timer_list subscr_expire_timer; - - /* Radio Resource Location Protocol (TS 04.31) */ - struct { - enum rrlp_mode mode; - } rrlp; - - enum gsm_chan_t ctype_by_chreq[18]; - - /* Use a TCH for handling requests of type paging any */ - int pag_any_tch; - - /* MSC data in case we are a true BSC */ - struct osmo_bsc_data *bsc_data; - - /* subscriber related features */ - bool auto_create_subscr; - bool auto_assign_exten; - uint64_t ext_min; - uint64_t ext_max; - struct gsm_subscriber_group *subscr_group; - struct gsm_sms_queue *sms_queue; - - /* nitb related control */ - int avoid_tmsi; - - /* control interface */ - struct ctrl_handle *ctrl; - - /* Allow or disallow TCH/F on dynamic TCH/F_TCH/H_PDCH; OS#1778 */ - bool dyn_ts_allow_tch_f; - - /* all active subscriber connections. */ - struct llist_head subscr_conns; - - /* if override is nonzero, this timezone data is used for all MM - * contexts. */ - /* TODO: in OsmoNITB, tz-override used to be BTS-specific. To enable - * BTS|RNC specific timezone overrides for multi-tz networks in - * OsmoMSC, this should be tied to the location area code (LAC). */ - struct gsm_tz tz; - - /* List of all struct bsc_subscr used in libbsc. This llist_head is - * allocated so that the llist_head pointer itself can serve as a - * talloc context (useful to not have to pass the entire gsm_network - * struct to the bsc_subscr_* API, and for bsc_susbscr unit tests to - * not require gsm_data.h). In an MSC-without-BSC environment, this - * pointer is NULL to indicate absence of a bsc_subscribers list. */ - struct llist_head *bsc_subscribers; -}; - -struct osmo_esme; - -enum gsm_sms_source_id { - SMS_SOURCE_UNKNOWN = 0, - SMS_SOURCE_MS, /* received from MS */ - SMS_SOURCE_VTY, /* received from VTY */ - SMS_SOURCE_SMPP, /* received via SMPP */ -}; - -#define SMS_HDR_SIZE 128 -#define SMS_TEXT_SIZE 256 - -struct gsm_sms_addr { - uint8_t ton; - uint8_t npi; - char addr[21+1]; -}; - -struct gsm_sms { - unsigned long long id; - struct gsm_subscriber *receiver; - struct gsm_sms_addr src, dst; - enum gsm_sms_source_id source; - - struct { - uint8_t transaction_id; - uint32_t msg_ref; - } gsm411; - - struct { - struct osmo_esme *esme; - uint32_t sequence_nr; - int transaction_mode; - char msg_id[16]; - } smpp; - - unsigned long validity_minutes; - uint8_t reply_path_req; - uint8_t status_rep_req; - uint8_t ud_hdr_ind; - uint8_t protocol_id; - uint8_t data_coding_scheme; - uint8_t msg_ref; - uint8_t user_data_len; - uint8_t user_data[SMS_TEXT_SIZE]; - - char text[SMS_TEXT_SIZE]; -}; - -extern void talloc_ctx_init(void *ctx_root); - -int gsm_set_bts_type(struct gsm_bts *bts, enum gsm_bts_type type); - -/* Get reference to a neighbor cell on a given BCCH ARFCN */ -struct gsm_bts *gsm_bts_neighbor(const struct gsm_bts *bts, - uint16_t arfcn, uint8_t bsic); - -enum gsm_bts_type parse_btstype(const char *arg); -const char *btstype2str(enum gsm_bts_type type); -struct gsm_bts *gsm_bts_by_lac(struct gsm_network *net, unsigned int lac, - struct gsm_bts *start_bts); - -extern void *tall_bsc_ctx; -extern int ipacc_rtp_direct; - -/* this actaully refers to the IPA transport, not the BTS model */ -static inline int is_ipaccess_bts(struct gsm_bts *bts) -{ - switch (bts->type) { - case GSM_BTS_TYPE_NANOBTS: - case GSM_BTS_TYPE_OSMOBTS: - return 1; - default: - break; - } - return 0; -} - -static inline int is_sysmobts_v2(struct gsm_bts *bts) -{ - switch (bts->type) { - case GSM_BTS_TYPE_OSMOBTS: - return 1; - default: - break; - } - return 0; -} - -static inline int is_siemens_bts(struct gsm_bts *bts) -{ - switch (bts->type) { - case GSM_BTS_TYPE_BS11: - return 1; - default: - break; - } - - return 0; -} - -static inline int is_nokia_bts(struct gsm_bts *bts) -{ - switch (bts->type) { - case GSM_BTS_TYPE_NOKIA_SITE: - return 1; - default: - break; - } - - return 0; -} - -static inline int is_e1_bts(struct gsm_bts *bts) -{ - switch (bts->type) { - case GSM_BTS_TYPE_BS11: - case GSM_BTS_TYPE_RBS2000: - case GSM_BTS_TYPE_NOKIA_SITE: - return 1; - default: - break; - } - - return 0; -} - -enum gsm_auth_policy gsm_auth_policy_parse(const char *arg); -const char *gsm_auth_policy_name(enum gsm_auth_policy policy); - -enum rrlp_mode rrlp_mode_parse(const char *arg); -const char *rrlp_mode_name(enum rrlp_mode mode); - -enum bts_gprs_mode bts_gprs_mode_parse(const char *arg, int *valid); -const char *bts_gprs_mode_name(enum bts_gprs_mode mode); -int bts_gprs_mode_is_compat(struct gsm_bts *bts, enum bts_gprs_mode mode); - -int gsm48_ra_id_by_bts(uint8_t *buf, struct gsm_bts *bts); -void gprs_ra_id_by_bts(struct gprs_ra_id *raid, struct gsm_bts *bts); -struct gsm_meas_rep *lchan_next_meas_rep(struct gsm_lchan *lchan); - -int gsm_btsmodel_set_feature(struct gsm_bts_model *model, enum gsm_bts_features feat); -int gsm_bts_model_register(struct gsm_bts_model *model); - -struct gsm_subscriber_connection *bsc_subscr_con_allocate(struct gsm_lchan *lchan); -void bsc_subscr_con_free(struct gsm_subscriber_connection *conn); - -struct gsm_subscriber_connection *msc_subscr_con_allocate(struct gsm_network *network); -void msc_subscr_con_free(struct gsm_subscriber_connection *conn); - -struct gsm_bts *gsm_bts_alloc_register(struct gsm_network *net, - enum gsm_bts_type type, - uint8_t bsic); - -void set_ts_e1link(struct gsm_bts_trx_ts *ts, uint8_t e1_nr, - uint8_t e1_ts, uint8_t e1_ts_ss); - -void gsm_trx_lock_rf(struct gsm_bts_trx *trx, int locked); -bool gsm_btsmodel_has_feature(struct gsm_bts_model *model, enum gsm_bts_features feat); -struct gsm_bts_trx *gsm_bts_trx_by_nr(struct gsm_bts *bts, int nr); -int gsm_bts_trx_set_system_infos(struct gsm_bts_trx *trx); -int gsm_bts_set_system_infos(struct gsm_bts *bts); - -/* generic E1 line operations for all ISDN-based BTS. */ -extern struct e1inp_line_ops bts_isdn_e1inp_line_ops; - -extern const struct value_string bts_type_names[_NUM_GSM_BTS_TYPE+1]; -extern const struct value_string bts_type_descs[_NUM_GSM_BTS_TYPE+1]; - -/* control interface handling */ -int bsc_base_ctrl_cmds_install(void); -int msc_ctrl_cmds_install(void); - -/* dependency handling */ -void bts_depend_mark(struct gsm_bts *bts, int dep); -void bts_depend_clear(struct gsm_bts *bts, int dep); -int bts_depend_check(struct gsm_bts *bts); -int bts_depend_is_depedency(struct gsm_bts *base, struct gsm_bts *other); - -int gsm_bts_get_radio_link_timeout(const struct gsm_bts *bts); -void gsm_bts_set_radio_link_timeout(struct gsm_bts *bts, int value); - -#endif /* _GSM_DATA_H */ diff --git a/openbsc/include/openbsc/gsm_data_shared.h b/openbsc/include/openbsc/gsm_data_shared.h deleted file mode 100644 index 4c71a075..00000000 --- a/openbsc/include/openbsc/gsm_data_shared.h +++ /dev/null @@ -1,994 +0,0 @@ -#ifndef _GSM_DATA_SHAREDH -#define _GSM_DATA_SHAREDH - -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -#ifndef ROLE_BSC -#include -#endif - -#include - -/* 16 is the max. number of SI2quater messages according to 3GPP TS 44.018 Table 10.5.2.33b.1: - 4-bit index is used (2#1111 = 10#15) */ -#define SI2Q_MAX_NUM 16 -/* length in bits (for single SI2quater message) */ -#define SI2Q_MAX_LEN 160 -#define SI2Q_MIN_LEN 18 - -struct osmo_bsc_data; - -struct osmo_bsc_sccp_con; -struct gsm_sms_queue; - -/* RRLP mode of operation */ -enum rrlp_mode { - RRLP_MODE_NONE, - RRLP_MODE_MS_BASED, - RRLP_MODE_MS_PREF, - RRLP_MODE_ASS_PREF, -}; - -/* Channel Request reason */ -enum gsm_chreq_reason_t { - GSM_CHREQ_REASON_EMERG, - GSM_CHREQ_REASON_PAG, - GSM_CHREQ_REASON_CALL, - GSM_CHREQ_REASON_LOCATION_UPD, - GSM_CHREQ_REASON_OTHER, - GSM_CHREQ_REASON_PDCH, -}; - -/* lchans 0..3 are SDCCH in combined channel configuration, - use 4 as magic number for BCCH hack - see osmo-bts-../oml.c:opstart_compl() */ -#define CCCH_LCHAN 4 - -#define TRX_NR_TS 8 -#define TS_MAX_LCHAN 8 - -#define HARDCODED_ARFCN 123 -#define HARDCODED_BSIC 0x3f /* NCC = 7 / BCC = 7 */ - -/* for multi-drop config */ -#define HARDCODED_BTS0_TS 1 -#define HARDCODED_BTS1_TS 6 -#define HARDCODED_BTS2_TS 11 - -#define MAX_VERSION_LENGTH 64 - -#define MAX_BTS_FEATURES 128 - -enum gsm_hooks { - GSM_HOOK_NM_SWLOAD, - GSM_HOOK_RR_PAGING, - GSM_HOOK_RR_SECURITY, -}; - -enum gsm_paging_event { - GSM_PAGING_SUCCEEDED, - GSM_PAGING_EXPIRED, - GSM_PAGING_OOM, - GSM_PAGING_BUSY, -}; - -enum bts_gprs_mode { - BTS_GPRS_NONE = 0, - BTS_GPRS_GPRS = 1, - BTS_GPRS_EGPRS = 2, -}; - -struct gsm_lchan; -struct gsm_subscriber; -struct gsm_mncc; -struct osmo_rtp_socket; -struct rtp_socket; -struct bsc_api; - -/* Network Management State */ -struct gsm_nm_state { - uint8_t operational; - uint8_t administrative; - uint8_t availability; -}; - -struct gsm_abis_mo { - uint8_t obj_class; - uint8_t procedure_pending; - struct abis_om_obj_inst obj_inst; - const char *name; - struct gsm_nm_state nm_state; - struct tlv_parsed *nm_attr; - struct gsm_bts *bts; -}; - -/* Ericsson OM2000 Managed Object */ -struct abis_om2k_mo { - uint8_t class; - uint8_t bts; - uint8_t assoc_so; - uint8_t inst; -} __attribute__ ((packed)); - -struct om2k_mo { - struct abis_om2k_mo addr; - struct osmo_fsm_inst *fsm; -}; - -#define A38_XOR_MIN_KEY_LEN 12 -#define A38_XOR_MAX_KEY_LEN 16 -#define A38_COMP128_KEY_LEN 16 -#define RSL_ENC_ALG_A5(x) (x+1) -#define MAX_EARFCN_LIST 32 - -/* is the data link established? who established it? */ -#define LCHAN_SAPI_UNUSED 0 -#define LCHAN_SAPI_MS 1 -#define LCHAN_SAPI_NET 2 -#define LCHAN_SAPI_REL 3 - -/* state of a logical channel */ -enum gsm_lchan_state { - LCHAN_S_NONE, /* channel is not active */ - LCHAN_S_ACT_REQ, /* channel activation requested */ - LCHAN_S_ACTIVE, /* channel is active and operational */ - LCHAN_S_REL_REQ, /* channel release has been requested */ - LCHAN_S_REL_ERR, /* channel is in an error state */ - LCHAN_S_BROKEN, /* channel is somehow unusable */ - LCHAN_S_INACTIVE, /* channel is set inactive */ -}; - -/* BTS ONLY */ -#define MAX_NUM_UL_MEAS 104 -#define LC_UL_M_F_L1_VALID (1 << 0) -#define LC_UL_M_F_RES_VALID (1 << 1) - -struct bts_ul_meas { - /* BER in units of 0.01%: 10.000 == 100% ber, 0 == 0% ber */ - uint16_t ber10k; - /* timing advance offset (in quarter bits) */ - int16_t ta_offs_qbits; - /* C/I ratio in dB */ - float c_i; - /* flags */ - uint8_t is_sub:1; - /* RSSI in dBm * -1 */ - uint8_t inv_rssi; -}; - -struct bts_codec_conf { - uint8_t hr; - uint8_t efr; - uint8_t amr; -}; - -struct amr_mode { - uint8_t mode; - uint8_t threshold; - uint8_t hysteresis; -}; - -struct amr_multirate_conf { - uint8_t gsm48_ie[2]; - struct amr_mode ms_mode[4]; - struct amr_mode bts_mode[4]; - uint8_t num_modes; -}; -/* /BTS ONLY */ - -enum lchan_csd_mode { - LCHAN_CSD_M_NT, - LCHAN_CSD_M_T_1200_75, - LCHAN_CSD_M_T_600, - LCHAN_CSD_M_T_1200, - LCHAN_CSD_M_T_2400, - LCHAN_CSD_M_T_9600, - LCHAN_CSD_M_T_14400, - LCHAN_CSD_M_T_29000, - LCHAN_CSD_M_T_32000, -}; - -/* State of the SAPIs in the lchan */ -enum lchan_sapi_state { - LCHAN_SAPI_S_NONE, - LCHAN_SAPI_S_REQ, - LCHAN_SAPI_S_ASSIGNED, - LCHAN_SAPI_S_REL, - LCHAN_SAPI_S_ERROR, -}; - -struct gsm_lchan { - /* The TS that we're part of */ - struct gsm_bts_trx_ts *ts; - /* The logical subslot number in the TS */ - uint8_t nr; - /* The logical channel type */ - enum gsm_chan_t type; - /* RSL channel mode */ - enum rsl_cmod_spd rsl_cmode; - /* If TCH, traffic channel mode */ - enum gsm48_chan_mode tch_mode; - enum lchan_csd_mode csd_mode; - /* State */ - enum gsm_lchan_state state; - const char *broken_reason; - /* Power levels for MS and BTS */ - uint8_t bs_power; - uint8_t ms_power; - /* Encryption information */ - struct gsm_encr encr; - - /* AMR bits */ - uint8_t mr_ms_lv[7]; - uint8_t mr_bts_lv[7]; - - /* Established data link layer services */ - uint8_t sapis[8]; - int sacch_deact; - - struct { - uint32_t bound_ip; - uint32_t connect_ip; - uint16_t bound_port; - uint16_t connect_port; - uint16_t conn_id; - uint8_t rtp_payload; - uint8_t rtp_payload2; - uint8_t speech_mode; -#ifdef ROLE_BSC - struct rtp_socket *rtp_socket; -#else - struct osmo_rtp_socket *rtp_socket; -#endif - } abis_ip; - - uint8_t rqd_ta; - - char *name; - -#ifdef ROLE_BSC - struct osmo_timer_list T3101; - struct osmo_timer_list T3109; - struct osmo_timer_list T3111; - struct osmo_timer_list error_timer; - struct osmo_timer_list act_timer; - struct osmo_timer_list rel_work; - uint8_t error_cause; - - /* table of neighbor cell measurements */ - struct neigh_meas_proc neigh_meas[MAX_NEIGH_MEAS]; - - /* cache of last measurement reports on this lchan */ - struct gsm_meas_rep meas_rep[6]; - int meas_rep_idx; - - /* GSM Random Access data */ - struct gsm48_req_ref *rqd_ref; - - struct gsm_subscriber_connection *conn; - - struct { - /* channel activation type and handover ref */ - uint8_t act_type; - uint8_t ho_ref; - struct gsm48_req_ref *rqd_ref; - uint8_t rqd_ta; - } dyn; -#else - /* Number of different GsmL1_Sapi_t used in osmo_bts_sysmo is 23. - * Currently we don't share these headers so this is a magic number. */ - struct llist_head sapi_cmds; - uint8_t sapis_dl[23]; - uint8_t sapis_ul[23]; - struct lapdm_channel lapdm_ch; - struct llist_head dl_tch_queue; - struct { - /* bitmask of all SI that are present/valid in si_buf */ - uint32_t valid; - uint32_t last; - /* buffers where we put the pre-computed SI: - SI2Q_MAX_NUM is the max number of SI2quater messages (see 3GPP TS 44.018) */ - sysinfo_buf_t buf[_MAX_SYSINFO_TYPE][SI2Q_MAX_NUM]; - } si; - struct { - uint8_t flags; - /* RSL measurment result number, 0 at lchan_act */ - uint8_t res_nr; - /* current Tx power level of the BTS */ - uint8_t bts_tx_pwr; - /* number of measurements stored in array below */ - uint8_t num_ul_meas; - struct bts_ul_meas uplink[MAX_NUM_UL_MEAS]; - /* last L1 header from the MS */ - uint8_t l1_info[2]; - struct gsm_meas_rep_unidir ul_res; - } meas; - struct { - struct amr_multirate_conf amr_mr; - struct { - struct osmo_fsm_inst *dl_amr_fsm; - /* TCH cache */ - uint8_t cache[20]; - /* FACCH cache */ - uint8_t facch[GSM_MACBLOCK_LEN]; - uint8_t len; - uint32_t fn; - bool is_update; - /* set for each SID frame to detect talkspurt for codecs - without explicit ONSET event */ - bool ul_sid; - /* indicates if DTXd was active during DL measurement - period */ - bool dl_active; - } dtx; - uint8_t last_cmr; - uint32_t last_fn; - } tch; - - /* 3GPP TS 48.058 § 9.3.37: [0; 255] ok, -1 means invalid*/ - int16_t ms_t_offs; - /* 3GPP TS 45.010 § 1.2 round trip propagation delay (in symbols) or -1 */ - int16_t p_offs; - - /* BTS-side ciphering state (rx only, bi-directional, ...) */ - uint8_t ciph_state; - uint8_t ciph_ns; - uint8_t loopback; - struct { - uint8_t active; - uint8_t ref; - /* T3105: PHYS INF retransmission */ - struct osmo_timer_list t3105; - /* counts up to Ny1 */ - unsigned int phys_info_count; - } ho; - /* S counter for link loss */ - int s; - /* Kind of the release/activation. E.g. RSL or PCU */ - int rel_act_kind; - /* RTP header Marker bit to indicate beginning of speech after pause */ - bool rtp_tx_marker; - /* power handling */ - struct { - uint8_t current; - uint8_t fixed; - } ms_power_ctrl; - - struct msgb *pending_rel_ind_msg; -#endif -}; - -enum { - TS_F_PDCH_ACTIVE = 0x1000, - TS_F_PDCH_ACT_PENDING = 0x2000, - TS_F_PDCH_DEACT_PENDING = 0x4000, - TS_F_PDCH_PENDING_MASK = 0x6000 /*< - TS_F_PDCH_ACT_PENDING | TS_F_PDCH_DEACT_PENDING */ -} gsm_bts_trx_ts_flags; - -/* One Timeslot in a TRX */ -struct gsm_bts_trx_ts { - struct gsm_bts_trx *trx; - /* number of this timeslot at the TRX */ - uint8_t nr; - - enum gsm_phys_chan_config pchan; - - struct { - enum gsm_phys_chan_config pchan_is; - enum gsm_phys_chan_config pchan_want; - struct msgb *pending_chan_activ; - } dyn; - - unsigned int flags; - struct gsm_abis_mo mo; - struct tlv_parsed nm_attr; - uint8_t nm_chan_comb; - int tsc; /* -1 == use BTS TSC */ - - struct { - /* Parameters below are configured by VTY */ - int enabled; - uint8_t maio; - uint8_t hsn; - struct bitvec arfcns; - uint8_t arfcns_data[1024/8]; - /* This is the pre-computed MA for channel assignments */ - struct bitvec ma; - uint8_t ma_len; /* part of ma_data that is used */ - uint8_t ma_data[8]; /* 10.5.2.21: max 8 bytes value part */ - } hopping; - - /* To which E1 subslot are we connected */ - struct gsm_e1_subslot e1_link; - - union { - struct { - struct om2k_mo om2k_mo; - } rbs2000; - }; - - struct gsm_lchan lchan[TS_MAX_LCHAN]; -}; - -/* One TRX in a BTS */ -struct gsm_bts_trx { - /* list header in bts->trx_list */ - struct llist_head list; - - struct gsm_bts *bts; - /* number of this TRX in the BTS */ - uint8_t nr; - /* human readable name / description */ - char *description; - /* how do we talk RSL with this TRX? */ - struct gsm_e1_subslot rsl_e1_link; - uint8_t rsl_tei; - struct e1inp_sign_link *rsl_link; - - /* Some BTS (specifically Ericsson RBS) have a per-TRX OML Link */ - struct e1inp_sign_link *oml_link; - - struct gsm_abis_mo mo; - struct tlv_parsed nm_attr; - struct { - struct gsm_abis_mo mo; - } bb_transc; - - uint16_t arfcn; - int nominal_power; /* in dBm */ - unsigned int max_power_red; /* in actual dB */ - -#ifndef ROLE_BSC - struct trx_power_params power_params; - int ms_power_control; - - struct { - void *l1h; - } role_bts; -#endif - - union { - struct { - struct { - struct gsm_abis_mo mo; - } bbsig; - struct { - struct gsm_abis_mo mo; - } pa; - } bs11; - struct { - unsigned int test_state; - uint8_t test_nr; - struct rxlev_stats rxlev_stat; - } ipaccess; - struct { - struct { - struct om2k_mo om2k_mo; - } trxc; - struct { - struct om2k_mo om2k_mo; - } rx; - struct { - struct om2k_mo om2k_mo; - } tx; - } rbs2000; - }; - struct gsm_bts_trx_ts ts[TRX_NR_TS]; -}; - -#define GSM_BTS_SI2Q(bts, i) (struct gsm48_system_information_type_2quater *)((bts)->si_buf[SYSINFO_TYPE_2quater][i]) -#define GSM_BTS_HAS_SI(bts, i) ((bts)->si_valid & (1 << i)) -#define GSM_BTS_SI(bts, i) (void *)((bts)->si_buf[i][0]) -#define GSM_LCHAN_SI(lchan, i) (void *)((lchan)->si.buf[i][0]) - -enum gsm_bts_type { - GSM_BTS_TYPE_UNKNOWN, - GSM_BTS_TYPE_BS11, - GSM_BTS_TYPE_NANOBTS, - GSM_BTS_TYPE_RBS2000, - GSM_BTS_TYPE_NOKIA_SITE, - GSM_BTS_TYPE_OSMOBTS, - _NUM_GSM_BTS_TYPE -}; - -enum gsm_bts_type_variant { - BTS_UNKNOWN, - BTS_OSMO_LITECELL15, - BTS_OSMO_OCTPHY, - BTS_OSMO_SYSMO, - BTS_OSMO_TRX, - _NUM_BTS_VARIANT -}; - -/* Used by OML layer for BTS Attribute reporting */ -enum bts_attribute { - BTS_TYPE_VARIANT, - BTS_SUB_MODEL, - TRX_PHY_VERSION, -}; - -struct vty; - -struct gsm_bts_model { - struct llist_head list; - - enum gsm_bts_type type; - enum gsm_bts_type_variant variant; - const char *name; - - bool started; - int (*start)(struct gsm_network *net); - int (*oml_rcvmsg)(struct msgb *msg); - - void (*e1line_bind_ops)(struct e1inp_line *line); - - void (*config_write_bts)(struct vty *vty, struct gsm_bts *bts); - void (*config_write_trx)(struct vty *vty, struct gsm_bts_trx *trx); - void (*config_write_ts)(struct vty *vty, struct gsm_bts_trx_ts *ts); - - struct tlv_definition nm_att_tlvdef; - - /* features of a given BTS model set via gsm_bts_model_register() locally */ - struct bitvec features; - uint8_t _features_data[MAX_BTS_FEATURES/8]; -}; - -/* N. B: always add new features to the end of the list (right before _NUM_BTS_FEAT) to avoid breaking compatibility - with BTS compiled against earlier version of this header */ -enum gsm_bts_features { - BTS_FEAT_HSCSD, - BTS_FEAT_GPRS, - BTS_FEAT_EGPRS, - BTS_FEAT_ECSD, - BTS_FEAT_HOPPING, - BTS_FEAT_MULTI_TSC, - BTS_FEAT_OML_ALERTS, - BTS_FEAT_AGCH_PCH_PROP, - BTS_FEAT_CBCH, - _NUM_BTS_FEAT -}; - -extern const struct value_string gsm_bts_features_descs[]; - -/* - * This keeps track of the paging status of one BTS. It - * includes a number of pending requests, a back pointer - * to the gsm_bts, a timer and some more state. - */ -struct gsm_bts_paging_state { - /* pending requests */ - struct llist_head pending_requests; - struct gsm_bts *bts; - - struct osmo_timer_list work_timer; - struct osmo_timer_list credit_timer; - - /* free chans needed */ - int free_chans_need; - - /* load */ - uint16_t available_slots; -}; - -struct gsm_envabtse { - struct gsm_abis_mo mo; -}; - -struct gsm_bts_gprs_nsvc { - struct gsm_bts *bts; - /* data read via VTY config file, to configure the BTS - * via OML from BSC */ - int id; - uint16_t nsvci; - uint16_t local_port; /* on the BTS */ - uint16_t remote_port; /* on the SGSN */ - uint32_t remote_ip; /* on the SGSN */ - - struct gsm_abis_mo mo; -}; - -enum gprs_rlc_par { - RLC_T3142, - RLC_T3169, - RLC_T3191, - RLC_T3193, - RLC_T3195, - RLC_N3101, - RLC_N3103, - RLC_N3105, - CV_COUNTDOWN, - T_DL_TBF_EXT, /* ms */ - T_UL_TBF_EXT, /* ms */ - _NUM_RLC_PAR -}; - -enum gprs_cs { - GPRS_CS1, - GPRS_CS2, - GPRS_CS3, - GPRS_CS4, - GPRS_MCS1, - GPRS_MCS2, - GPRS_MCS3, - GPRS_MCS4, - GPRS_MCS5, - GPRS_MCS6, - GPRS_MCS7, - GPRS_MCS8, - GPRS_MCS9, - _NUM_GRPS_CS -}; - -struct gprs_rlc_cfg { - uint16_t parameter[_NUM_RLC_PAR]; - struct { - uint16_t repeat_time; /* ms */ - uint8_t repeat_count; - } paging; - uint32_t cs_mask; /* bitmask of gprs_cs */ - uint8_t initial_cs; - uint8_t initial_mcs; -}; - - -enum neigh_list_manual_mode { - NL_MODE_AUTOMATIC = 0, - NL_MODE_MANUAL = 1, - NL_MODE_MANUAL_SI5SEP = 2, /* SI2 and SI5 have separate neighbor lists */ -}; - -enum bts_loc_fix { - BTS_LOC_FIX_INVALID = 0, - BTS_LOC_FIX_2D = 1, - BTS_LOC_FIX_3D = 2, -}; - -extern const struct value_string bts_loc_fix_names[]; - -struct bts_location { - struct llist_head list; - time_t tstamp; - enum bts_loc_fix valid; - double lat; - double lon; - double height; -}; - -/* One BTS */ -struct gsm_bts { - /* list header in net->bts_list */ - struct llist_head list; - - /* Geographical location of the BTS */ - struct llist_head loc_list; - - /* number of ths BTS in network */ - uint8_t nr; - /* human readable name / description */ - char *description; - /* Cell Identity */ - uint16_t cell_identity; - /* location area code of this BTS */ - uint16_t location_area_code; - /* Base Station Identification Code (BSIC), lower 3 bits is BCC, - * which is used as TSC for the CCCH */ - uint8_t bsic; - /* type of BTS */ - enum gsm_bts_type type; - enum gsm_bts_type_variant variant; - struct gsm_bts_model *model; - enum gsm_band band; - char version[MAX_VERSION_LENGTH]; - char sub_model[MAX_VERSION_LENGTH]; - - /* features of a given BTS set/reported via OML */ - struct bitvec features; - uint8_t _features_data[MAX_BTS_FEATURES/8]; - - /* Connected PCU version (if any) */ - char pcu_version[MAX_VERSION_LENGTH]; - - /* maximum Tx power that the MS is permitted to use in this cell */ - int ms_max_power; - - /* how do we talk OML with this TRX? */ - struct gsm_e1_subslot oml_e1_link; - uint8_t oml_tei; - struct e1inp_sign_link *oml_link; - - /* Abis network management O&M handle */ - struct abis_nm_h *nmh; - - struct gsm_abis_mo mo; - - /* number of this BTS on given E1 link */ - uint8_t bts_nr; - - /* DTX features of this BTS */ - enum gsm48_dtx_mode dtxu; - bool dtxd; - - /* paging state and control */ - struct gsm_bts_paging_state paging; - - /* CCCH is on C0 */ - struct gsm_bts_trx *c0; - - struct { - struct gsm_abis_mo mo; - } site_mgr; - - /* bitmask of all SI that are present/valid in si_buf */ - uint32_t si_valid; - /* 3GPP TS 44.018 Table 10.5.2.33b.1 INDEX and COUNT for SI2quater */ - uint8_t si2q_index; /* distinguish individual SI2quater messages */ - uint8_t si2q_count; /* si2q_index for the last (highest indexed) individual SI2quater message */ - /* buffers where we put the pre-computed SI */ - sysinfo_buf_t si_buf[_MAX_SYSINFO_TYPE][SI2Q_MAX_NUM]; - /* offsets used while generating SI2quater */ - size_t e_offset; - size_t u_offset; - - /* ip.accesss Unit ID's have Site/BTS/TRX layout */ - union { - struct { - uint16_t site_id; - uint16_t bts_id; - uint32_t flags; - uint32_t rsl_ip; - } ip_access; - struct { - struct { - struct gsm_abis_mo mo; - } cclk; - struct { - struct gsm_abis_mo mo; - } rack; - struct gsm_envabtse envabtse[4]; - } bs11; - struct { - struct { - struct om2k_mo om2k_mo; - struct gsm_abis_mo mo; - struct llist_head conn_groups; - } cf; - struct { - struct om2k_mo om2k_mo; - struct gsm_abis_mo mo; - struct llist_head conn_groups; - } is; - struct { - struct om2k_mo om2k_mo; - struct gsm_abis_mo mo; - struct llist_head conn_groups; - } con; - struct { - struct om2k_mo om2k_mo; - struct gsm_abis_mo mo; - } dp; - struct { - struct om2k_mo om2k_mo; - struct gsm_abis_mo mo; - } tf; - uint32_t use_superchannel:1; - } rbs2000; - struct { - uint8_t bts_type; - unsigned int configured:1, - skip_reset:1, - no_loc_rel_cnf:1, - bts_reset_timer_cnf, - did_reset:1, - wait_reset:1; - struct osmo_timer_list reset_timer; - } nokia; - }; - - /* Not entirely sure how ip.access specific this is */ - struct { - uint8_t supports_egprs_11bit_rach; - enum bts_gprs_mode mode; - struct { - struct gsm_abis_mo mo; - uint16_t nsei; - uint8_t timer[7]; - } nse; - struct { - struct gsm_abis_mo mo; - uint16_t bvci; - uint8_t timer[11]; - struct gprs_rlc_cfg rlc_cfg; - } cell; - struct gsm_bts_gprs_nsvc nsvc[2]; - uint8_t rac; - uint8_t net_ctrl_ord; - bool ctrl_ack_type_use_block; - } gprs; - - /* RACH NM values */ - int rach_b_thresh; - int rach_ldavg_slots; - - /* transceivers */ - int num_trx; - struct llist_head trx_list; - - /* SI related items */ - int force_combined_si; - int bcch_change_mark; - -#ifdef ROLE_BSC - /* Abis NM queue */ - struct llist_head abis_queue; - int abis_nm_pend; - - struct gsm_network *network; - - /* should the channel allocator allocate channels from high TRX to TRX0, - * rather than starting from TRX0 and go upwards? */ - int chan_alloc_reverse; - - enum neigh_list_manual_mode neigh_list_manual_mode; - /* parameters from which we build SYSTEM INFORMATION */ - struct { - struct gsm48_rach_control rach_control; - uint8_t ncc_permitted; - struct gsm48_cell_sel_par cell_sel_par; - struct gsm48_si_selection_params cell_ro_sel_par; /* rest octet */ - struct gsm48_cell_options cell_options; - struct gsm48_control_channel_descr chan_desc; - struct bitvec neigh_list; - struct bitvec cell_alloc; - struct bitvec si5_neigh_list; - struct osmo_earfcn_si2q si2quater_neigh_list; - size_t uarfcn_length; /* index for uarfcn and scramble lists */ - struct { - /* bitmask large enough for all possible ARFCN's */ - uint8_t neigh_list[1024/8]; - uint8_t cell_alloc[1024/8]; - /* If the user wants a different neighbor list in SI5 than in SI2 */ - uint8_t si5_neigh_list[1024/8]; - uint8_t meas_bw_list[MAX_EARFCN_LIST]; - uint16_t earfcn_list[MAX_EARFCN_LIST]; - uint16_t uarfcn_list[MAX_EARFCN_LIST]; - uint16_t scramble_list[MAX_EARFCN_LIST]; - } data; - } si_common; - bool early_classmark_allowed; - /* for testing only: Have an infinitely long radio link timeout */ - bool infinite_radio_link_timeout; - - /* do we use static (user-defined) system information messages? (bitmask) */ - uint32_t si_mode_static; - - /* exclude the BTS from the global RF Lock handling */ - int excl_from_rf_lock; - - /* supported codecs beside FR */ - struct bts_codec_conf codec; - - /* BTS dependencies bit field */ - uint32_t depends_on[256/(8*4)]; - - /* full and half rate multirate config */ - struct amr_multirate_conf mr_full; - struct amr_multirate_conf mr_half; - - /* PCU socket state */ - char *pcu_sock_path; - struct pcu_sock_state *pcu_state; - -#endif /* ROLE_BSC */ - void *role; -}; - - -struct gsm_bts *gsm_bts_alloc(void *talloc_ctx); -struct gsm_bts *gsm_bts_num(struct gsm_network *net, int num); - -struct gsm_bts_trx *gsm_bts_trx_alloc(struct gsm_bts *bts); -struct gsm_bts_trx *gsm_bts_trx_num(const struct gsm_bts *bts, int num); - -enum gsm_bts_type str2btstype(const char *arg); -const char *btstype2str(enum gsm_bts_type type); - -enum bts_attribute str2btsattr(const char *s); -const char *btsatttr2str(enum bts_attribute v); - -enum gsm_bts_type_variant str2btsvariant(const char *arg); -const char *btsvariant2str(enum gsm_bts_type_variant v); - -extern const struct value_string gsm_chreq_descs[]; -const struct value_string gsm_pchant_names[13]; -const struct value_string gsm_pchant_descs[13]; -const char *gsm_pchan_name(enum gsm_phys_chan_config c); -enum gsm_phys_chan_config gsm_pchan_parse(const char *name); -const char *gsm_lchant_name(enum gsm_chan_t c); -const char *gsm_chreq_name(enum gsm_chreq_reason_t c); -char *gsm_trx_name(const struct gsm_bts_trx *trx); -char *gsm_ts_name(const struct gsm_bts_trx_ts *ts); -char *gsm_ts_and_pchan_name(const struct gsm_bts_trx_ts *ts); -char *gsm_lchan_name_compute(const struct gsm_lchan *lchan); -const char *gsm_lchans_name(enum gsm_lchan_state s); - -static inline char *gsm_lchan_name(const struct gsm_lchan *lchan) -{ - return lchan->name; -} - -static inline int gsm_bts_set_feature(struct gsm_bts *bts, enum gsm_bts_features feat) -{ - OSMO_ASSERT(_NUM_BTS_FEAT < MAX_BTS_FEATURES); - return bitvec_set_bit_pos(&bts->features, feat, 1); -} - -static inline bool gsm_bts_has_feature(const struct gsm_bts *bts, enum gsm_bts_features feat) -{ - OSMO_ASSERT(_NUM_BTS_FEAT < MAX_BTS_FEATURES); - return bitvec_get_bit_pos(&bts->features, feat); -} - -void gsm_abis_mo_reset(struct gsm_abis_mo *mo); - -struct gsm_abis_mo * -gsm_objclass2mo(struct gsm_bts *bts, uint8_t obj_class, - const struct abis_om_obj_inst *obj_inst); - -struct gsm_nm_state * -gsm_objclass2nmstate(struct gsm_bts *bts, uint8_t obj_class, - const struct abis_om_obj_inst *obj_inst); -void * -gsm_objclass2obj(struct gsm_bts *bts, uint8_t obj_class, - const struct abis_om_obj_inst *obj_inst); - -/* reset the state of all MO in the BTS */ -void gsm_bts_mo_reset(struct gsm_bts *bts); - -uint8_t gsm_pchan2chan_nr(enum gsm_phys_chan_config pchan, - uint8_t ts_nr, uint8_t lchan_nr); -uint8_t gsm_lchan2chan_nr(const struct gsm_lchan *lchan); -uint8_t gsm_lchan_as_pchan2chan_nr(const struct gsm_lchan *lchan, - enum gsm_phys_chan_config as_pchan); - -/* return the gsm_lchan for the CBCH (if it exists at all) */ -struct gsm_lchan *gsm_bts_get_cbch(struct gsm_bts *bts); - -/* - * help with parsing regexps - */ -int gsm_parse_reg(void *ctx, regex_t *reg, char **str, - int argc, const char **argv) __attribute__ ((warn_unused_result)); - -static inline uint8_t gsm_ts_tsc(const struct gsm_bts_trx_ts *ts) -{ - if (ts->tsc != -1) - return ts->tsc; - else - return ts->trx->bts->bsic & 7; -} - -struct gsm_lchan *rsl_lchan_lookup(struct gsm_bts_trx *trx, uint8_t chan_nr, - int *rc); - -enum gsm_phys_chan_config ts_pchan(struct gsm_bts_trx_ts *ts); -uint8_t ts_subslots(struct gsm_bts_trx_ts *ts); -bool ts_is_tch(struct gsm_bts_trx_ts *ts); - -#endif diff --git a/openbsc/include/openbsc/gsm_subscriber.h b/openbsc/include/openbsc/gsm_subscriber.h deleted file mode 100644 index 7e656145..00000000 --- a/openbsc/include/openbsc/gsm_subscriber.h +++ /dev/null @@ -1,130 +0,0 @@ -#ifndef _GSM_SUBSCR_H -#define _GSM_SUBSCR_H - -#include - -#include -#include - -#include - -#define GSM_NAME_LENGTH 160 - -#define GSM_EXTENSION_LENGTH 15 /* MSISDN can only be 15 digits length */ -#define GSM_MIN_EXTEN 20000 -#define GSM_MAX_EXTEN 49999 - -#define GSM_SUBSCRIBER_FIRST_CONTACT 0x00000001 -/* gprs_sgsn.h defines additional flags including and above bit 16 (0x10000) */ - -#define GSM_SUBSCRIBER_NO_EXPIRATION 0x0 - -struct vty; -struct sgsn_mm_ctx; -struct sgsn_subscriber_data; - -struct subscr_request; - -struct gsm_subscriber_group { - struct gsm_network *net; - - int keep_subscr; -}; - -struct gsm_equipment { - long long unsigned int id; - char imei[GSM23003_IMEISV_NUM_DIGITS+1]; - char name[GSM_NAME_LENGTH]; - - struct gsm48_classmark1 classmark1; - uint8_t classmark2_len; - uint8_t classmark2[3]; - uint8_t classmark3_len; - uint8_t classmark3[14]; -}; - -struct gsm_subscriber { - struct gsm_subscriber_group *group; - long long unsigned int id; - char imsi[GSM23003_IMSI_MAX_DIGITS+1]; - uint32_t tmsi; - uint16_t lac; - char name[GSM_NAME_LENGTH]; - char extension[GSM_EXTENSION_LENGTH]; - int authorized; - time_t expire_lu; - - /* Don't delete subscribers even if group->keep_subscr is not set */ - int keep_in_ram; - - /* Temporary field which is not stored in the DB/HLR */ - uint32_t flags; - - /* Every user can only have one equipment in use at any given - * point in time */ - struct gsm_equipment equipment; - - /* for internal management */ - int use_count; - struct llist_head entry; - - /* pending requests */ - int is_paging; - struct llist_head requests; - - /* GPRS/SGSN related fields */ - struct sgsn_subscriber_data *sgsn_data; -}; - -enum gsm_subscriber_field { - GSM_SUBSCRIBER_IMSI, - GSM_SUBSCRIBER_TMSI, - GSM_SUBSCRIBER_EXTENSION, - GSM_SUBSCRIBER_ID, -}; - -enum gsm_subscriber_update_reason { - GSM_SUBSCRIBER_UPDATE_ATTACHED, - GSM_SUBSCRIBER_UPDATE_DETACHED, - GSM_SUBSCRIBER_UPDATE_EQUIPMENT, -}; - -struct gsm_subscriber *subscr_get(struct gsm_subscriber *subscr); -struct gsm_subscriber *subscr_put(struct gsm_subscriber *subscr); -struct gsm_subscriber *subscr_create_subscriber(struct gsm_subscriber_group *sgrp, - const char *imsi); -struct gsm_subscriber *subscr_get_by_tmsi(struct gsm_subscriber_group *sgrp, - uint32_t tmsi); -struct gsm_subscriber *subscr_get_by_imsi(struct gsm_subscriber_group *sgrp, - const char *imsi); -struct gsm_subscriber *subscr_get_by_extension(struct gsm_subscriber_group *sgrp, - const char *ext); -struct gsm_subscriber *subscr_get_by_id(struct gsm_subscriber_group *sgrp, - unsigned long long id); -struct gsm_subscriber *subscr_get_or_create(struct gsm_subscriber_group *sgrp, - const char *imsi); -int subscr_update(struct gsm_subscriber *s, struct gsm_bts *bts, int reason); -struct gsm_subscriber *subscr_active_by_tmsi(struct gsm_subscriber_group *sgrp, - uint32_t tmsi); -struct gsm_subscriber *subscr_active_by_imsi(struct gsm_subscriber_group *sgrp, - const char *imsi); - -char *subscr_name(struct gsm_subscriber *subscr); - -int subscr_purge_inactive(struct gsm_subscriber_group *sgrp); -void subscr_update_from_db(struct gsm_subscriber *subscr); -void subscr_expire(struct gsm_subscriber_group *sgrp); -int subscr_update_expire_lu(struct gsm_subscriber *subscr, struct gsm_bts *bts); - -/* - * Paging handling with authentication - */ -struct subscr_request *subscr_request_channel(struct gsm_subscriber *subscr, - int type, gsm_cbfn *cbfn, void *param); -void subscr_remove_request(struct subscr_request *req); - -/* internal */ -struct gsm_subscriber *subscr_alloc(void); -extern struct llist_head active_subscribers; - -#endif /* _GSM_SUBSCR_H */ diff --git a/openbsc/include/openbsc/gsup_client.h b/openbsc/include/openbsc/gsup_client.h deleted file mode 100644 index a113225e..00000000 --- a/openbsc/include/openbsc/gsup_client.h +++ /dev/null @@ -1,60 +0,0 @@ -/* GPRS Subscriber Update Protocol client */ - -/* (C) 2014 by Sysmocom s.f.m.c. GmbH - * All Rights Reserved - * - * Author: Jacob Erlbeck - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation; either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - * - */ -#pragma once - -#include - -#include - -#define GSUP_CLIENT_RECONNECT_INTERVAL 10 -#define GSUP_CLIENT_PING_INTERVAL 20 - -struct msgb; -struct ipa_client_conn; -struct gsup_client; - -/* Expects message in msg->l2h */ -typedef int (*gsup_client_read_cb_t)(struct gsup_client *gsupc, - struct msgb *msg); - -struct gsup_client { - struct ipa_client_conn *link; - gsup_client_read_cb_t read_cb; - void *data; - - struct oap_client_state oap_state; - - struct osmo_timer_list ping_timer; - struct osmo_timer_list connect_timer; - int is_connected; - int got_ipa_pong; -}; - -struct gsup_client *gsup_client_create(const char *ip_addr, - unsigned int tcp_port, - gsup_client_read_cb_t read_cb, - struct oap_client_config *oap_config); - -void gsup_client_destroy(struct gsup_client *gsupc); -int gsup_client_send(struct gsup_client *gsupc, struct msgb *msg); -struct msgb *gsup_client_msgb_alloc(void); - diff --git a/openbsc/include/openbsc/gtphub.h b/openbsc/include/openbsc/gtphub.h deleted file mode 100644 index 9cb7605f..00000000 --- a/openbsc/include/openbsc/gtphub.h +++ /dev/null @@ -1,523 +0,0 @@ -/* GTP Hub Implementation */ - -/* (C) 2015 by sysmocom s.f.m.c. GmbH - * All Rights Reserved - * - * Author: Neels Hofmeyr - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation; either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - */ - -#pragma once - -#include -#include - -#include -#include -#include - -#include - - -/* support */ - -/* TODO move to osmocom/core/socket.c ? */ -#include /* for IPPROTO_* etc */ -struct osmo_sockaddr { - struct sockaddr_storage a; - socklen_t l; -}; - -/* TODO move to osmocom/core/socket.c ? */ -/*! \brief Initialize a sockaddr - * \param[out] addr Valid osmo_sockaddr pointer to write result to - * \param[in] family Address Family like AF_INET, AF_INET6, AF_UNSPEC - * \param[in] type Socket type like SOCK_DGRAM, SOCK_STREAM - * \param[in] proto Protocol like IPPROTO_TCP, IPPROTO_UDP - * \param[in] host Remote host name or IP address in string form - * \param[in] port Remote port number in host byte order - * \returns 0 on success, otherwise an error code (from getaddrinfo()). - * - * Copy the first result from a getaddrinfo() call with the given parameters to - * *addr and *addr_len. On error, do not change *addr and return nonzero. - */ -int osmo_sockaddr_init(struct osmo_sockaddr *addr, - uint16_t family, uint16_t type, uint8_t proto, - const char *host, uint16_t port); - -/* Conveniently pass AF_UNSPEC, SOCK_DGRAM and IPPROTO_UDP to - * osmo_sockaddr_init(). */ -static inline int osmo_sockaddr_init_udp(struct osmo_sockaddr *addr, - const char *host, uint16_t port) -{ - return osmo_sockaddr_init(addr, AF_UNSPEC, SOCK_DGRAM, IPPROTO_UDP, - host, port); -} - -/*! \brief convert sockaddr to human readable string. - * \param[out] addr_str Valid pointer to a buffer of length addr_str_len. - * \param[in] addr_str_len Size of buffer addr_str points at. - * \param[out] port_str Valid pointer to a buffer of length port_str_len. - * \param[in] port_str_len Size of buffer port_str points at. - * \param[in] addr Binary representation as returned by osmo_sockaddr_init(). - * \param[in] flags flags as passed to getnameinfo(). - * \returns 0 on success, an error code on error. - * - * Return the IPv4 or IPv6 address string and the port (a.k.a. service) string - * representations of the given struct osmo_sockaddr in two caller provided - * char buffers. Flags of (NI_NUMERICHOST | NI_NUMERICSERV) return numeric - * address and port. Either one of addr_str or port_str may be NULL, in which - * case nothing is returned there. - * - * See also osmo_sockaddr_to_str() (less flexible, but much more convenient). */ -int osmo_sockaddr_to_strs(char *addr_str, size_t addr_str_len, - char *port_str, size_t port_str_len, - const struct osmo_sockaddr *addr, - int flags); - - -/*! \brief concatenate the parts returned by osmo_sockaddr_to_strs(). - * \param[in] addr Binary representation as returned by osmo_sockaddr_init(). - * \param[in] buf A buffer to use for string operations. - * \param[in] buf_len Length of the buffer. - * \returns Address string (in buffer). - * - * Compose a string of the numeric IP-address and port represented by *addr of - * the form " port ". The returned string is valid until the - * next invocation of this function. - */ -const char *osmo_sockaddr_to_strb(const struct osmo_sockaddr *addr, - char *buf, size_t buf_len); - -/*! \brief conveniently return osmo_sockaddr_to_strb() in a static buffer. - * \param[in] addr Binary representation as returned by osmo_sockaddr_init(). - * \returns Address string in static buffer. - * - * See osmo_sockaddr_to_strb(). - * - * Note: only one osmo_sockaddr_to_str() call will work per print/log - * statement. For two or more, use osmo_sockaddr_to_strb() with a separate - * buffer each. - */ -const char *osmo_sockaddr_to_str(const struct osmo_sockaddr *addr); - -/*! \brief compare two osmo_sockaddr. - * \param[in] a The first address to compare. - * \param[in] b The other address to compare. - * \returns 0 if equal, otherwise -1 or 1. - */ -int osmo_sockaddr_cmp(const struct osmo_sockaddr *a, - const struct osmo_sockaddr *b); - -/*! \brief Overwrite *dst with *src. - * Like memcpy(), but copy only the valid bytes. */ -void osmo_sockaddr_copy(struct osmo_sockaddr *dst, - const struct osmo_sockaddr *src); - - -/* general */ - -enum gtphub_plane_idx { - GTPH_PLANE_CTRL = 0, - GTPH_PLANE_USER = 1, - GTPH_PLANE_N -}; - -enum gtphub_side_idx { - GTPH_SIDE_SGSN = 0, - GTPH_SIDE_GGSN = 1, - GTPH_SIDE_N -}; - -#define for_each_side(I) for (I = 0; I < GTPH_SIDE_N; I++) -#define for_each_plane(I) for (I = 0; I < GTPH_PLANE_N; I++) -#define for_each_side_and_plane(I,J) for_each_side(I) for_each_plane(J) - -static inline int other_side_idx(int side_idx) -{ - return (side_idx + 1) & 1; -} - -extern const char* const gtphub_plane_idx_names[GTPH_PLANE_N]; -extern const uint16_t gtphub_plane_idx_default_port[GTPH_PLANE_N]; - -extern const char* const gtphub_side_idx_names[GTPH_SIDE_N]; - -/* A host address in the form that is expected in the 7.7.32 GSN Address IE. - * len is either 4 (IPv4) or 16 (IPv6), any other value is invalid. If no - * address is set, len shall be 0. */ -struct gsn_addr { - uint16_t len; - uint8_t buf[16]; -}; - -void gsn_addr_copy(struct gsn_addr *gsna, const struct gsn_addr *src); -int gsn_addr_from_str(struct gsn_addr *gsna, const char *numeric_addr_str); - -/* Return gsna in numeric string form, in a static buffer. */ -const char *gsn_addr_to_str(const struct gsn_addr *gsna); - -/* note: strbuf_len doesn't need to be larger than INET6_ADDRSTRLEN + 1. */ -const char *gsn_addr_to_strb(const struct gsn_addr *gsna, - char *strbuf, int strbuf_len); - -/* Return 1 on match, zero otherwise. */ -int gsn_addr_same(const struct gsn_addr *a, const struct gsn_addr *b); - -/* Decode sa to gsna. Return 0 on success. If port is non-NULL, the port number - * from sa is also returned. */ -int gsn_addr_from_sockaddr(struct gsn_addr *gsna, uint16_t *port, - const struct osmo_sockaddr *sa); - -/* expiry */ - -struct expiring_item; -typedef void (*del_cb_t)(struct expiring_item *); - -struct expiring_item { - struct llist_head entry; - time_t expiry; - del_cb_t del_cb; -}; - -struct expiry { - int expiry_in_seconds; - struct llist_head items; -}; - -/* Initialize an expiry queue. */ -void expiry_init(struct expiry *exq, int expiry_in_seconds); - -/* Add a new mapping, or restart the expiry timeout for an already listed - * mapping. */ -void expiry_add(struct expiry *exq, struct expiring_item *item, time_t now); - -/* Initialize to all-empty; must be called before using the item in any way. */ -void expiring_item_init(struct expiring_item *item); - -/* Remove the given item from its expiry queue, and call item->del_cb, if set. - * This sets item->del_cb to NULL and is harmless when run a second time on the - * same item, so the del_cb may choose to call this function, too, to allow - * deleting items from several code paths. */ -void expiring_item_del(struct expiring_item *item); - -/* Carry out due expiry of mappings. Must be invoked regularly. - * 'now' is the current clock count in seconds and must correspond to the clock - * count passed to nr_map_add(). A monotonous clock counter should be used. */ -int expiry_tick(struct expiry *exq, time_t now); - -/* Expire all items. */ -void expiry_clear(struct expiry *exq); - - -/* number map */ - -/* A number map assigns a "random" mapped number to each user provided number. - * If the same number is requested multiple times, the same mapped number is - * returned. - * - * Number maps plug into possibly shared pools and expiry queues, for example: - * - * mapA -----------+-> pool1 <-+-- mapB - * {10->1, 11->5} | {1, 2, 3, ...} | {10->2, 11->3} - * | | - * | | - * /-> \-> expiry1 <-/ - * | (30 seconds) - * | - * mapC -------+-----> pool2 <-+-- mapD - * {10->1, 11->3} {1, 2, 3, ...} | {10->2, 11->5} - * | - * expiry2 <-/ - * (60 seconds) - * - * A map contains mappings ("10->1"). Each map needs a number pool, which can - * be shared with other maps. Each new mapping receives a number from the pool, - * which is then unavailable to any other map using the same pool. - * - * A map may point at an expiry queue, in which case all mappings added to it - * are also appended to the expiry queue (using a separate llist entry in the - * mapping). Any number of maps may submit to the same expiry queue, if they - * desire the same expiry timeout. An expiry queue stores the mappings in - * chronological order, so that expiry checking is needed only from the start - * of the queue; hence only mappings with identical expiry timeout can be added - * to the same expiry queue. Upon expiry, a mapping is dropped from the map it - * was submitted at. expiry_tick() needs to be called regularly for each expiry - * queue. - * - * A nr_mapping can be embedded in a larger struct: each mapping can have a - * distinct destructor (del_cb), and each del_cb can figure out the container - * struct's address and free that upon expiry or manual deletion. So in expiry - * queues (and even maps), mappings of different container types can be mixed. - * This can help to drastically reduce the amount of unnecessary visits during - * expiry checking, for the case that no expiry is pending. An expiry queue - * always knows which mappings to expire next, because they are right at the - * start of its list. - * - * Mapping allocation and a del_cb are provided by the caller. If del_cb is - * NULL, no deallocation will be done (allowing statically allocated entries). - */ - -typedef unsigned int nr_t; - -/* Generator for unused numbers. So far this counts upwards from zero, but the - * implementation may change in the future. Treat this like an opaque struct. - * If this becomes random, the tests need to be fixed. */ -struct nr_pool { - nr_t last_nr; - nr_t nr_min; - nr_t nr_max; -}; - -struct nr_mapping { - struct llist_head entry; - struct expiring_item expiry_entry; - - void *origin; - nr_t orig; - nr_t repl; -}; - -struct nr_map { - struct nr_pool *pool; /* multiple nr_maps can share a nr_pool. */ - struct expiry *add_items_to_expiry; - struct llist_head mappings; -}; - - -void nr_pool_init(struct nr_pool *pool, nr_t nr_min, nr_t nr_max); - -/* Return the next unused number from the nr_pool. */ -nr_t nr_pool_next(struct nr_pool *pool); - -/* Initialize the nr_mapping to zero/empty values. */ -void nr_mapping_init(struct nr_mapping *mapping); - -/* Remove the given mapping from its parent map and expiry queue, and call - * mapping->del_cb, if set. */ -void nr_mapping_del(struct nr_mapping *mapping); - -/* Initialize an (already allocated) nr_map, and set the map's number pool. - * Multiple nr_map instances may use the same nr_pool. Set the nr_map's expiry - * queue to exq, so that all added mappings are automatically expired after the - * time configured in exq. exq may be NULL to disable automatic expiry. */ -void nr_map_init(struct nr_map *map, struct nr_pool *pool, - struct expiry *exq); - -/* Add a new entry to the map. mapping->orig, mapping->origin and - * mapping->del_cb must be set before calling this function. The remaining - * fields of *mapping will be overwritten. mapping->repl is set to the next - * available mapped number from map->pool. 'now' is the current clock count in - * seconds; if no map->expiry is used, just pass 0 for 'now'. */ -void nr_map_add(struct nr_map *map, struct nr_mapping *mapping, - time_t now); - -/* Restart the timeout for the given mapping. mapping must be a member of map. - */ -void nr_map_refresh(struct nr_map *map, struct nr_mapping *mapping, - time_t now); - -/* Return a known mapping from nr_orig and the given origin. If nr_orig is - * unknown, return NULL. */ -struct nr_mapping *nr_map_get(const struct nr_map *map, - void *origin, nr_t nr_orig); - -/* Return a known mapping to nr_repl. If nr_repl is unknown, return NULL. */ -struct nr_mapping *nr_map_get_inv(const struct nr_map *map, nr_t nr_repl); - -/* Remove all mappings from map. */ -void nr_map_clear(struct nr_map *map); - -/* Return 1 if map has no entries, 0 otherwise. */ -int nr_map_empty(const struct nr_map *map); - - -/* config */ - -static const int GTPH_EXPIRE_QUICKLY_SECS = 30; /* TODO is there a spec for this? */ -static const int GTPH_EXPIRE_SLOWLY_MINUTES = 6 * 60; /* TODO is there a spec for this? */ - -struct gtphub_cfg_addr { - const char *addr_str; - uint16_t port; -}; - -struct gtphub_cfg_bind { - struct gtphub_cfg_addr bind; -}; - -struct gtphub_cfg { - struct gtphub_cfg_bind to_gsns[GTPH_SIDE_N][GTPH_PLANE_N]; - struct gtphub_cfg_addr proxy[GTPH_SIDE_N][GTPH_PLANE_N]; - int sgsn_use_sender; /* Use sender, not GSN addr IE with std ports */ -}; - - -/* state */ - -struct gtphub_peer { - struct llist_head entry; - - struct llist_head addresses; /* Alternatives, not load balancing. */ - struct nr_pool seq_pool; - struct nr_map seq_map; -}; - -struct gtphub_peer_addr { - struct llist_head entry; - - struct gtphub_peer *peer; - struct gsn_addr addr; - struct llist_head ports; -}; - -struct gtphub_peer_port { - struct llist_head entry; - - struct gtphub_peer_addr *peer_addr; - uint16_t port; - unsigned int ref_count; /* references from other peers' seq_maps */ - struct osmo_sockaddr sa; /* a "cache" for (peer_addr->addr, port) */ - int last_restart_count; /* 0..255 = valid, all else means unknown */ - - struct rate_ctr_group *counters_io; -}; - -struct gtphub_tunnel_endpoint { - struct gtphub_peer_port *peer; - uint32_t tei_orig; /* from/to peer */ - - struct rate_ctr_group *counters_io; -}; - -struct gtphub_tunnel { - struct llist_head entry; - struct expiring_item expiry_entry; - - uint32_t tei_repl; /* unique TEI to replace peers' TEIs */ - struct gtphub_tunnel_endpoint endpoint[GTPH_SIDE_N][GTPH_PLANE_N]; -}; - -struct gtphub_bind { - struct gsn_addr local_addr; - uint16_t local_port; - struct osmo_fd ofd; - - /* list of struct gtphub_peer */ - struct llist_head peers; - - const char *label; /* For logging */ - struct rate_ctr_group *counters_io; -}; - -struct gtphub_resolved_ggsn { - struct llist_head entry; - struct expiring_item expiry_entry; - - /* The APN OI, the Operator Identifier, is the combined address, - * including parts of the IMSI and APN NI, and ending with ".gprs". */ - char apn_oi_str[GSM_APN_LENGTH]; - - /* Which address and port we resolved that to. */ - struct gtphub_peer_port *peer; -}; - -struct gtphub { - struct gtphub_bind to_gsns[GTPH_SIDE_N][GTPH_PLANE_N]; - - /* pointers to an entry of to_gsns[s][p].peers */ - struct gtphub_peer_port *proxy[GTPH_SIDE_N][GTPH_PLANE_N]; - - /* The TEI numbers will simply wrap and be reused, which will work out - * in practice. Problems would arise if one given peer maintained the - * same TEI for a time long enough for the TEI nr map to wrap an entire - * uint32_t; if a new TEI were mapped every second, this would take - * more than 100 years (in which a single given TEI must not time out) - * to cause a problem. */ - struct nr_pool tei_pool; - - struct llist_head tunnels; /* struct gtphub_tunnel */ - struct llist_head pending_deletes; /* opaque (gtphub.c) */ - - struct llist_head ggsn_lookups; /* opaque (gtphub_ares.c) */ - struct llist_head resolved_ggsns; /* struct gtphub_resolved_ggsn */ - - struct osmo_timer_list gc_timer; - struct expiry expire_quickly; - struct expiry expire_slowly; - - uint8_t restart_counter; - - int sgsn_use_sender; -}; - -struct gtp_packet_desc; - - -/* api */ - -int gtphub_vty_init(struct gtphub *global_hub, struct gtphub_cfg *global_cfg); -int gtphub_cfg_read(struct gtphub_cfg *cfg, const char *config_file); - -/* Initialize and start gtphub: bind to ports, run expiry timers. */ -int gtphub_start(struct gtphub *hub, struct gtphub_cfg *cfg, - uint8_t restart_counter); - -/* Close all sockets, expire all maps and peers and free all allocations. The - * struct is then unusable, unless gtphub_start() is run on it again. */ -void gtphub_stop(struct gtphub *hub); - -time_t gtphub_now(void); - -/* Remove expired items, empty peers, ... */ -void gtphub_gc(struct gtphub *hub, time_t now); - -/* Return the string of the first address for this peer. */ -const char *gtphub_peer_str(struct gtphub_peer *peer); - -/* Return a human readable description of tun in a static buffer. */ -const char *gtphub_tunnel_str(struct gtphub_tunnel *tun); - -/* Return 1 if all of tun's endpoints are fully established, 0 otherwise. */ -int gtphub_tunnel_complete(struct gtphub_tunnel *tun); - -int gtphub_handle_buf(struct gtphub *hub, - unsigned int side_idx, - unsigned int port_idx, - const struct osmo_sockaddr *from_addr, - uint8_t *buf, - size_t received, - time_t now, - uint8_t **reply_buf, - struct osmo_fd **to_ofd, - struct osmo_sockaddr *to_addr); - -struct gtphub_peer_port *gtphub_port_have(struct gtphub *hub, - struct gtphub_bind *bind, - const struct gsn_addr *addr, - uint16_t port); - -struct gtphub_peer_port *gtphub_port_find_sa(const struct gtphub_bind *bind, - const struct osmo_sockaddr *addr); - -void gtphub_resolved_ggsn(struct gtphub *hub, const char *apn_oi_str, - struct gsn_addr *resolved_addr, - time_t now); - -const char *gtphub_port_str(struct gtphub_peer_port *port); - -int gtphub_write(const struct osmo_fd *to, - const struct osmo_sockaddr *to_addr, - const uint8_t *buf, size_t buf_len); diff --git a/openbsc/include/openbsc/handover.h b/openbsc/include/openbsc/handover.h deleted file mode 100644 index 3fe71a28..00000000 --- a/openbsc/include/openbsc/handover.h +++ /dev/null @@ -1,14 +0,0 @@ -#ifndef _HANDOVER_H -#define _HANDOVER_H - -struct gsm_subscriber_connection; - -int bsc_handover_start(struct gsm_lchan *old_lchan, struct gsm_bts *bts); - -/* clear any operation for this connection */ -void bsc_clear_handover(struct gsm_subscriber_connection *conn, int free_lchan); - -/* Return the old lchan or NULL. This is meant for audio handling */ -struct gsm_lchan *bsc_handover_pending(struct gsm_lchan *new_lchan); - -#endif /* _HANDOVER_H */ diff --git a/openbsc/include/openbsc/handover_decision.h b/openbsc/include/openbsc/handover_decision.h deleted file mode 100644 index 81078b05..00000000 --- a/openbsc/include/openbsc/handover_decision.h +++ /dev/null @@ -1,7 +0,0 @@ -#ifndef _HANDOVER_DECISION_H -#define _HANDOVER_DECISION_H - -void on_dso_load_ho_dec(void); - -#endif /* _HANDOVER_DECISION_H */ - diff --git a/openbsc/include/openbsc/ipaccess.h b/openbsc/include/openbsc/ipaccess.h deleted file mode 100644 index 82e89c27..00000000 --- a/openbsc/include/openbsc/ipaccess.h +++ /dev/null @@ -1,52 +0,0 @@ -#ifndef _IPACCESS_H -#define _IPACCESS_H - -#include -#include "gsm_subscriber.h" -#include -#include -#include - -struct ipac_msgt_sccp_state { - uint8_t src_ref[3]; - uint8_t dst_ref[3]; - uint8_t trans_id; - uint8_t invoke_id; - char imsi[GSM23003_IMSI_MAX_DIGITS+1]; - uint8_t data[0]; -} __attribute__((packed)); - -/* - * @add_remove 0 for remove, 1 for add, 3 to asK - * @nr_lacs Number of extra lacs inside this package - * @lac One lac entry - */ -struct ipac_ext_lac_cmd { - uint8_t add_remove; - uint8_t nr_extra_lacs; - uint16_t lac; - uint8_t data[0]; -} __attribute__((packed)); - -void ipaccess_drop_oml(struct gsm_bts *bts); -void ipaccess_drop_rsl(struct gsm_bts_trx *trx); - -struct sdp_header_item { - struct sdp_header_entry header_entry; - struct llist_head entry; - off_t absolute_offset; -}; - -struct sdp_header { - struct sdp_firmware firmware_info; - - /* for more_magic a list of sdp_header_entry_list */ - struct llist_head header_list; - - /* the entry of the sdp_header */ - struct llist_head entry; -}; - -int ipaccess_analyze_file(int fd, const unsigned int st_size, const unsigned base_offset, struct llist_head *list); - -#endif /* _IPACCESS_H */ diff --git a/openbsc/include/openbsc/iu.h b/openbsc/include/openbsc/iu.h deleted file mode 100644 index f973ac1b..00000000 --- a/openbsc/include/openbsc/iu.h +++ /dev/null @@ -1,62 +0,0 @@ -#pragma once - -#include - -struct sgsn_pdp_ctx; -struct msgb; -struct gprs_ra_id; - -struct RANAP_RAB_SetupOrModifiedItemIEs_s; -struct RANAP_GlobalRNC_ID; - -struct ue_conn_ctx { - struct llist_head list; - struct osmo_sccp_link *link; - uint32_t conn_id; - int integrity_active; - struct gprs_ra_id ra_id; -}; - -enum iu_event_type { - IU_EVENT_RAB_ASSIGN, - IU_EVENT_SECURITY_MODE_COMPLETE, - IU_EVENT_IU_RELEASE, /* An actual Iu Release message was received */ - IU_EVENT_LINK_INVALIDATED, /* A SUA link was lost or closed down */ - /* FIXME: maybe IU_EVENT_IU_RELEASE and IU_EVENT_LINK_INVALIDATED - * should be combined to one generic event that simply means the - * ue_conn_ctx should no longer be used, for whatever reason. */ -}; - -extern const struct value_string iu_event_type_names[]; -static inline const char *iu_event_type_str(enum iu_event_type e) -{ - return get_value_string(iu_event_type_names, e); -} - -/* Implementations of iu_recv_cb_t shall find the ue_conn_ctx in msg->dst. */ -typedef int (* iu_recv_cb_t )(struct msgb *msg, struct gprs_ra_id *ra_id, - /* TODO "gprs_" in generic CS+PS domain ^ */ - uint16_t *sai); - -typedef int (* iu_event_cb_t )(struct ue_conn_ctx *ue_ctx, - enum iu_event_type type, void *data); - -typedef int (* iu_rab_ass_resp_cb_t )(struct ue_conn_ctx *ue_ctx, uint8_t rab_id, - struct RANAP_RAB_SetupOrModifiedItemIEs_s *setup_ies); - -int iu_init(void *ctx, const char *listen_addr, uint16_t listen_port, - iu_recv_cb_t iu_recv_cb, iu_event_cb_t iu_event_cb); - -void iu_link_del(struct osmo_sccp_link *link); - -int iu_tx(struct msgb *msg, uint8_t sapi); - -int iu_page_cs(const char *imsi, const uint32_t *tmsi, uint16_t lac); -int iu_page_ps(const char *imsi, const uint32_t *ptmsi, uint16_t lac, uint8_t rac); - -int iu_rab_act(struct ue_conn_ctx *ue_ctx, struct msgb *msg); -int iu_rab_deact(struct ue_conn_ctx *ue_ctx, uint8_t rab_id); -int iu_tx_sec_mode_cmd(struct ue_conn_ctx *uectx, struct gsm_auth_tuple *tp, - int send_ck, int new_key); - -void iu_vty_init(int *asn_debug_p); diff --git a/openbsc/include/openbsc/meas_feed.h b/openbsc/include/openbsc/meas_feed.h deleted file mode 100644 index f77ee075..00000000 --- a/openbsc/include/openbsc/meas_feed.h +++ /dev/null @@ -1,41 +0,0 @@ -#ifndef _OPENBSC_MEAS_FEED_H -#define _OPENBSC_MEAS_FEED_H - -#include - -#include - -struct meas_feed_hdr { - uint8_t msg_type; - uint8_t reserved; - uint16_t version; -}; - -struct meas_feed_meas { - struct meas_feed_hdr hdr; - char imsi[15+1]; - char name[31+1]; - char scenario[31+1]; - struct gsm_meas_rep mr; - /* The logical channel type, enum gsm_chan_t */ - uint8_t lchan_type; - /* The physical channel type, enum gsm_phys_chan_config */ - uint8_t pchan_type; - /* number of ths BTS in network */ - uint8_t bts_nr; - /* number of this TRX in the BTS */ - uint8_t trx_nr; - /* number of this timeslot at the TRX */ - uint8_t ts_nr; - /* The logical subslot number in the TS */ - uint8_t ss_nr; -}; - -enum meas_feed_msgtype { - MEAS_FEED_MEAS = 0, -}; - -#define MEAS_FEED_VERSION 1 - - -#endif diff --git a/openbsc/include/openbsc/meas_rep.h b/openbsc/include/openbsc/meas_rep.h deleted file mode 100644 index b0c03f0b..00000000 --- a/openbsc/include/openbsc/meas_rep.h +++ /dev/null @@ -1,67 +0,0 @@ -#ifndef _MEAS_REP_H -#define _MEAS_REP_H - -#include - -#include - -#define MRC_F_PROCESSED 0x0001 - -/* extracted from a L3 measurement report IE */ -struct gsm_meas_rep_cell { - uint8_t rxlev; - uint8_t bsic; - uint8_t neigh_idx; - uint16_t arfcn; - unsigned int flags; -}; - -#define MEAS_REP_F_UL_DTX 0x01 -#define MEAS_REP_F_DL_VALID 0x02 -#define MEAS_REP_F_BA1 0x04 -#define MEAS_REP_F_DL_DTX 0x08 -#define MEAS_REP_F_MS_TO 0x10 -#define MEAS_REP_F_MS_L1 0x20 -#define MEAS_REP_F_FPC 0x40 - -/* parsed uplink and downlink measurement result */ -struct gsm_meas_rep { - /* back-pointer to the logical channel */ - struct gsm_lchan *lchan; - - /* number of the measurement report */ - uint8_t nr; - /* flags, see MEAS_REP_F_* */ - unsigned int flags; - - /* uplink and downlink rxlev, rxqual; full and sub */ - struct gsm_meas_rep_unidir ul; - struct gsm_meas_rep_unidir dl; - - uint8_t bs_power; - /* according to 3GPP TS 48.058 § MS Timing Offset [-63; 192] */ - int16_t ms_timing_offset; - struct { - int8_t pwr; /* MS power in dBm */ - uint8_t ta; /* MS timing advance */ - } ms_l1; - - /* neighbor measurement reports for up to 6 cells */ - int num_cell; - struct gsm_meas_rep_cell cell[6]; -}; - -/* obtain an average over the last 'num' fields in the meas reps */ -int get_meas_rep_avg(const struct gsm_lchan *lchan, - enum meas_rep_field field, unsigned int num); - -/* Check if N out of M last values for FIELD are >= bd */ -int meas_rep_n_out_of_m_be(const struct gsm_lchan *lchan, - enum meas_rep_field field, - unsigned int n, unsigned int m, int be); - -unsigned int calc_initial_idx(unsigned int array_size, - unsigned int meas_rep_idx, - unsigned int num_values); - -#endif /* _MEAS_REP_H */ diff --git a/openbsc/include/openbsc/mgcp.h b/openbsc/include/openbsc/mgcp.h deleted file mode 100644 index b2262bc8..00000000 --- a/openbsc/include/openbsc/mgcp.h +++ /dev/null @@ -1,285 +0,0 @@ -/* A Media Gateway Control Protocol Media Gateway: RFC 3435 */ - -/* - * (C) 2009-2012 by Holger Hans Peter Freyther - * (C) 2009-2012 by On-Waves - * All Rights Reserved - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation; either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - * - */ - -#ifndef OPENBSC_MGCP_H -#define OPENBSC_MGCP_H - -#include -#include -#include - -#include "debug.h" - -#include -#include -#include -#include - -#define RTP_PORT_DEFAULT 4000 -#define RTP_PORT_NET_DEFAULT 16000 - -/** - * Calculate the RTP audio port for the given multiplex - * and the direction. This allows a semi static endpoint - * to port calculation removing the need for the BSC - * and the MediaGateway to communicate. - * - * Port usage explained: - * base + (multiplex * 2) + 0 == local port to wait for network packets - * base + (multiplex * 2) + 1 == local port for rtcp - * - * The above port will receive packets from the BTS that need - * to be patched and forwarded to the network. - * The above port will receive packets from the network that - * need to be patched and forwarded to the BTS. - * - * We assume to have a static BTS IP address so we can differentiate - * network and BTS. - * - */ -static inline int rtp_calculate_port(int multiplex, int base) -{ - return base + (multiplex * 2); -} - - -/* - * Handling of MGCP Endpoints and the MGCP Config - */ -struct mgcp_endpoint; -struct mgcp_config; -struct mgcp_trunk_config; -struct mgcp_rtp_end; - -#define MGCP_ENDP_CRCX 1 -#define MGCP_ENDP_DLCX 2 -#define MGCP_ENDP_MDCX 3 - -/* - * what to do with the msg? - * - continue as usual? - * - reject and send a failure code? - * - defer? do not send anything - */ -#define MGCP_POLICY_CONT 4 -#define MGCP_POLICY_REJECT 5 -#define MGCP_POLICY_DEFER 6 - -typedef int (*mgcp_realloc)(struct mgcp_trunk_config *cfg, int endpoint); -typedef int (*mgcp_change)(struct mgcp_trunk_config *cfg, int endpoint, int state); -typedef int (*mgcp_policy)(struct mgcp_trunk_config *cfg, int endpoint, int state, const char *transactio_id); -typedef int (*mgcp_reset)(struct mgcp_trunk_config *cfg); -typedef int (*mgcp_rqnt)(struct mgcp_endpoint *endp, char tone); - -/** - * Return: - * < 0 in case no audio was processed - * >= 0 in case audio was processed. The remaining payload - * length will be returned. - */ -typedef int (*mgcp_processing)(struct mgcp_endpoint *endp, - struct mgcp_rtp_end *dst_end, - char *data, int *len, int buf_size); -typedef int (*mgcp_processing_setup)(struct mgcp_endpoint *endp, - struct mgcp_rtp_end *dst_end, - struct mgcp_rtp_end *src_end); - -typedef void (*mgcp_get_format)(struct mgcp_endpoint *endp, - int *payload_type, - const char**subtype_name, - const char**fmtp_extra); - -#define PORT_ALLOC_STATIC 0 -#define PORT_ALLOC_DYNAMIC 1 - -/** - * This holds information on how to allocate ports - */ -struct mgcp_port_range { - int mode; - - /* addr or NULL to fall-back to default */ - char *bind_addr; - - /* pre-allocated from a base? */ - int base_port; - - /* dynamically allocated */ - int range_start; - int range_end; - int last_port; -}; - -#define MGCP_KEEPALIVE_ONCE (-1) - -struct mgcp_trunk_config { - struct llist_head entry; - - struct mgcp_config *cfg; - - int trunk_nr; - int trunk_type; - - char *audio_fmtp_extra; - char *audio_name; - int audio_payload; - int audio_send_ptime; - int audio_send_name; - int audio_loop; - - int no_audio_transcoding; - - int omit_rtcp; - int keepalive_interval; - - /* RTP patching */ - int force_constant_ssrc; /* 0: don't, 1: once */ - int force_aligned_timing; - - /* spec handling */ - int force_realloc; - - /* timer */ - struct osmo_timer_list keepalive_timer; - - unsigned int number_endpoints; - struct mgcp_endpoint *endpoints; -}; - -enum mgcp_role { - MGCP_BSC = 0, - MGCP_BSC_NAT, -}; - -struct mgcp_config { - int source_port; - char *local_ip; - char *source_addr; - char *bts_ip; - char *call_agent_addr; - - struct in_addr bts_in; - - /* transcoder handling */ - char *transcoder_ip; - struct in_addr transcoder_in; - int transcoder_remote_base; - - /* RTP processing */ - mgcp_processing rtp_processing_cb; - mgcp_processing_setup setup_rtp_processing_cb; - - mgcp_get_format get_net_downlink_format_cb; - - struct osmo_wqueue gw_fd; - - struct mgcp_port_range bts_ports; - struct mgcp_port_range net_ports; - struct mgcp_port_range transcoder_ports; - int endp_dscp; - - int bts_force_ptime; - - mgcp_change change_cb; - mgcp_policy policy_cb; - mgcp_reset reset_cb; - mgcp_realloc realloc_cb; - mgcp_rqnt rqnt_cb; - void *data; - - uint32_t last_call_id; - - /* trunk handling */ - struct mgcp_trunk_config trunk; - struct llist_head trunks; - - /* only used for start with a static configuration */ - int last_net_port; - int last_bts_port; - - enum mgcp_role role; - - /* osmux translator: 0 means disabled, 1 means enabled */ - int osmux; - /* addr to bind the server to */ - char *osmux_addr; - /* The BSC-NAT may ask for enabling osmux on demand. This tells us if - * the osmux socket is already initialized. - */ - int osmux_init; - /* osmux batch factor: from 1 to 4 maximum */ - int osmux_batch; - /* osmux batch size (in bytes) */ - int osmux_batch_size; - /* osmux port */ - uint16_t osmux_port; - /* Pad circuit with dummy messages until we see the first voice - * message. - */ - uint16_t osmux_dummy; -}; - -/* config management */ -struct mgcp_config *mgcp_config_alloc(void); -int mgcp_parse_config(const char *config_file, struct mgcp_config *cfg, - enum mgcp_role role); -int mgcp_vty_init(void); -int mgcp_endpoints_allocate(struct mgcp_trunk_config *cfg); -void mgcp_release_endp(struct mgcp_endpoint *endp); -void mgcp_initialize_endp(struct mgcp_endpoint *endp); -int mgcp_reset_transcoder(struct mgcp_config *cfg); -void mgcp_format_stats(struct mgcp_endpoint *endp, char *stats, size_t size); -int mgcp_parse_stats(struct msgb *msg, uint32_t *ps, uint32_t *os, uint32_t *pr, uint32_t *_or, int *loss, uint32_t *jitter); - -void mgcp_trunk_set_keepalive(struct mgcp_trunk_config *tcfg, int interval); - -/* - * format helper functions - */ -struct msgb *mgcp_handle_message(struct mgcp_config *cfg, struct msgb *msg); - -/* adc helper */ -static inline int mgcp_timeslot_to_endpoint(int multiplex, int timeslot) -{ - if (timeslot == 0) { - LOGP(DMGCP, LOGL_ERROR, "Timeslot should not be 0\n"); - timeslot = 255; - } - - return timeslot + (32 * multiplex); -} - -static inline void mgcp_endpoint_to_timeslot(int endpoint, int *multiplex, int *timeslot) -{ - *multiplex = endpoint / 32; - *timeslot = endpoint % 32; -} - -int mgcp_send_reset_ep(struct mgcp_endpoint *endp, int endpoint); -int mgcp_send_reset_all(struct mgcp_config *cfg); - - -int mgcp_create_bind(const char *source_addr, struct osmo_fd *fd, int port); -int mgcp_send(struct mgcp_endpoint *endp, int dest, int is_rtp, struct sockaddr_in *addr, char *buf, int rc); -int mgcp_udp_send(int fd, struct in_addr *addr, int port, char *buf, int len); - -#endif diff --git a/openbsc/include/openbsc/mgcp_internal.h b/openbsc/include/openbsc/mgcp_internal.h deleted file mode 100644 index 7c89d102..00000000 --- a/openbsc/include/openbsc/mgcp_internal.h +++ /dev/null @@ -1,342 +0,0 @@ -/* MGCP Private Data */ - -/* - * (C) 2009-2012 by Holger Hans Peter Freyther - * (C) 2009-2012 by On-Waves - * All Rights Reserved - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation; either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - * - */ - -#pragma once - -#include - -#include - -#define CI_UNUSED 0 - -enum mgcp_connection_mode { - MGCP_CONN_NONE = 0, - MGCP_CONN_RECV_ONLY = 1, - MGCP_CONN_SEND_ONLY = 2, - MGCP_CONN_RECV_SEND = MGCP_CONN_RECV_ONLY | MGCP_CONN_SEND_ONLY, - MGCP_CONN_LOOPBACK = 4 | MGCP_CONN_RECV_SEND, -}; - -enum mgcp_trunk_type { - MGCP_TRUNK_VIRTUAL, - MGCP_TRUNK_E1, -}; - -struct mgcp_rtp_stream_state { - uint32_t ssrc; - uint16_t last_seq; - uint32_t last_timestamp; - uint32_t err_ts_counter; - int32_t last_tsdelta; - uint32_t last_arrival_time; -}; - -struct mgcp_rtp_state { - int initialized; - int patch_ssrc; - - uint32_t orig_ssrc; - - int seq_offset; - - int32_t timestamp_offset; - uint32_t packet_duration; - - struct mgcp_rtp_stream_state in_stream; - struct mgcp_rtp_stream_state out_stream; - - /* jitter and packet loss calculation */ - int stats_initialized; - uint16_t stats_base_seq; - uint16_t stats_max_seq; - uint32_t stats_ssrc; - uint32_t stats_jitter; - int32_t stats_transit; - int stats_cycles; -}; - -struct mgcp_rtp_codec { - uint32_t rate; - int channels; - uint32_t frame_duration_num; - uint32_t frame_duration_den; - - int payload_type; - char *audio_name; - char *subtype_name; -}; - -struct mgcp_rtp_end { - /* statistics */ - unsigned int packets; - unsigned int octets; - unsigned int dropped_packets; - struct in_addr addr; - - /* in network byte order */ - int rtp_port, rtcp_port; - - /* audio codec information */ - struct mgcp_rtp_codec codec; - struct mgcp_rtp_codec alt_codec; /* TODO/XXX: make it generic */ - - /* per endpoint data */ - int frames_per_packet; - uint32_t packet_duration_ms; - char *fmtp_extra; - int output_enabled; - int force_output_ptime; - - /* RTP patching */ - int force_constant_ssrc; /* -1: always, 0: don't, 1: once */ - int force_aligned_timing; - void *rtp_process_data; - - /* - * Each end has a socket... - */ - struct osmo_fd rtp; - struct osmo_fd rtcp; - - int local_port; - int local_alloc; -}; - -enum { - MGCP_TAP_BTS_IN, - MGCP_TAP_BTS_OUT, - MGCP_TAP_NET_IN, - MGCP_TAP_NET_OUT, - - /* last element */ - MGCP_TAP_COUNT -}; - -struct mgcp_rtp_tap { - int enabled; - struct sockaddr_in forward; -}; - -struct mgcp_lco { - char *string; - char *codec; - int pkt_period_min; /* time in ms */ - int pkt_period_max; /* time in ms */ -}; - -enum mgcp_type { - MGCP_RTP_DEFAULT = 0, - MGCP_RTP_TRANSCODED, - MGCP_OSMUX_BSC, - MGCP_OSMUX_BSC_NAT, -}; - -#include - -struct mgcp_endpoint { - int allocated; - uint32_t ci; - char *callid; - struct mgcp_lco local_options; - int conn_mode; - int orig_mode; - - /* backpointer */ - struct mgcp_config *cfg; - struct mgcp_trunk_config *tcfg; - - /* port status for bts/net */ - struct mgcp_rtp_end bts_end; - struct mgcp_rtp_end net_end; - - /* - * For transcoding we will send from the local_port - * of trans_bts and it will arrive at trans_net from - * where we will forward it to the network. - */ - struct mgcp_rtp_end trans_bts; - struct mgcp_rtp_end trans_net; - enum mgcp_type type; - - /* sequence bits */ - struct mgcp_rtp_state net_state; - struct mgcp_rtp_state bts_state; - - /* fields for re-transmission */ - char *last_trans; - char *last_response; - - /* tap for the endpoint */ - struct mgcp_rtp_tap taps[MGCP_TAP_COUNT]; - - struct { - /* Osmux state: disabled, activating, active */ - enum osmux_state state; - /* Allocated Osmux circuit ID for this endpoint */ - int allocated_cid; - /* Used Osmux circuit ID for this endpoint */ - uint8_t cid; - /* handle to batch messages */ - struct osmux_in_handle *in; - /* handle to unbatch messages */ - struct osmux_out_handle out; - /* statistics */ - struct { - uint32_t chunks; - uint32_t octets; - } stats; - } osmux; -}; - -#define for_each_line(line, save) \ - for (line = strline_r(NULL, &save); line;\ - line = strline_r(NULL, &save)) - -static inline char *strline_r(char *str, char **saveptr) -{ - char *result; - - if (str) - *saveptr = str; - - result = *saveptr; - - if (*saveptr != NULL) { - *saveptr = strpbrk(*saveptr, "\r\n"); - - if (*saveptr != NULL) { - char *eos = *saveptr; - - if ((*saveptr)[0] == '\r' && (*saveptr)[1] == '\n') - (*saveptr)++; - (*saveptr)++; - if ((*saveptr)[0] == '\0') - *saveptr = NULL; - - *eos = '\0'; - } - } - - return result; -} - - - -#define ENDPOINT_NUMBER(endp) abs((int)(endp - endp->tcfg->endpoints)) - -/** - * Internal structure while parsing a request - */ -struct mgcp_parse_data { - struct mgcp_config *cfg; - struct mgcp_endpoint *endp; - char *trans; - char *save; - int found; -}; - -int mgcp_send_dummy(struct mgcp_endpoint *endp); -int mgcp_bind_bts_rtp_port(struct mgcp_endpoint *endp, int rtp_port); -int mgcp_bind_net_rtp_port(struct mgcp_endpoint *endp, int rtp_port); -int mgcp_bind_trans_bts_rtp_port(struct mgcp_endpoint *enp, int rtp_port); -int mgcp_bind_trans_net_rtp_port(struct mgcp_endpoint *enp, int rtp_port); -int mgcp_free_rtp_port(struct mgcp_rtp_end *end); - -/* For transcoding we need to manage an in and an output that are connected */ -static inline int endp_back_channel(int endpoint) -{ - return endpoint + 60; -} - -struct mgcp_trunk_config *mgcp_trunk_alloc(struct mgcp_config *cfg, int index); -struct mgcp_trunk_config *mgcp_trunk_num(struct mgcp_config *cfg, int index); - -void mgcp_rtp_end_config(struct mgcp_endpoint *endp, int expect_ssrc_change, - struct mgcp_rtp_end *rtp); -uint32_t mgcp_rtp_packet_duration(struct mgcp_endpoint *endp, - struct mgcp_rtp_end *rtp); - -void mgcp_state_calc_loss(struct mgcp_rtp_state *s, struct mgcp_rtp_end *, - uint32_t *expected, int *loss); -uint32_t mgcp_state_calc_jitter(struct mgcp_rtp_state *); - -/* payload processing default functions */ -int mgcp_rtp_processing_default(struct mgcp_endpoint *endp, struct mgcp_rtp_end *dst_end, - char *data, int *len, int buf_size); - -int mgcp_setup_rtp_processing_default(struct mgcp_endpoint *endp, - struct mgcp_rtp_end *dst_end, - struct mgcp_rtp_end *src_end); - -void mgcp_get_net_downlink_format_default(struct mgcp_endpoint *endp, - int *payload_type, - const char**subtype_name, - const char**fmtp_extra); - -/* internal RTP Annex A counting */ -void mgcp_rtp_annex_count(struct mgcp_endpoint *endp, struct mgcp_rtp_state *state, - const uint16_t seq, const int32_t transit, - const uint32_t ssrc); - -int mgcp_set_ip_tos(int fd, int tos); - -enum { - MGCP_DEST_NET = 0, - MGCP_DEST_BTS, -}; - - -#define MGCP_DUMMY_LOAD 0x23 - - -/** - * SDP related information - */ -/* Assume audio frame length of 20ms */ -#define DEFAULT_RTP_AUDIO_FRAME_DUR_NUM 20 -#define DEFAULT_RTP_AUDIO_FRAME_DUR_DEN 1000 -#define DEFAULT_RTP_AUDIO_PACKET_DURATION_MS 20 -#define DEFAULT_RTP_AUDIO_DEFAULT_RATE 8000 -#define DEFAULT_RTP_AUDIO_DEFAULT_CHANNELS 1 - -#define PTYPE_UNDEFINED (-1) -int mgcp_parse_sdp_data(struct mgcp_endpoint *endp, struct mgcp_rtp_end *rtp, struct mgcp_parse_data *p); -int mgcp_set_audio_info(void *ctx, struct mgcp_rtp_codec *codec, - int payload_type, const char *audio_name); - - -/** - * Internal network related - */ -static inline const char *mgcp_net_src_addr(struct mgcp_endpoint *endp) -{ - if (endp->cfg->net_ports.bind_addr) - return endp->cfg->net_ports.bind_addr; - return endp->cfg->source_addr; -} - -static inline const char *mgcp_bts_src_addr(struct mgcp_endpoint *endp) -{ - if (endp->cfg->bts_ports.bind_addr) - return endp->cfg->bts_ports.bind_addr; - return endp->cfg->source_addr; -} diff --git a/openbsc/include/openbsc/mgcp_transcode.h b/openbsc/include/openbsc/mgcp_transcode.h deleted file mode 100644 index 147e48bb..00000000 --- a/openbsc/include/openbsc/mgcp_transcode.h +++ /dev/null @@ -1,90 +0,0 @@ -/* - * (C) 2014 by On-Waves - * All Rights Reserved - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation; either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - * - */ -#ifndef OPENBSC_MGCP_TRANSCODE_H -#define OPENBSC_MGCP_TRANSCODE_H - -#include "bscconfig.h" - -#include -#ifdef HAVE_BCG729 -#include -#include -#endif - -enum audio_format { - AF_INVALID, - AF_S16, - AF_L16, - AF_GSM, - AF_G729, - AF_PCMA, - AF_PCMU -}; - - -struct mgcp_process_rtp_state { - /* decoding */ - enum audio_format src_fmt; - union { - gsm gsm_handle; -#ifdef HAVE_BCG729 - bcg729DecoderChannelContextStruct *g729_dec; -#endif - } src; - size_t src_frame_size; - size_t src_samples_per_frame; - - /* processing */ - - /* encoding */ - enum audio_format dst_fmt; - union { - gsm gsm_handle; -#ifdef HAVE_BCG729 - bcg729EncoderChannelContextStruct *g729_enc; -#endif - } dst; - size_t dst_frame_size; - size_t dst_samples_per_frame; - int dst_packet_duration; - - int is_running; - uint16_t next_seq; - uint32_t next_time; - int16_t samples[10*160]; - size_t sample_cnt; - size_t sample_offs; -}; - - -int mgcp_transcoding_setup(struct mgcp_endpoint *endp, - struct mgcp_rtp_end *dst_end, - struct mgcp_rtp_end *src_end); - -void mgcp_transcoding_net_downlink_format(struct mgcp_endpoint *endp, - int *payload_type, - const char**audio_name, - const char**fmtp_extra); - -int mgcp_transcoding_process_rtp(struct mgcp_endpoint *endp, - struct mgcp_rtp_end *dst_end, - char *data, int *len, int buf_size); - -int mgcp_transcoding_get_frame_size(void *state_, int nsamples, int dst); -#endif /* OPENBSC_MGCP_TRANSCODE_H */ diff --git a/openbsc/include/openbsc/misdn.h b/openbsc/include/openbsc/misdn.h deleted file mode 100644 index 9851ad32..00000000 --- a/openbsc/include/openbsc/misdn.h +++ /dev/null @@ -1,27 +0,0 @@ -/* (C) 2008 by Harald Welte - * All Rights Reserved - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation; either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - * - */ - -#ifndef MISDN_H -#define MISDN_H - -#include - -int mi_setup(int cardnr, struct e1inp_line *line, int release_l2); -int mi_e1_line_update(struct e1inp_line *line); - -#endif diff --git a/openbsc/include/openbsc/mncc.h b/openbsc/include/openbsc/mncc.h deleted file mode 100644 index 49f0c8b8..00000000 --- a/openbsc/include/openbsc/mncc.h +++ /dev/null @@ -1,219 +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 by Holger Hans Peter Freyther - * - * All Rights Reserved - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation; either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - * - */ - -#ifndef _MNCC_H -#define _MNCC_H - -#include -#include - -#include - -struct gsm_network; -struct msgb; - - -/* One end of a call */ -struct gsm_call { - struct llist_head entry; - - /* network handle */ - void *net; - - /* the 'local' transaction */ - uint32_t callref; - /* the 'remote' transaction */ - uint32_t remote_ref; -}; - -#define MNCC_SETUP_REQ 0x0101 -#define MNCC_SETUP_IND 0x0102 -#define MNCC_SETUP_RSP 0x0103 -#define MNCC_SETUP_CNF 0x0104 -#define MNCC_SETUP_COMPL_REQ 0x0105 -#define MNCC_SETUP_COMPL_IND 0x0106 -/* MNCC_REJ_* is perfomed via MNCC_REL_* */ -#define MNCC_CALL_CONF_IND 0x0107 -#define MNCC_CALL_PROC_REQ 0x0108 -#define MNCC_PROGRESS_REQ 0x0109 -#define MNCC_ALERT_REQ 0x010a -#define MNCC_ALERT_IND 0x010b -#define MNCC_NOTIFY_REQ 0x010c -#define MNCC_NOTIFY_IND 0x010d -#define MNCC_DISC_REQ 0x010e -#define MNCC_DISC_IND 0x010f -#define MNCC_REL_REQ 0x0110 -#define MNCC_REL_IND 0x0111 -#define MNCC_REL_CNF 0x0112 -#define MNCC_FACILITY_REQ 0x0113 -#define MNCC_FACILITY_IND 0x0114 -#define MNCC_START_DTMF_IND 0x0115 -#define MNCC_START_DTMF_RSP 0x0116 -#define MNCC_START_DTMF_REJ 0x0117 -#define MNCC_STOP_DTMF_IND 0x0118 -#define MNCC_STOP_DTMF_RSP 0x0119 -#define MNCC_MODIFY_REQ 0x011a -#define MNCC_MODIFY_IND 0x011b -#define MNCC_MODIFY_RSP 0x011c -#define MNCC_MODIFY_CNF 0x011d -#define MNCC_MODIFY_REJ 0x011e -#define MNCC_HOLD_IND 0x011f -#define MNCC_HOLD_CNF 0x0120 -#define MNCC_HOLD_REJ 0x0121 -#define MNCC_RETRIEVE_IND 0x0122 -#define MNCC_RETRIEVE_CNF 0x0123 -#define MNCC_RETRIEVE_REJ 0x0124 -#define MNCC_USERINFO_REQ 0x0125 -#define MNCC_USERINFO_IND 0x0126 -#define MNCC_REJ_REQ 0x0127 -#define MNCC_REJ_IND 0x0128 - -#define MNCC_BRIDGE 0x0200 -#define MNCC_FRAME_RECV 0x0201 -#define MNCC_FRAME_DROP 0x0202 -#define MNCC_LCHAN_MODIFY 0x0203 -#define MNCC_RTP_CREATE 0x0204 -#define MNCC_RTP_CONNECT 0x0205 -#define MNCC_RTP_FREE 0x0206 - -#define GSM_TCHF_FRAME 0x0300 -#define GSM_TCHF_FRAME_EFR 0x0301 -#define GSM_TCHH_FRAME 0x0302 -#define GSM_TCH_FRAME_AMR 0x0303 -#define GSM_BAD_FRAME 0x03ff - -#define MNCC_SOCKET_HELLO 0x0400 - -#define GSM_MAX_FACILITY 128 -#define GSM_MAX_SSVERSION 128 -#define GSM_MAX_USERUSER 128 - -#define MNCC_F_BEARER_CAP 0x0001 -#define MNCC_F_CALLED 0x0002 -#define MNCC_F_CALLING 0x0004 -#define MNCC_F_REDIRECTING 0x0008 -#define MNCC_F_CONNECTED 0x0010 -#define MNCC_F_CAUSE 0x0020 -#define MNCC_F_USERUSER 0x0040 -#define MNCC_F_PROGRESS 0x0080 -#define MNCC_F_EMERGENCY 0x0100 -#define MNCC_F_FACILITY 0x0200 -#define MNCC_F_SSVERSION 0x0400 -#define MNCC_F_CCCAP 0x0800 -#define MNCC_F_KEYPAD 0x1000 -#define MNCC_F_SIGNAL 0x2000 - -struct gsm_mncc { - /* context based information */ - uint32_t msg_type; - uint32_t callref; - - /* which fields are present */ - uint32_t fields; - - /* data derived informations (MNCC_F_ based) */ - struct gsm_mncc_bearer_cap bearer_cap; - struct gsm_mncc_number called; - struct gsm_mncc_number calling; - struct gsm_mncc_number redirecting; - struct gsm_mncc_number connected; - struct gsm_mncc_cause cause; - struct gsm_mncc_progress progress; - struct gsm_mncc_useruser useruser; - struct gsm_mncc_facility facility; - struct gsm_mncc_cccap cccap; - struct gsm_mncc_ssversion ssversion; - struct { - int sup; - int inv; - } clir; - int signal; - - /* data derived information, not MNCC_F based */ - int keypad; - int more; - int notify; /* 0..127 */ - int emergency; - char imsi[16]; - - unsigned char lchan_type; - unsigned char lchan_mode; -}; - -struct gsm_data_frame { - uint32_t msg_type; - uint32_t callref; - unsigned char data[0]; -}; - -#define MNCC_SOCK_VERSION 5 -struct gsm_mncc_hello { - uint32_t msg_type; - uint32_t version; - - /* send the sizes of the structs */ - uint32_t mncc_size; - uint32_t data_frame_size; - - /* send some offsets */ - uint32_t called_offset; - uint32_t signal_offset; - uint32_t emergency_offset; - uint32_t lchan_type_offset; -}; - -struct gsm_mncc_rtp { - uint32_t msg_type; - uint32_t callref; - uint32_t ip; - uint16_t port; - uint32_t payload_type; - uint32_t payload_msg_type; -}; - -struct gsm_mncc_bridge { - uint32_t msg_type; - uint32_t callref[2]; -}; - -const char *get_mncc_name(int value); -void mncc_set_cause(struct gsm_mncc *data, int loc, int val); -void cc_tx_to_mncc(struct gsm_network *net, struct msgb *msg); - -/* input from CC code into mncc_builtin */ -int int_mncc_recv(struct gsm_network *net, struct msgb *msg); - -/* input from CC code into mncc_sock */ -int mncc_sock_from_cc(struct gsm_network *net, struct msgb *msg); - -int mncc_sock_init(struct gsm_network *net, const char *sock_path); - -#define mncc_is_data_frame(msg_type) \ - (msg_type == GSM_TCHF_FRAME \ - || msg_type == GSM_TCHF_FRAME_EFR \ - || msg_type == GSM_TCHH_FRAME \ - || msg_type == GSM_TCH_FRAME_AMR \ - || msg_type == GSM_BAD_FRAME) - - -#endif diff --git a/openbsc/include/openbsc/mncc_int.h b/openbsc/include/openbsc/mncc_int.h deleted file mode 100644 index 213ce141..00000000 --- a/openbsc/include/openbsc/mncc_int.h +++ /dev/null @@ -1,14 +0,0 @@ -#ifndef _MNCC_INT_H -#define _MNCC_INT_H - -#include - -struct mncc_int { - uint8_t def_codec[2]; -}; - -extern struct mncc_int mncc_int; - -uint8_t mncc_codec_for_mode(int lchan_type); - -#endif diff --git a/openbsc/include/openbsc/nat_rewrite_trie.h b/openbsc/include/openbsc/nat_rewrite_trie.h deleted file mode 100644 index 0571099c..00000000 --- a/openbsc/include/openbsc/nat_rewrite_trie.h +++ /dev/null @@ -1,47 +0,0 @@ -/* - * (C) 2013 by On-Waves - * (C) 2013 by Holger Hans Peter Freyther - * All Rights Reserved - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation; either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - * - */ -#ifndef NAT_REWRITE_FILE_H -#define NAT_REWRITE_FILE_H - -#include - -struct vty; - -struct nat_rewrite_rule { - /* For digits 0-9 and + */ - struct nat_rewrite_rule *rules[11]; - - char empty; - char prefix[14]; - char rewrite[6]; -}; - -struct nat_rewrite { - struct nat_rewrite_rule rule; - size_t prefixes; -}; - - -struct nat_rewrite *nat_rewrite_parse(void *ctx, const char *filename); -struct nat_rewrite_rule *nat_rewrite_lookup(struct nat_rewrite *, const char *prefix); -void nat_rewrite_dump(struct nat_rewrite *rewr); -void nat_rewrite_dump_vty(struct vty *vty, struct nat_rewrite *rewr); - -#endif diff --git a/openbsc/include/openbsc/network_listen.h b/openbsc/include/openbsc/network_listen.h deleted file mode 100644 index 67d1f4ef..00000000 --- a/openbsc/include/openbsc/network_listen.h +++ /dev/null @@ -1,16 +0,0 @@ -#ifndef _OPENBSC_NWL_H -#define _OPENBSC_NWL_H - -#include -#include - -void ipac_nwl_init(void); - -/* Start a NWL test. It will raise the S_IPAC_TEST_COMPLETE signal. */ -int ipac_nwl_test_start(struct gsm_bts_trx *trx, uint8_t testnr, - const uint8_t *phys_conf, unsigned int phys_conf_len); - -int ipac_rxlevstat2whitelist(uint16_t *buf, const struct rxlev_stats *st, uint8_t min_rxlev, - uint16_t max_num_arfcns); - -#endif /* _OPENBSC_NWL_H */ diff --git a/openbsc/include/openbsc/oap_client.h b/openbsc/include/openbsc/oap_client.h deleted file mode 100644 index 80c86d5d..00000000 --- a/openbsc/include/openbsc/oap_client.h +++ /dev/null @@ -1,82 +0,0 @@ -/* Osmocom Authentication Protocol API */ - -/* (C) 2015 by Sysmocom s.f.m.c. GmbH - * All Rights Reserved - * - * Author: Neels Hofmeyr - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation; either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - * - */ - -#pragma once - -#include - -struct msgb; -struct osmo_oap_message; - -/* This is the config part for vty. It is essentially copied in - * oap_client_state, where values are copied over once the config is - * considered valid. */ -struct oap_client_config { - uint16_t client_id; - int secret_k_present; - uint8_t secret_k[16]; - int secret_opc_present; - uint8_t secret_opc[16]; -}; - -/* The runtime state of the OAP client. client_id and the secrets are in fact - * duplicated from oap_client_config, so that a separate validation of the - * config data is possible, and so that only a struct oap_client_state* is - * passed around. */ -struct oap_client_state { - enum { - OAP_UNINITIALIZED = 0, /* just allocated. */ - OAP_DISABLED, /* disabled by config. */ - OAP_INITIALIZED, /* enabled, config is valid. */ - OAP_REQUESTED_CHALLENGE, - OAP_SENT_CHALLENGE_RESULT, - OAP_REGISTERED - } state; - uint16_t client_id; - uint8_t secret_k[16]; - uint8_t secret_opc[16]; - int registration_failures; -}; - -/* From config, initialize state. Return 0 on success. */ -int oap_client_init(struct oap_client_config *config, - struct oap_client_state *state); - -/* Construct an OAP registration message and return in *msg_tx. Use - * state->client_id and update state->state. - * Return 0 on success, or a negative value on error. - * If an error is returned, *msg_tx is guaranteed to be NULL. */ -int oap_client_register(struct oap_client_state *state, struct msgb **msg_tx); - -/* Decode and act on a received OAP message msg_rx. Update state->state. If a - * non-NULL pointer is returned in *msg_tx, that msgb should be sent to the OAP - * server (and freed) by the caller. The received msg_rx is not freed. - * Return 0 on success, or a negative value on error. - * If an error is returned, *msg_tx is guaranteed to be NULL. */ -int oap_client_handle(struct oap_client_state *state, - const struct msgb *msg_rx, struct msgb **msg_tx); - -/* Allocate a msgb and in it, return the encoded oap_client_msg. Return - * NULL on error. (Like oap_client_encode(), but also allocates a msgb.) - * About the name: the idea is do_something(oap_client_encoded(my_struct)) - */ -struct msgb *oap_client_encoded(const struct osmo_oap_message *oap_client_msg); diff --git a/openbsc/include/openbsc/openbscdefines.h b/openbsc/include/openbsc/openbscdefines.h deleted file mode 100644 index c6ac153b..00000000 --- a/openbsc/include/openbsc/openbscdefines.h +++ /dev/null @@ -1,34 +0,0 @@ -/* - * (C) 2009 by Holger Hans Peter Freyther - * - * All Rights Reserved - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation; either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - * - */ - -#ifndef OPENBSCDEFINES_H -#define OPENBSCDEFINES_H - -#ifdef BUILDING_ON_WINDOWS - #ifdef BUILDING_OPENBSC - #define BSC_API __declspec(dllexport) - #else - #define BSC_API __declspec(dllimport) - #endif -#else - #define BSC_API __attribute__((visibility("default"))) -#endif - -#endif diff --git a/openbsc/include/openbsc/osmo_bsc.h b/openbsc/include/openbsc/osmo_bsc.h deleted file mode 100644 index 9e688fd5..00000000 --- a/openbsc/include/openbsc/osmo_bsc.h +++ /dev/null @@ -1,71 +0,0 @@ -/* OpenBSC BSC code */ - -#ifndef OSMO_BSC_H -#define OSMO_BSC_H - -#include "bsc_api.h" -#include "bsc_msg_filter.h" - -#define BSS_SEND_USSD 1 - -enum bsc_con { - BSC_CON_SUCCESS, - BSC_CON_REJECT_NO_LINK, - BSC_CON_REJECT_RF_GRACE, - BSC_CON_NO_MEM, -}; - -struct sccp_connection; -struct bsc_msc_data; -struct bsc_msc_connection; - -struct osmo_bsc_sccp_con { - struct llist_head entry; - - int ciphering_handled; - - /* for audio handling */ - uint16_t cic; - int rtp_port; - - /* for advanced ping/pong */ - int send_ping; - - /* SCCP connection realted */ - struct sccp_connection *sccp; - struct bsc_msc_data *msc; - struct osmo_timer_list sccp_it_timeout; - struct osmo_timer_list sccp_cc_timeout; - - struct llist_head sccp_queue; - unsigned int sccp_queue_size; - - struct gsm_subscriber_connection *conn; - uint8_t new_subscriber; - - struct bsc_filter_state filter_state; -}; - -struct bsc_api *osmo_bsc_api(); - -int bsc_queue_for_msc(struct osmo_bsc_sccp_con *conn, struct msgb *msg); -int bsc_open_connection(struct osmo_bsc_sccp_con *sccp, struct msgb *msg); -enum bsc_con bsc_create_new_connection(struct gsm_subscriber_connection *conn, - struct bsc_msc_data *msc, int send_ping); -int bsc_delete_connection(struct osmo_bsc_sccp_con *sccp); - -struct bsc_msc_data *bsc_find_msc(struct gsm_subscriber_connection *conn, struct msgb *); -int bsc_scan_bts_msg(struct gsm_subscriber_connection *conn, struct msgb *msg); -int bsc_scan_msc_msg(struct gsm_subscriber_connection *conn, struct msgb *msg); -int bsc_send_welcome_ussd(struct gsm_subscriber_connection *conn); - -int bsc_handle_udt(struct bsc_msc_data *msc, struct msgb *msg, unsigned int length); -int bsc_handle_dt1(struct osmo_bsc_sccp_con *conn, struct msgb *msg, unsigned int len); - -int bsc_ctrl_cmds_install(); - -void bsc_gen_location_state_trap(struct gsm_bts *bts); - -struct llist_head *bsc_access_lists(void); - -#endif diff --git a/openbsc/include/openbsc/osmo_bsc_grace.h b/openbsc/include/openbsc/osmo_bsc_grace.h deleted file mode 100644 index 5a81cd13..00000000 --- a/openbsc/include/openbsc/osmo_bsc_grace.h +++ /dev/null @@ -1,35 +0,0 @@ -/* - * (C) 2010-2013 by Holger Hans Peter Freyther - * (C) 2010-2013 by On-Waves - * All Rights Reserved - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation; either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - * - */ - -#ifndef OSMO_BSC_GRACE_H -#define OSMO_BSC_GRACE_H - -#include -#include - -struct bsc_msc_data; - -int bsc_grace_allow_new_connection(struct gsm_network *net, struct gsm_bts *bts); -int bsc_grace_paging_request(enum signal_rf rf_policy, - struct bsc_subscr *subscr, - int chan_needed, - struct bsc_msc_data *msc); - -#endif diff --git a/openbsc/include/openbsc/osmo_bsc_rf.h b/openbsc/include/openbsc/osmo_bsc_rf.h deleted file mode 100644 index 19ccd080..00000000 --- a/openbsc/include/openbsc/osmo_bsc_rf.h +++ /dev/null @@ -1,66 +0,0 @@ -#ifndef OSMO_BSC_RF -#define OSMO_BSC_RF - -#include -#include -#include - -enum osmo_bsc_rf_opstate { - OSMO_BSC_RF_OPSTATE_INOPERATIONAL, - OSMO_BSC_RF_OPSTATE_OPERATIONAL, -}; - -enum osmo_bsc_rf_adminstate { - OSMO_BSC_RF_ADMINSTATE_UNLOCKED, - OSMO_BSC_RF_ADMINSTATE_LOCKED, -}; - -enum osmo_bsc_rf_policy { - OSMO_BSC_RF_POLICY_OFF, - OSMO_BSC_RF_POLICY_ON, - OSMO_BSC_RF_POLICY_GRACE, - OSMO_BSC_RF_POLICY_UNKNOWN, -}; - - -struct gsm_network; - -struct osmo_bsc_rf { - /* the value of signal.h */ - int policy; - struct osmo_fd listen; - struct gsm_network *gsm_network; - - const char *last_state_command; - - char *last_rf_lock_ctrl_command; - - /* delay the command */ - char last_request; - struct osmo_timer_list delay_cmd; - - /* verify that RF is up as it should be */ - struct osmo_timer_list rf_check; - - /* some handling for the automatic grace switch */ - struct osmo_timer_list grace_timeout; - - /* auto RF switch-off due lack of MSC connection */ - struct osmo_timer_list auto_off_timer; -}; - -struct osmo_bsc_rf_conn { - struct osmo_wqueue queue; - struct osmo_bsc_rf *rf; -}; - -const char *osmo_bsc_rf_get_opstate_name(enum osmo_bsc_rf_opstate opstate); -const char *osmo_bsc_rf_get_adminstate_name(enum osmo_bsc_rf_adminstate adminstate); -const char *osmo_bsc_rf_get_policy_name(enum osmo_bsc_rf_policy policy); -enum osmo_bsc_rf_opstate osmo_bsc_rf_get_opstate_by_bts(struct gsm_bts *bts); -enum osmo_bsc_rf_adminstate osmo_bsc_rf_get_adminstate_by_bts(struct gsm_bts *bts); -enum osmo_bsc_rf_policy osmo_bsc_rf_get_policy_by_bts(struct gsm_bts *bts); -struct osmo_bsc_rf *osmo_bsc_rf_create(const char *path, struct gsm_network *net); -void osmo_bsc_rf_schedule_lock(struct osmo_bsc_rf *rf, char cmd); - -#endif diff --git a/openbsc/include/openbsc/osmo_msc.h b/openbsc/include/openbsc/osmo_msc.h deleted file mode 100644 index beb3f5e4..00000000 --- a/openbsc/include/openbsc/osmo_msc.h +++ /dev/null @@ -1,11 +0,0 @@ -/* Routines for the MSC handling */ - -#ifndef OSMO_MSC_H -#define OSMO_MSC_H - -#include "bsc_api.h" - -struct bsc_api *msc_bsc_api(); -void msc_release_connection(struct gsm_subscriber_connection *conn); - -#endif diff --git a/openbsc/include/openbsc/osmux.h b/openbsc/include/openbsc/osmux.h deleted file mode 100644 index 0b64a7f1..00000000 --- a/openbsc/include/openbsc/osmux.h +++ /dev/null @@ -1,42 +0,0 @@ -#ifndef _OPENBSC_OSMUX_H_ -#define _OPENBSC_OSMUX_H_ - -#include - -#define OSMUX_PORT 1984 - -enum { - OSMUX_ROLE_BSC = 0, - OSMUX_ROLE_BSC_NAT, -}; - -int osmux_init(int role, struct mgcp_config *cfg); -int osmux_enable_endpoint(struct mgcp_endpoint *endp, int role, - struct in_addr *addr, uint16_t port); -void osmux_disable_endpoint(struct mgcp_endpoint *endp); -void osmux_allocate_cid(struct mgcp_endpoint *endp); -void osmux_release_cid(struct mgcp_endpoint *endp); - -int osmux_xfrm_to_rtp(struct mgcp_endpoint *endp, int type, char *buf, int rc); -int osmux_xfrm_to_osmux(int type, char *buf, int rc, struct mgcp_endpoint *endp); - -int osmux_send_dummy(struct mgcp_endpoint *endp); - -int osmux_get_cid(void); -void osmux_put_cid(uint8_t osmux_cid); -int osmux_used_cid(void); - -enum osmux_state { - OSMUX_STATE_DISABLED = 0, - OSMUX_STATE_NEGOTIATING, - OSMUX_STATE_ACTIVATING, - OSMUX_STATE_ENABLED, -}; - -enum osmux_usage { - OSMUX_USAGE_OFF = 0, - OSMUX_USAGE_ON = 1, - OSMUX_USAGE_ONLY = 2, -}; - -#endif diff --git a/openbsc/include/openbsc/paging.h b/openbsc/include/openbsc/paging.h deleted file mode 100644 index 7dd8500a..00000000 --- a/openbsc/include/openbsc/paging.h +++ /dev/null @@ -1,77 +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 . - * - */ - -#ifndef PAGING_H -#define PAGING_H - -#include -#include - -#include -#include - -#include -#include - -/** - * A pending paging request - */ -struct gsm_paging_request { - /* list_head for list of all paging requests */ - struct llist_head entry; - /* the subscriber which we're paging. Later gsm_paging_request - * should probably become a part of the bsc_subsrc struct? */ - struct bsc_subscr *bsub; - /* back-pointer to the BTS on which we are paging */ - struct gsm_bts *bts; - /* what kind of channel type do we ask the MS to establish */ - int chan_type; - - /* Timer 3113: how long do we try to page? */ - struct osmo_timer_list T3113; - - /* How often did we ask the BTS to page? */ - int attempts; - - /* callback to be called in case paging completes */ - gsm_cbfn *cbfn; - void *cbfn_param; -}; - -/* schedule paging request */ -int paging_request(struct gsm_network *network, struct bsc_subscr *bsub, - int type, gsm_cbfn *cbfn, void *data); -int paging_request_bts(struct gsm_bts *bts, struct bsc_subscr *bsub, - int type, gsm_cbfn *cbfn, void *data); - -/* stop paging requests */ -void paging_request_stop(struct llist_head *bts_list, - struct gsm_bts *_bts, struct bsc_subscr *bsub, - struct gsm_subscriber_connection *conn, - struct msgb *msg); - -/* update paging load */ -void paging_update_buffer_space(struct gsm_bts *bts, uint16_t); - -/* pending paging requests */ -unsigned int paging_pending_requests_nr(struct gsm_bts *bts); - -void *paging_get_data(struct gsm_bts *bts, struct bsc_subscr *bsub); - -#endif diff --git a/openbsc/include/openbsc/pcu_if.h b/openbsc/include/openbsc/pcu_if.h deleted file mode 100644 index 1f398b4a..00000000 --- a/openbsc/include/openbsc/pcu_if.h +++ /dev/null @@ -1,35 +0,0 @@ -#ifndef _PCU_IF_H -#define _PCU_IF_H - -#include - -extern int pcu_direct; - -struct pcu_sock_state { - struct gsm_network *net; - struct osmo_fd listen_bfd; /* fd for listen socket */ - struct osmo_fd conn_bfd; /* fd for connection to lcr */ - struct llist_head upqueue; /* queue for sending messages */ -}; - -/* PCU relevant information has changed; Inform PCU (if connected) */ -void pcu_info_update(struct gsm_bts *bts); - -/* Forward rach indication to PCU */ -int pcu_tx_rach_ind(struct gsm_bts *bts, int16_t qta, uint16_t ra, uint32_t fn, - uint8_t is_11bit, enum ph_burst_type burst_type); - -/* Confirm the sending of an immediate assignment to the pcu */ -int pcu_tx_imm_ass_sent(struct gsm_bts *bts, uint32_t tlli); - - -/* Confirm the sending of an immediate assignment to the pcu */ -int pcu_tx_imm_ass_sent(struct gsm_bts *bts, uint32_t tlli); - -/* Open connection to PCU */ -int pcu_sock_init(const char *path, struct gsm_bts *bts); - -/* Close connection to PCU */ -void pcu_sock_exit(struct gsm_bts *bts); - -#endif /* _PCU_IF_H */ diff --git a/openbsc/include/openbsc/pcuif_proto.h b/openbsc/include/openbsc/pcuif_proto.h deleted file mode 100644 index eb28d66b..00000000 --- a/openbsc/include/openbsc/pcuif_proto.h +++ /dev/null @@ -1,176 +0,0 @@ -#ifndef _PCUIF_PROTO_H -#define _PCUIF_PROTO_H - -#define PCU_IF_VERSION 0x08 - -/* msg_type */ -#define PCU_IF_MSG_DATA_REQ 0x00 /* send data to given channel */ -#define PCU_IF_MSG_DATA_CNF 0x01 /* confirm (e.g. transmission on PCH) */ -#define PCU_IF_MSG_DATA_IND 0x02 /* receive data from given channel */ -#define PCU_IF_MSG_DATA_CNF_DT 0x11 /* confirm (with direct tlli) */ -#define PCU_IF_MSG_RTS_REQ 0x10 /* ready to send request */ -#define PCU_IF_MSG_RACH_IND 0x22 /* receive RACH */ -#define PCU_IF_MSG_INFO_IND 0x32 /* retrieve BTS info */ -#define PCU_IF_MSG_ACT_REQ 0x40 /* activate/deactivate PDCH */ -#define PCU_IF_MSG_TIME_IND 0x52 /* GSM time indication */ -#define PCU_IF_MSG_PAG_REQ 0x60 /* paging request */ - -/* sapi */ -#define PCU_IF_SAPI_RACH 0x01 /* channel request on CCCH */ -#define PCU_IF_SAPI_AGCH 0x02 /* assignment on AGCH */ -#define PCU_IF_SAPI_PCH 0x03 /* paging/assignment on PCH */ -#define PCU_IF_SAPI_BCCH 0x04 /* SI on BCCH */ -#define PCU_IF_SAPI_PDTCH 0x05 /* packet data/control/ccch block */ -#define PCU_IF_SAPI_PRACH 0x06 /* packet random access channel */ -#define PCU_IF_SAPI_PTCCH 0x07 /* packet TA control channel */ -#define PCU_IF_SAPI_AGCH_DT 0x08 /* assignment on AGCH but with additional TLLI */ - -/* flags */ -#define PCU_IF_FLAG_ACTIVE (1 << 0)/* BTS is active */ -#define PCU_IF_FLAG_SYSMO (1 << 1)/* access PDCH of sysmoBTS directly */ -#define PCU_IF_FLAG_CS1 (1 << 16) -#define PCU_IF_FLAG_CS2 (1 << 17) -#define PCU_IF_FLAG_CS3 (1 << 18) -#define PCU_IF_FLAG_CS4 (1 << 19) -#define PCU_IF_FLAG_MCS1 (1 << 20) -#define PCU_IF_FLAG_MCS2 (1 << 21) -#define PCU_IF_FLAG_MCS3 (1 << 22) -#define PCU_IF_FLAG_MCS4 (1 << 23) -#define PCU_IF_FLAG_MCS5 (1 << 24) -#define PCU_IF_FLAG_MCS6 (1 << 25) -#define PCU_IF_FLAG_MCS7 (1 << 26) -#define PCU_IF_FLAG_MCS8 (1 << 27) -#define PCU_IF_FLAG_MCS9 (1 << 28) - -struct gsm_pcu_if_data { - uint8_t sapi; - uint8_t len; - uint8_t data[162]; - uint32_t fn; - uint16_t arfcn; - uint8_t trx_nr; - uint8_t ts_nr; - uint8_t block_nr; - int8_t rssi; - uint16_t ber10k; /*!< \brief BER in units of 0.01% */ - int16_t ta_offs_qbits; /* !< \brief Burst TA Offset in quarter bits */ - int16_t lqual_cb; /* !< \brief Link quality in centiBel */ -} __attribute__ ((packed)); - -/* data confirmation with direct tlli (instead of raw mac block with tlli) */ -struct gsm_pcu_if_data_cnf_dt { - uint8_t sapi; - uint32_t tlli; - uint32_t fn; - uint16_t arfcn; - uint8_t trx_nr; - uint8_t ts_nr; - uint8_t block_nr; - int8_t rssi; - uint16_t ber10k; /*!< \brief BER in units of 0.01% */ - int16_t ta_offs_qbits; /* !< \brief Burst TA Offset in quarter bits */ - int16_t lqual_cb; /* !< \brief Link quality in centiBel */ -} __attribute__ ((packed)); - -struct gsm_pcu_if_rts_req { - uint8_t sapi; - uint8_t spare[3]; - uint32_t fn; - uint16_t arfcn; - uint8_t trx_nr; - uint8_t ts_nr; - uint8_t block_nr; -} __attribute__ ((packed)); - -struct gsm_pcu_if_rach_ind { - uint8_t sapi; - uint16_t ra; - int16_t qta; - uint32_t fn; - uint16_t arfcn; - uint8_t is_11bit; - uint8_t burst_type; -} __attribute__ ((packed)); - -struct gsm_pcu_if_info_trx { - uint16_t arfcn; - uint8_t pdch_mask; /* PDCH channels per TS */ - uint8_t spare; - uint8_t tsc[8]; /* TSC per channel */ - uint32_t hlayer1; -} __attribute__ ((packed)); - -struct gsm_pcu_if_info_ind { - uint32_t version; - uint32_t flags; - struct gsm_pcu_if_info_trx trx[8]; /* TRX infos per BTS */ - uint8_t bsic; - /* RAI */ - uint16_t mcc, mnc, lac, rac; - /* NSE */ - uint16_t nsei; - uint8_t nse_timer[7]; - uint8_t cell_timer[11]; - /* cell */ - uint16_t cell_id; - uint16_t repeat_time; - uint8_t repeat_count; - uint16_t bvci; - uint8_t t3142; - uint8_t t3169; - uint8_t t3191; - uint8_t t3193_10ms; - uint8_t t3195; - uint8_t n3101; - uint8_t n3103; - uint8_t n3105; - uint8_t cv_countdown; - uint16_t dl_tbf_ext; - uint16_t ul_tbf_ext; - uint8_t initial_cs; - uint8_t initial_mcs; - /* NSVC */ - uint16_t nsvci[2]; - uint16_t local_port[2]; - uint16_t remote_port[2]; - uint32_t remote_ip[2]; -} __attribute__ ((packed)); - -struct gsm_pcu_if_act_req { - uint8_t activate; - uint8_t trx_nr; - uint8_t ts_nr; - uint8_t spare; -} __attribute__ ((packed)); - -struct gsm_pcu_if_time_ind { - uint32_t fn; -} __attribute__ ((packed)); - -struct gsm_pcu_if_pag_req { - uint8_t sapi; - uint8_t chan_needed; - uint8_t identity_lv[9]; -} __attribute__ ((packed)); - -struct gsm_pcu_if { - /* context based information */ - uint8_t msg_type; /* message type */ - uint8_t bts_nr; /* bts number */ - uint8_t spare[2]; - - union { - struct gsm_pcu_if_data data_req; - struct gsm_pcu_if_data data_cnf; - struct gsm_pcu_if_data_cnf_dt data_cnf_dt; - struct gsm_pcu_if_data data_ind; - struct gsm_pcu_if_rts_req rts_req; - struct gsm_pcu_if_rach_ind rach_ind; - struct gsm_pcu_if_info_ind info_ind; - struct gsm_pcu_if_act_req act_req; - struct gsm_pcu_if_time_ind time_ind; - struct gsm_pcu_if_pag_req pag_req; - } u; -} __attribute__ ((packed)); - -#endif /* _PCUIF_PROTO_H */ diff --git a/openbsc/include/openbsc/rest_octets.h b/openbsc/include/openbsc/rest_octets.h deleted file mode 100644 index 49a23129..00000000 --- a/openbsc/include/openbsc/rest_octets.h +++ /dev/null @@ -1,139 +0,0 @@ -#ifndef _REST_OCTETS_H -#define _REST_OCTETS_H - -#include -#include -#include - -/* generate SI1 rest octets */ -int rest_octets_si1(uint8_t *data, uint8_t *nch_pos, int is1800_net); -int rest_octets_si2quater(uint8_t *data, struct gsm_bts *bts); -int rest_octets_si6(uint8_t *data, bool is1800_net); - -struct gsm48_si_selection_params { - uint16_t penalty_time:5, - temp_offs:3, - cell_resel_off:6, - cbq:1, - present:1; -}; - -struct gsm48_si_power_offset { - uint8_t power_offset:2, - present:1; -}; - -struct gsm48_si3_gprs_ind { - uint8_t si13_position:1, - ra_colour:3, - present:1; -}; - -struct gsm48_lsa_params { - uint32_t prio_thr:3, - lsa_offset:3, - mcc:12, - mnc:12; - unsigned int present; -}; - -struct gsm48_si_ro_info { - struct gsm48_si_selection_params selection_params; - struct gsm48_si_power_offset power_offset; - uint8_t si2ter_indicator; - uint8_t early_cm_ctrl; - struct { - uint8_t where:3, - present:1; - } scheduling; - struct gsm48_si3_gprs_ind gprs_ind; - /* SI 3 specific */ - uint8_t si2quater_indicator; - /* SI 4 specific */ - struct gsm48_lsa_params lsa_params; - uint16_t cell_id; - uint8_t break_ind; /* do we have SI7 + SI8 ? */ -}; - - -/* Generate SI3 Rest Octests (Chapter 10.5.2.34 / Table 10.4.72) */ -int rest_octets_si3(uint8_t *data, const struct gsm48_si_ro_info *si3); - -/* Generate SI4 Rest Octets (Chapter 10.5.2.35) */ -int rest_octets_si4(uint8_t *data, const struct gsm48_si_ro_info *si4, int len); - -enum pbcch_carrier_type { - PBCCH_BCCH, - PBCCH_ARFCN, - PBCCH_MAIO -}; - -/* TS 03.60 Chapter 6.3.3.1: Network Mode of Operation */ -enum gprs_nmo { - GPRS_NMO_I = 0, /* CS pagin on GPRS paging or traffic channel */ - GPRS_NMO_II = 1, /* all paging on CCCH */ - GPRS_NMO_III = 2, /* no paging coordination */ -}; - -/* TS 04.60 12.24 */ -struct gprs_cell_options { - enum gprs_nmo nmo; - /* T3168: wait for packet uplink assignment message */ - uint32_t t3168; /* in milliseconds */ - /* T3192: wait for release of the TBF after reception of the final block */ - uint32_t t3192; /* in milliseconds */ - uint32_t drx_timer_max;/* in seconds */ - uint32_t bs_cv_max; - uint8_t supports_egprs_11bit_rach; - bool ctrl_ack_type_use_block; /* use PACKET CONTROL ACKNOWLEDGMENT */ - - uint8_t ext_info_present; - struct { - uint8_t egprs_supported; - uint8_t use_egprs_p_ch_req; - uint8_t bep_period; - uint8_t pfc_supported; - uint8_t dtm_supported; - uint8_t bss_paging_coordination; - } ext_info; -}; - -/* TS 04.60 Table 12.9.2 */ -struct gprs_power_ctrl_pars { - uint8_t alpha; - uint8_t t_avg_w; - uint8_t t_avg_t; - uint8_t pc_meas_chan; - uint8_t n_avg_i; -}; - -struct gsm48_si13_info { - struct gprs_cell_options cell_opts; - struct gprs_power_ctrl_pars pwr_ctrl_pars; - uint8_t bcch_change_mark; - uint8_t si_change_field; - uint8_t pbcch_present; - - union { - struct { - uint8_t rac; - uint8_t spgc_ccch_sup; - uint8_t net_ctrl_ord; - uint8_t prio_acc_thr; - } no_pbcch; - struct { - uint8_t psi1_rep_per; - uint8_t pb; - uint8_t tsc; - uint8_t tn; - enum pbcch_carrier_type carrier_type; - uint16_t arfcn; - uint8_t maio; - } pbcch; - }; -}; - -/* Generate SI13 Rest Octests (Chapter 10.5.2.37b) */ -int rest_octets_si13(uint8_t *data, const struct gsm48_si13_info *si13); - -#endif /* _REST_OCTETS_H */ diff --git a/openbsc/include/openbsc/rrlp.h b/openbsc/include/openbsc/rrlp.h deleted file mode 100644 index c89402a2..00000000 --- a/openbsc/include/openbsc/rrlp.h +++ /dev/null @@ -1,7 +0,0 @@ -#ifndef _RRLP_H -#define _RRLP_H - -void on_dso_load_rrlp(void); - -#endif /* _RRLP_H */ - diff --git a/openbsc/include/openbsc/rs232.h b/openbsc/include/openbsc/rs232.h deleted file mode 100644 index 61187ca6..00000000 --- a/openbsc/include/openbsc/rs232.h +++ /dev/null @@ -1,9 +0,0 @@ -#ifndef _RS232_H -#define _RS232_H - -int rs232_setup(const char *serial_port, unsigned int delay_ms, - struct gsm_bts *bts); - -int handle_serial_msg(struct msgb *msg); - -#endif /* _RS232_H */ diff --git a/openbsc/include/openbsc/rtp_proxy.h b/openbsc/include/openbsc/rtp_proxy.h deleted file mode 100644 index 52ffefd2..00000000 --- a/openbsc/include/openbsc/rtp_proxy.h +++ /dev/null @@ -1,95 +0,0 @@ -#ifndef _RTP_PROXY_H -#define _RTP_PROXY_H - -/* RTP proxy handling for ip.access nanoBTS */ - -/* (C) 2009 by Harald Welte - * All Rights Reserved - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation; either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - * - */ - - -#include - -#include -#include - -#include - -#define RTP_PT_GSM_FULL 3 -#define RTP_PT_GSM_HALF 96 -#define RTP_PT_GSM_EFR 97 -#define RTP_PT_AMR 98 -#define RTP_LEN_GSM_FULL 33 -#define RTP_LEN_GSM_HALF 15 -#define RTP_LEN_GSM_EFR 31 -#define RTP_GSM_DURATION 160 - -enum rtp_rx_action { - RTP_NONE, - RTP_PROXY, - RTP_RECV_UPSTREAM, -}; - -enum rtp_tx_action { - RTP_SEND_NONE, - RTP_SEND_DOWNSTREAM, -}; - -struct rtp_sub_socket { - struct sockaddr_in sin_local; - struct sockaddr_in sin_remote; - - struct osmo_fd bfd; - /* linked list of to-be-transmitted msgb's */ - struct llist_head tx_queue; -}; - -struct rtp_socket { - struct llist_head list; - - struct rtp_sub_socket rtp; - struct rtp_sub_socket rtcp; - - /* what should we do on receive? */ - enum rtp_rx_action rx_action; - union { - struct { - struct rtp_socket *other_sock; - } proxy; - struct { - struct gsm_network *net; - uint32_t callref; - } receive; - }; - enum rtp_tx_action tx_action; - struct { - uint16_t sequence; - uint32_t timestamp; - uint32_t ssrc; - struct timeval last_tv; - } transmit; -}; - -struct rtp_socket *rtp_socket_create(void); -int rtp_socket_bind(struct rtp_socket *rs, uint32_t ip); -int rtp_socket_connect(struct rtp_socket *rs, uint32_t ip, uint16_t port); -int rtp_socket_proxy(struct rtp_socket *this, struct rtp_socket *other); -int rtp_socket_upstream(struct rtp_socket *this, struct gsm_network *net, uint32_t callref); -int rtp_socket_free(struct rtp_socket *rs); -int rtp_send_frame(struct rtp_socket *rs, struct gsm_data_frame *frame); - -#endif /* _RTP_PROXY_H */ diff --git a/openbsc/include/openbsc/sgsn.h b/openbsc/include/openbsc/sgsn.h deleted file mode 100644 index 1ede2c93..00000000 --- a/openbsc/include/openbsc/sgsn.h +++ /dev/null @@ -1,186 +0,0 @@ -#ifndef _SGSN_H -#define _SGSN_H - - -#include -#include -#include -#include -#include - -#include - -struct gprs_gsup_client; -struct hostent; - -enum sgsn_auth_policy { - SGSN_AUTH_POLICY_OPEN, - SGSN_AUTH_POLICY_CLOSED, - SGSN_AUTH_POLICY_ACL_ONLY, - SGSN_AUTH_POLICY_REMOTE -}; - - -enum sgsn_rate_ctr_keys { - CTR_LLC_DL_BYTES, - CTR_LLC_UL_BYTES, - CTR_LLC_DL_PACKETS, - CTR_LLC_UL_PACKETS, - CTR_GPRS_ATTACH_REQUEST, - CTR_GPRS_ATTACH_ACKED, - CTR_GPRS_ATTACH_REJECTED, - CTR_GPRS_DETACH_REQUEST, - CTR_GPRS_DETACH_ACKED, - CTR_GPRS_ROUTING_AREA_REQUEST, - CTR_GPRS_ROUTING_AREA_ACKED, - CTR_GPRS_ROUTING_AREA_REJECT, - /* PDP single packet counter / GSM 04.08 9.5.1 - 9.5.9 */ - CTR_PDP_ACTIVATE_REQUEST, - CTR_PDP_ACTIVATE_REJECT, - CTR_PDP_ACTIVATE_ACCEPT, - CTR_PDP_REQUEST_ACTIVATE, /* unused */ - CTR_PDP_REQUEST_ACTIVATE_REJ, /* unused */ - CTR_PDP_MODIFY_REQUEST, /* unsued */ - CTR_PDP_MODIFY_ACCEPT, /* unused */ - CTR_PDP_DL_DEACTIVATE_REQUEST, - CTR_PDP_DL_DEACTIVATE_ACCEPT, - CTR_PDP_UL_DEACTIVATE_REQUEST, - CTR_PDP_UL_DEACTIVATE_ACCEPT, -}; - -struct sgsn_cdr { - char *filename; - int interval; -}; - -struct sgsn_config { - /* parsed from config file */ - - char *gtp_statedir; - struct sockaddr_in gtp_listenaddr; - - /* misc */ - struct gprs_ns_inst *nsi; - - enum sgsn_auth_policy auth_policy; - enum gprs_ciph_algo cipher; - struct llist_head imsi_acl; - - struct sockaddr_in gsup_server_addr; - int gsup_server_port; - - int require_authentication; - int require_update_location; - - /* CDR configuration */ - struct sgsn_cdr cdr; - - struct { - int T3312; - int T3322; - int T3350; - int T3360; - int T3370; - int T3313; - int T3314; - int T3316; - int T3385; - int T3386; - int T3395; - int T3397; - } timers; - - int dynamic_lookup; - - struct oap_client_config oap; - - /* RFC1144 TCP/IP header compression */ - struct { - int active; - int passive; - int s01; - } pcomp_rfc1144; - - /* V.42vis data compression */ - struct { - int active; - int passive; - int p0; - int p1; - int p2; - } dcomp_v42bis; -}; - -struct sgsn_instance { - char *config_file; - struct sgsn_config cfg; - /* File descriptor wrappers for LibGTP */ - struct osmo_fd gtp_fd0; - struct osmo_fd gtp_fd1c; - struct osmo_fd gtp_fd1u; - /* Timer for libGTP */ - struct osmo_timer_list gtp_timer; - /* GSN instance for libgtp */ - struct gsn_t *gsn; - /* Subscriber */ - struct gsup_client *gsup_client; - /* LLME inactivity timer */ - struct osmo_timer_list llme_timer; - - /* c-ares event loop integration */ - struct osmo_timer_list ares_timer; - struct llist_head ares_fds; - ares_channel ares_channel; - struct ares_addr_node *ares_servers; - - struct rate_ctr_group *rate_ctrs; -}; - -extern struct sgsn_instance *sgsn; - -/* sgsn_vty.c */ - -int sgsn_vty_init(void); -int sgsn_parse_config(const char *config_file, struct sgsn_config *cfg); - -/* sgsn.c */ - -/* Main input function for Gb proxy */ -int sgsn_rcvmsg(struct msgb *msg, struct gprs_nsvc *nsvc, uint16_t ns_bvci); - - -struct sgsn_pdp_ctx *sgsn_create_pdp_ctx(struct sgsn_ggsn_ctx *ggsn, - struct sgsn_mm_ctx *mmctx, - uint16_t nsapi, - struct tlv_parsed *tp); -int sgsn_delete_pdp_ctx(struct sgsn_pdp_ctx *pctx); -void sgsn_pdp_upd_gtp_u(struct sgsn_pdp_ctx *pdp, void *addr, size_t alen); - -/* gprs_sndcp.c */ - -/* Entry point for the SNSM-ACTIVATE.indication */ -int sndcp_sm_activate_ind(struct gprs_llc_lle *lle, uint8_t nsapi); -/* Entry point for the SNSM-DEACTIVATE.indication */ -int sndcp_sm_deactivate_ind(struct gprs_llc_lle *lle, uint8_t nsapi); -/* Called by SNDCP when it has received/re-assembled a N-PDU */ -int sgsn_rx_sndcp_ud_ind(struct gprs_ra_id *ra_id, int32_t tlli, uint8_t nsapi, - struct msgb *msg, uint32_t npdu_len, uint8_t *npdu); -int sndcp_unitdata_req(struct msgb *msg, struct gprs_llc_lle *lle, uint8_t nsapi, - void *mmcontext); -int sndcp_llunitdata_ind(struct msgb *msg, struct gprs_llc_lle *lle, - uint8_t *hdr, uint16_t len); - - -/* - * CDR related functionality - */ -int sgsn_cdr_init(struct sgsn_instance *sgsn); - - -/* - * C-ARES related functionality - */ -int sgsn_ares_init(struct sgsn_instance *sgsn); -int sgsn_ares_query(struct sgsn_instance *sgsm, const char *name, ares_host_callback cb, void *data); - -#endif diff --git a/openbsc/include/openbsc/signal.h b/openbsc/include/openbsc/signal.h deleted file mode 100644 index d4ccf80d..00000000 --- a/openbsc/include/openbsc/signal.h +++ /dev/null @@ -1,262 +0,0 @@ -/* Generic signalling/notification infrastructure */ -/* (C) 2009-2010, 2015 by Holger Hans Peter Freyther - * (C) 2009 by Harald Welte - * (C) 2010 by On-Waves - * All Rights Reserved - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation; either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - * - */ - -#ifndef OPENBSC_SIGNAL_H -#define OPENBSC_SIGNAL_H - -#include -#include - -#include - -#include - -/* - * Signalling subsystems - */ -enum signal_subsystems { - SS_PAGING, - SS_SMS, - SS_ABISIP, - SS_NM, - SS_LCHAN, - SS_SUBSCR, - SS_SCALL, - SS_CHALLOC, - SS_IPAC_NWL, - SS_RF, - SS_MSC, - SS_HO, - SS_CCCH, - SS_SGSN, -}; - -/* SS_PAGING signals */ -enum signal_paging { - S_PAGING_SUCCEEDED, - S_PAGING_EXPIRED, -}; - -/* SS_SMS signals */ -enum signal_sms { - S_SMS_SUBMITTED, /* A SMS has been successfully submitted to us */ - S_SMS_DELIVERED, /* A SMS has been successfully delivered to a MS */ - S_SMS_SMMA, /* A MS tells us it has more space available */ - S_SMS_MEM_EXCEEDED, /* A MS tells us it has no more space available */ - S_SMS_UNKNOWN_ERROR, /* A MS tells us it has an error */ -}; - -/* SS_ABISIP signals */ -enum signal_abisip { - S_ABISIP_CRCX_ACK, - S_ABISIP_MDCX_ACK, - S_ABISIP_DLCX_IND, -}; - -/* SS_NM signals */ -enum signal_nm { - S_NM_SW_ACTIV_REP, /* GSM 12.21 software activated report */ - S_NM_FAIL_REP, /* GSM 12.21 failure event report */ - S_NM_NACK, /* GSM 12.21 various NM_MT_*_NACK happened */ - S_NM_IPACC_NACK, /* GSM 12.21 nanoBTS extensions NM_MT_IPACC_*_*_NACK happened */ - S_NM_IPACC_ACK, /* GSM 12.21 nanoBTS extensions NM_MT_IPACC_*_*_ACK happened */ - S_NM_IPACC_RESTART_ACK, /* nanoBTS has send a restart ack */ - S_NM_IPACC_RESTART_NACK,/* nanoBTS has send a restart ack */ - S_NM_TEST_REP, /* GSM 12.21 Test Report */ - S_NM_STATECHG_OPER, /* Operational State changed*/ - S_NM_STATECHG_ADM, /* Administrative State changed */ - S_NM_OM2K_CONF_RES, /* OM2K Configuration Result */ -}; - -/* SS_LCHAN signals */ -enum signal_lchan { - /* - * The lchan got freed with an use_count != 0 and error - * recovery needs to be carried out from within the - * signal handler. - */ - S_LCHAN_UNEXPECTED_RELEASE, - S_LCHAN_ACTIVATE_ACK, /* 08.58 Channel Activate ACK */ - S_LCHAN_ACTIVATE_NACK, /* 08.58 Channel Activate NACK */ - S_LCHAN_HANDOVER_COMPL, /* 04.08 Handover Completed */ - S_LCHAN_HANDOVER_FAIL, /* 04.08 Handover Failed */ - S_LCHAN_HANDOVER_DETECT, /* 08.58 Handover Detect */ - S_LCHAN_MEAS_REP, /* 08.58 Measurement Report */ -}; - -/* SS_CHALLOC signals */ -enum signal_challoc { - S_CHALLOC_ALLOC_FAIL, /* allocation of lchan has failed */ - S_CHALLOC_FREED, /* lchan has been successfully freed */ -}; - -/* SS_SUBSCR signals */ -enum signal_subscr { - S_SUBSCR_ATTACHED, - S_SUBSCR_DETACHED, - S_SUBSCR_IDENTITY, /* we've received some identity information */ -}; - -/* SS_SCALL signals */ -enum signal_scall { - S_SCALL_SUCCESS, - S_SCALL_EXPIRED, - S_SCALL_DETACHED, -}; - -/* SS_IPAC_NWL signals */ -enum signal_ipaccess { - S_IPAC_NWL_COMPLETE, -}; - -enum signal_global { - S_GLOBAL_BTS_CLOSE_OM, -}; - -/* SS_RF signals */ -enum signal_rf { - S_RF_OFF, - S_RF_ON, - S_RF_GRACE, -}; - -struct gsm_subscriber; - -struct paging_signal_data { - struct gsm_subscriber *subscr; - struct gsm_bts *bts; - - int paging_result; - - /* NULL in case the paging didn't work */ - struct gsm_subscriber_connection *conn; -}; - -struct scall_signal_data { - struct gsm_subscriber_connection *conn; - void *data; -}; - -struct ipacc_ack_signal_data { - struct gsm_bts_trx *trx; - uint8_t msg_type; -}; - -struct abis_om2k_mo; - -struct nm_statechg_signal_data { - struct gsm_bts *bts; - uint8_t obj_class; - void *obj; - struct gsm_nm_state *old_state; - struct gsm_nm_state *new_state; - - /* This pointer is vaold for TS 12.21 MO */ - struct abis_om_obj_inst *obj_inst; - /* This pointer is vaold for RBS2000 MO */ - struct abis_om2k_mo *om2k_mo; -}; - -struct nm_om2k_signal_data { - struct gsm_bts *bts; - void *obj; - struct abis_om2k_mo *om2k_mo; - - uint8_t accordance_ind; -}; - -struct nm_nack_signal_data { - struct msgb *msg; - struct gsm_bts *bts; - uint8_t mt; -}; - -struct challoc_signal_data { - struct gsm_bts *bts; - struct gsm_lchan *lchan; - enum gsm_chan_t type; -}; - -struct rf_signal_data { - struct gsm_network *net; -}; - -struct sms_signal_data { - /* The transaction where this occured */ - struct gsm_trans *trans; - /* Can be NULL for SMMA */ - struct gsm_sms *sms; - /* int paging result. Only the ones with > 0 */ - int paging_result; -}; - -struct lchan_signal_data { - /* The lchan the signal happened on */ - struct gsm_lchan *lchan; - /* Measurement reports on this lchan */ - struct gsm_meas_rep *mr; -}; - -/* MSC signals */ -enum signal_msc { - S_MSC_LOST, - S_MSC_CONNECTED, - S_MSC_AUTHENTICATED, -}; - -struct bsc_msc_data; -struct msc_signal_data { - struct bsc_msc_data *data; -}; - -/* SS_CCCH signals */ -enum signal_ccch { - S_CCCH_PAGING_LOAD, - S_CCCH_RACH_LOAD, -}; - -struct ccch_signal_data { - struct gsm_bts *bts; - uint16_t pg_buf_space; - uint16_t rach_slot_count; - uint16_t rach_busy_count; - uint16_t rach_access_count; -}; - -/* GPRS SGSN signals SS_SGSN */ -enum signal_sgsn { - S_SGSN_ATTACH, - S_SGSN_DETACH, - S_SGSN_UPDATE, - S_SGSN_PDP_ACT, - S_SGSN_PDP_DEACT, - S_SGSN_PDP_TERMINATE, - S_SGSN_PDP_FREE, - S_SGSN_MM_FREE, -}; - -struct sgsn_mm_ctx; -struct sgsn_signal_data { - struct sgsn_mm_ctx *mm; - struct sgsn_pdp_ctx *pdp; /* non-NULL for PDP_ACT, PDP_DEACT, PDP_FREE */ -}; - -#endif diff --git a/openbsc/include/openbsc/silent_call.h b/openbsc/include/openbsc/silent_call.h deleted file mode 100644 index 619a5432..00000000 --- a/openbsc/include/openbsc/silent_call.h +++ /dev/null @@ -1,15 +0,0 @@ -#ifndef _SILENT_CALL_H -#define _SILENT_CALL_H - -struct gsm_subscriber_connection; - -extern int gsm_silent_call_start(struct gsm_subscriber *subscr, - void *data, int type); -extern int gsm_silent_call_stop(struct gsm_subscriber *subscr); - -#if 0 -extern int silent_call_rx(struct gsm_subscriber_connection *conn, struct msgb *msg); -extern int silent_call_reroute(struct gsm_subscriber_connection *conn, struct msgb *msg); -#endif - -#endif /* _SILENT_CALL_H */ diff --git a/openbsc/include/openbsc/slhc.h b/openbsc/include/openbsc/slhc.h deleted file mode 100644 index cd5a47cf..00000000 --- a/openbsc/include/openbsc/slhc.h +++ /dev/null @@ -1,187 +0,0 @@ -#ifndef _SLHC_H -#define _SLHC_H -/* - * Definitions for tcp compression routines. - * - * $Header: slcompress.h,v 1.10 89/12/31 08:53:02 van Exp $ - * - * Copyright (c) 1989 Regents of the University of California. - * All rights reserved. - * - * Redistribution and use in source and binary forms are permitted - * provided that the above copyright notice and this paragraph are - * duplicated in all such forms and that any documentation, - * advertising materials, and other materials related to such - * distribution and use acknowledge that the software was developed - * by the University of California, Berkeley. The name of the - * University may not be used to endorse or promote products derived - * from this software without specific prior written permission. - * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR - * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. - * - * Van Jacobson (van@helios.ee.lbl.gov), Dec 31, 1989: - * - Initial distribution. - * - * - * modified for KA9Q Internet Software Package by - * Katie Stevens (dkstevens@ucdavis.edu) - * University of California, Davis - * Computing Services - * - 01-31-90 initial adaptation - * - * - Feb 1991 Bill_Simpson@um.cc.umich.edu - * variable number of conversation slots - * allow zero or one slots - * separate routines - * status display - */ - -/* - * Compressed packet format: - * - * The first octet contains the packet type (top 3 bits), TCP - * 'push' bit, and flags that indicate which of the 4 TCP sequence - * numbers have changed (bottom 5 bits). The next octet is a - * conversation number that associates a saved IP/TCP header with - * the compressed packet. The next two octets are the TCP checksum - * from the original datagram. The next 0 to 15 octets are - * sequence number changes, one change per bit set in the header - * (there may be no changes and there are two special cases where - * the receiver implicitly knows what changed -- see below). - * - * There are 5 numbers which can change (they are always inserted - * in the following order): TCP urgent pointer, window, - * acknowledgment, sequence number and IP ID. (The urgent pointer - * is different from the others in that its value is sent, not the - * change in value.) Since typical use of SLIP links is biased - * toward small packets (see comments on MTU/MSS below), changes - * use a variable length coding with one octet for numbers in the - * range 1 - 255 and 3 octets (0, MSB, LSB) for numbers in the - * range 256 - 65535 or 0. (If the change in sequence number or - * ack is more than 65535, an uncompressed packet is sent.) - */ - -/* - * Packet types (must not conflict with IP protocol version) - * - * The top nibble of the first octet is the packet type. There are - * three possible types: IP (not proto TCP or tcp with one of the - * control flags set); uncompressed TCP (a normal IP/TCP packet but - * with the 8-bit protocol field replaced by an 8-bit connection id -- - * this type of packet syncs the sender & receiver); and compressed - * TCP (described above). - * - * LSB of 4-bit field is TCP "PUSH" bit (a worthless anachronism) and - * is logically part of the 4-bit "changes" field that follows. Top - * three bits are actual packet type. For backward compatibility - * and in the interest of conserving bits, numbers are chosen so the - * IP protocol version number (4) which normally appears in this nibble - * means "IP packet". - */ - - -#include -#include - -/* SLIP compression masks for len/vers byte */ -#define SL_TYPE_IP 0x40 -#define SL_TYPE_UNCOMPRESSED_TCP 0x70 -#define SL_TYPE_COMPRESSED_TCP 0x80 -#define SL_TYPE_ERROR 0x00 - -/* Bits in first octet of compressed packet */ -#define NEW_C 0x40 /* flag bits for what changed in a packet */ -#define NEW_I 0x20 -#define NEW_S 0x08 -#define NEW_A 0x04 -#define NEW_W 0x02 -#define NEW_U 0x01 - -/* reserved, special-case values of above */ -#define SPECIAL_I (NEW_S|NEW_W|NEW_U) /* echoed interactive traffic */ -#define SPECIAL_D (NEW_S|NEW_A|NEW_W|NEW_U) /* unidirectional data */ -#define SPECIALS_MASK (NEW_S|NEW_A|NEW_W|NEW_U) - -#define TCP_PUSH_BIT 0x10 - -/* - * data type and sizes conversion assumptions: - * - * VJ code KA9Q style generic - * u_char byte_t unsigned char 8 bits - * u_short int16 unsigned short 16 bits - * u_int int16 unsigned short 16 bits - * u_long unsigned long unsigned long 32 bits - * int int32 long 32 bits - */ - -typedef __u8 byte_t; -typedef __u32 int32; - -/* - * "state" data for each active tcp conversation on the wire. This is - * basically a copy of the entire IP/TCP header from the last packet - * we saw from the conversation together with a small identifier - * the transmit & receive ends of the line use to locate saved header. - */ -struct cstate { - byte_t cs_this; /* connection id number (xmit) */ - struct cstate *next; /* next in ring (xmit) */ - struct iphdr cs_ip; /* ip/tcp hdr from most recent packet */ - struct tcphdr cs_tcp; - unsigned char cs_ipopt[64]; - unsigned char cs_tcpopt[64]; - int cs_hsize; -}; -#define NULLSLSTATE (struct cstate *)0 - -/* - * all the state data for one serial line (we need one of these per line). - */ -struct slcompress { - struct cstate *tstate; /* transmit connection states (array)*/ - struct cstate *rstate; /* receive connection states (array)*/ - - byte_t tslot_limit; /* highest transmit slot id (0-l)*/ - byte_t rslot_limit; /* highest receive slot id (0-l)*/ - - byte_t xmit_oldest; /* oldest xmit in ring */ - byte_t xmit_current; /* most recent xmit id */ - byte_t recv_current; /* most recent rcvd id */ - - byte_t flags; -#define SLF_TOSS 0x01 /* tossing rcvd frames until id received */ - - int32 sls_o_nontcp; /* outbound non-TCP packets */ - int32 sls_o_tcp; /* outbound TCP packets */ - int32 sls_o_uncompressed; /* outbound uncompressed packets */ - int32 sls_o_compressed; /* outbound compressed packets */ - int32 sls_o_searches; /* searches for connection state */ - int32 sls_o_misses; /* times couldn't find conn. state */ - - int32 sls_i_uncompressed; /* inbound uncompressed packets */ - int32 sls_i_compressed; /* inbound compressed packets */ - int32 sls_i_error; /* inbound error packets */ - int32 sls_i_tossed; /* inbound packets tossed because of error */ - - int32 sls_i_runt; - int32 sls_i_badcheck; -}; -#define NULLSLCOMPR (struct slcompress *)0 - -/* In slhc.c: */ -struct slcompress *slhc_init(const void *ctx, int rslots, int tslots); - -void slhc_free(struct slcompress *comp); - -int slhc_compress(struct slcompress *comp, unsigned char *icp, int isize, - unsigned char *ocp, unsigned char **cpp, int compress_cid); -int slhc_uncompress(struct slcompress *comp, unsigned char *icp, int isize); -int slhc_remember(struct slcompress *comp, unsigned char *icp, int isize); -int slhc_toss(struct slcompress *comp); - -void slhc_i_status(struct slcompress *comp); -void slhc_o_status(struct slcompress *comp); - -#endif /* _SLHC_H */ diff --git a/openbsc/include/openbsc/smpp.h b/openbsc/include/openbsc/smpp.h deleted file mode 100644 index bcdac8f0..00000000 --- a/openbsc/include/openbsc/smpp.h +++ /dev/null @@ -1,4 +0,0 @@ -#pragma once - -int smpp_openbsc_alloc_init(void *ctx); -int smpp_openbsc_start(struct gsm_network *net); diff --git a/openbsc/include/openbsc/sms_queue.h b/openbsc/include/openbsc/sms_queue.h deleted file mode 100644 index 2a8bd585..00000000 --- a/openbsc/include/openbsc/sms_queue.h +++ /dev/null @@ -1,17 +0,0 @@ -#ifndef SMS_QUEUE_H -#define SMS_QUEUE_H - -struct gsm_network; -struct gsm_sms_queue; -struct vty; - -int sms_queue_start(struct gsm_network *, int in_flight); -int sms_queue_trigger(struct gsm_sms_queue *); - -/* vty helper functions */ -int sms_queue_stats(struct gsm_sms_queue *, struct vty* vty); -int sms_queue_set_max_pending(struct gsm_sms_queue *, int max); -int sms_queue_set_max_failure(struct gsm_sms_queue *, int fail); -int sms_queue_clear(struct gsm_sms_queue *); - -#endif diff --git a/openbsc/include/openbsc/socket.h b/openbsc/include/openbsc/socket.h deleted file mode 100644 index 0fd85f10..00000000 --- a/openbsc/include/openbsc/socket.h +++ /dev/null @@ -1,14 +0,0 @@ -#ifndef _BSC_SOCKET_H -#define _BSC_SOCKET_H - -#include - -#ifndef IPPROTO_GRE -#define IPPROTO_GRE 47 -#endif - -int make_sock(struct osmo_fd *bfd, int proto, - uint32_t ip, uint16_t port, int priv_nr, - int (*cb)(struct osmo_fd *fd, unsigned int what), void *data); - -#endif /* _BSC_SOCKET_H */ diff --git a/openbsc/include/openbsc/system_information.h b/openbsc/include/openbsc/system_information.h deleted file mode 100644 index 71bea266..00000000 --- a/openbsc/include/openbsc/system_information.h +++ /dev/null @@ -1,22 +0,0 @@ -#ifndef _SYSTEM_INFO_H -#define _SYSTEM_INFO_H - -#include - -#include - -struct gsm_bts; - -int gsm_generate_si(struct gsm_bts *bts, enum osmo_sysinfo_type type); -size_t si2q_earfcn_count(const struct osmo_earfcn_si2q *e); -unsigned range1024_p(unsigned n); -unsigned range512_q(unsigned m); -int range_encode(enum gsm48_range r, int *arfcns, int arfcns_used, int *w, - int f0, uint8_t *chan_list); -uint8_t si2q_num(struct gsm_bts *bts); -int bts_earfcn_add(struct gsm_bts *bts, uint16_t earfcn, uint8_t thresh_hi, uint8_t thresh_lo, uint8_t prio, - uint8_t qrx, uint8_t meas_bw); -int bts_uarfcn_del(struct gsm_bts *bts, uint16_t arfcn, uint16_t scramble); -int bts_uarfcn_add(struct gsm_bts *bts, uint16_t arfcn, uint16_t scramble, - bool diversity); -#endif diff --git a/openbsc/include/openbsc/token_auth.h b/openbsc/include/openbsc/token_auth.h deleted file mode 100644 index 47dc7aa9..00000000 --- a/openbsc/include/openbsc/token_auth.h +++ /dev/null @@ -1,7 +0,0 @@ -#ifndef _TOKEN_AUTH_H -#define _TOKEN_AUTH_H - -void on_dso_load_token(void); - -#endif /* _TOKEN_AUTH_H */ - diff --git a/openbsc/include/openbsc/transaction.h b/openbsc/include/openbsc/transaction.h deleted file mode 100644 index 9a87d04e..00000000 --- a/openbsc/include/openbsc/transaction.h +++ /dev/null @@ -1,79 +0,0 @@ -#ifndef _TRANSACT_H -#define _TRANSACT_H - -#include -#include -#include -#include -#include -#include -#include - -/* One transaction */ -struct gsm_trans { - /* Entry in list of all transactions */ - struct llist_head entry; - - /* Back pointer to the network struct */ - struct gsm_network *net; - - /* The protocol within which we live */ - uint8_t protocol; - - /* The current transaction ID */ - uint8_t transaction_id; - - /* To whom we belong, unique identifier of remote MM entity */ - struct gsm_subscriber *subscr; - - /* The associated connection we are using to transmit messages */ - struct gsm_subscriber_connection *conn; - - /* reference from MNCC or other application */ - uint32_t callref; - - /* if traffic channel receive was requested */ - int tch_recv; - - /* is thats one paging? */ - struct subscr_request *paging_request; - - union { - struct { - - /* current call state */ - int state; - - /* current timer and message queue */ - int Tcurrent; /* current CC timer */ - int T308_second; /* used to send release again */ - struct osmo_timer_list timer; - struct gsm_mncc msg; /* stores setup/disconnect/release message */ - } cc; - struct { - struct gsm411_smc_inst smc_inst; - struct gsm411_smr_inst smr_inst; - - struct gsm_sms *sms; - } sms; - }; -}; - - - -struct gsm_trans *trans_find_by_id(struct gsm_subscriber_connection *conn, - uint8_t proto, uint8_t trans_id); -struct gsm_trans *trans_find_by_callref(struct gsm_network *net, - uint32_t callref); - -struct gsm_trans *trans_alloc(struct gsm_network *net, - struct gsm_subscriber *subscr, - uint8_t protocol, uint8_t trans_id, - uint32_t callref); -void trans_free(struct gsm_trans *trans); - -int trans_assign_trans_id(struct gsm_network *net, struct gsm_subscriber *subscr, - uint8_t protocol, uint8_t ti_flag); -int trans_has_conn(const struct gsm_subscriber_connection *conn); - -#endif diff --git a/openbsc/include/openbsc/trau_mux.h b/openbsc/include/openbsc/trau_mux.h deleted file mode 100644 index 75c359b5..00000000 --- a/openbsc/include/openbsc/trau_mux.h +++ /dev/null @@ -1,70 +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 . - * - */ - -/* The "TRAU mux map" defines which particular 16kbit sub-slot (in which E1 - * timeslot on which E1 interface) should be directly muxed to which other - * sub-slot. Entries in the mux map are always bi-directional. - * - * The idea of all this is to directly switch voice channels in the BSC - * from one phone to another. We do this right now since we don't support - * any external interface for voice channels, and in the future as an - * optimization to routing them externally. - */ - -#include -#include -#include - -struct decoded_trau_frame; - -/* map a TRAU mux map entry */ -int trau_mux_map(const struct gsm_e1_subslot *src, - const struct gsm_e1_subslot *dst); -int trau_mux_map_lchan(const struct gsm_lchan *src, - const struct gsm_lchan *dst); - -/* unmap a TRAU mux map entry */ -int trau_mux_unmap(const struct gsm_e1_subslot *ss, uint32_t callref); - -/* we get called by subchan_demux */ -int trau_mux_input(struct gsm_e1_subslot *src_e1_ss, - const uint8_t *trau_bits, int num_bits); - -/* add a trau receiver */ -int trau_recv_lchan(struct gsm_lchan *lchan, uint32_t callref); - -/* send trau from application */ -int trau_send_frame(struct gsm_lchan *lchan, struct gsm_data_frame *frame); - -/* switch trau muxer to new lchan */ -int switch_trau_mux(struct gsm_lchan *old_lchan, struct gsm_lchan *new_lchan); - -/* callback invoked if we receive TRAU frames */ -int subch_cb(struct subch_demux *dmx, int ch, uint8_t *data, int len, void *_priv); - -/* TRAU frame transcoding */ -struct msgb *trau_decode_fr(uint32_t callref, - const struct decoded_trau_frame *tf); -struct msgb *trau_decode_efr(uint32_t callref, - const struct decoded_trau_frame *tf); -void trau_encode_fr(struct decoded_trau_frame *tf, - const unsigned char *data); -void trau_encode_efr(struct decoded_trau_frame *tf, - const unsigned char *data); diff --git a/openbsc/include/openbsc/trau_upqueue.h b/openbsc/include/openbsc/trau_upqueue.h deleted file mode 100644 index ecc76584..00000000 --- a/openbsc/include/openbsc/trau_upqueue.h +++ /dev/null @@ -1,7 +0,0 @@ -#ifndef _TRAU_UPQUEUE_H -#define _TRAU_UPQUEUE_H - -void trau_tx_to_mncc(struct gsm_network *net, struct msgb *msg); - -#endif /* _TRAU_UPQUEUE_H */ - diff --git a/openbsc/include/openbsc/ussd.h b/openbsc/include/openbsc/ussd.h deleted file mode 100644 index 26654681..00000000 --- a/openbsc/include/openbsc/ussd.h +++ /dev/null @@ -1,10 +0,0 @@ -#ifndef _USSD_H -#define _USSD_H - -/* Handler function for mobile-originated USSD messages */ - -#include - -int handle_rcv_ussd(struct gsm_subscriber_connection *conn, struct msgb *msg); - -#endif diff --git a/openbsc/include/openbsc/v42bis.h b/openbsc/include/openbsc/v42bis.h deleted file mode 100644 index 607a58e5..00000000 --- a/openbsc/include/openbsc/v42bis.h +++ /dev/null @@ -1,147 +0,0 @@ -/* - * SpanDSP - a series of DSP components for telephony - * - * v42bis.h - * - * Written by Steve Underwood - * - * Copyright (C) 2005, 2011 Steve Underwood - * - * All rights reserved. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License version 2.1, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this program; if not, write to the Free Software - * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - */ - -/*! \page v42bis_page V.42bis modem data compression -\section v42bis_page_sec_1 What does it do? -The v.42bis specification defines a data compression scheme, to work in -conjunction with the error correction scheme defined in V.42. - -\section v42bis_page_sec_2 How does it work? -*/ - -#include - -#if !defined(_SPANDSP_V42BIS_H_) -#define _SPANDSP_V42BIS_H_ - -#define SPAN_DECLARE(x) x - -#define V42BIS_MIN_STRING_SIZE 6 -#define V42BIS_MAX_STRING_SIZE 250 -#define V42BIS_MIN_DICTIONARY_SIZE 512 -#define V42BIS_MAX_BITS 12 -#define V42BIS_MAX_CODEWORDS 4096 /* 2^V42BIS_MAX_BITS */ -#define V42BIS_MAX_OUTPUT_LENGTH 1024 - -enum -{ - V42BIS_P0_NEITHER_DIRECTION = 0, - V42BIS_P0_INITIATOR_RESPONDER, - V42BIS_P0_RESPONDER_INITIATOR, - V42BIS_P0_BOTH_DIRECTIONS -}; - -enum -{ - V42BIS_COMPRESSION_MODE_DYNAMIC = 0, - V42BIS_COMPRESSION_MODE_ALWAYS, - V42BIS_COMPRESSION_MODE_NEVER -}; - -typedef void (*put_msg_func_t)(void *user_data, const uint8_t *msg, int len); - -/*! - V.42bis compression/decompression descriptor. This defines the working state for a - single instance of V.42bis compress/decompression. -*/ -typedef struct v42bis_state_s v42bis_state_t; - -#if defined(__cplusplus) -extern "C" -{ -#endif - -/*! Compress a block of octets. - \param s The V.42bis context. - \param buf The data to be compressed. - \param len The length of the data buffer. - \return 0 */ -SPAN_DECLARE(int) v42bis_compress(v42bis_state_t *s, const uint8_t buf[], int len); - -/*! Flush out any data remaining in a compression buffer. - \param s The V.42bis context. - \return 0 */ -SPAN_DECLARE(int) v42bis_compress_flush(v42bis_state_t *s); - -/*! Decompress a block of octets. - \param s The V.42bis context. - \param buf The data to be decompressed. - \param len The length of the data buffer. - \return 0 */ -SPAN_DECLARE(int) v42bis_decompress(v42bis_state_t *s, const uint8_t buf[], int len); - -/*! Flush out any data remaining in the decompression buffer. - \param s The V.42bis context. - \return 0 */ -SPAN_DECLARE(int) v42bis_decompress_flush(v42bis_state_t *s); - -/*! Set the compression mode. - \param s The V.42bis context. - \param mode One of the V.42bis compression modes - - V42BIS_COMPRESSION_MODE_DYNAMIC, - V42BIS_COMPRESSION_MODE_ALWAYS, - V42BIS_COMPRESSION_MODE_NEVER */ -SPAN_DECLARE(void) v42bis_compression_control(v42bis_state_t *s, int mode); - -/*! Initialise a V.42bis context. - \param s The V.42bis context. - \param negotiated_p0 The negotiated P0 parameter, from the V.42bis spec. - \param negotiated_p1 The negotiated P1 parameter, from the V.42bis spec. - \param negotiated_p2 The negotiated P2 parameter, from the V.42bis spec. - \param encode_handler Encode callback handler. - \param encode_user_data An opaque pointer passed to the encode callback handler. - \param max_encode_len The maximum length that should be passed to the encode handler. - \param decode_handler Decode callback handler. - \param decode_user_data An opaque pointer passed to the decode callback handler. - \param max_decode_len The maximum length that should be passed to the decode handler. - \return The V.42bis context. */ -SPAN_DECLARE(v42bis_state_t *) v42bis_init(const void *ctx, - v42bis_state_t *s, - int negotiated_p0, - int negotiated_p1, - int negotiated_p2, - put_msg_func_t encode_handler, - void *encode_user_data, - int max_encode_len, - put_msg_func_t decode_handler, - void *decode_user_data, - int max_decode_len); - -/*! Release a V.42bis context. - \param s The V.42bis context. - \return 0 if OK */ -SPAN_DECLARE(int) v42bis_release(v42bis_state_t *s); - -/*! Free a V.42bis context. - \param s The V.42bis context. - \return 0 if OK */ -SPAN_DECLARE(int) v42bis_free(v42bis_state_t *s); - -#if defined(__cplusplus) -} -#endif - -#endif -/*- End of file ------------------------------------------------------------*/ diff --git a/openbsc/include/openbsc/v42bis_private.h b/openbsc/include/openbsc/v42bis_private.h deleted file mode 100644 index daa5ea31..00000000 --- a/openbsc/include/openbsc/v42bis_private.h +++ /dev/null @@ -1,126 +0,0 @@ -/* - * SpanDSP - a series of DSP components for telephony - * - * private/v42bis.h - * - * Written by Steve Underwood - * - * Copyright (C) 2005 Steve Underwood - * - * All rights reserved. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License version 2.1, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this program; if not, write to the Free Software - * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - */ - -#if !defined(_SPANDSP_PRIVATE_V42BIS_H_) -#define _SPANDSP_PRIVATE_V42BIS_H_ - -/*! - V.42bis dictionary node. - Note that 0 is not a valid node to point to (0 is always a control code), so 0 is used - as a "no such value" marker in this structure. -*/ -typedef struct -{ - /*! \brief The value of the octet represented by the current dictionary node */ - uint8_t node_octet; - /*! \brief The parent of this node */ - uint16_t parent; - /*! \brief The first child of this node */ - uint16_t child; - /*! \brief The next node at the same depth */ - uint16_t next; -} v42bis_dict_node_t; - -/*! - V.42bis compression or decompression. This defines the working state for a single instance - of V.42bis compression or decompression. -*/ -typedef struct -{ - /*! \brief Compression enabled. */ - int v42bis_parm_p0; - /*! \brief Compression mode. */ - int compression_mode; - /*! \brief Callback function to handle output data. */ - put_msg_func_t handler; - /*! \brief An opaque pointer passed in calls to the data handler. */ - void *user_data; - /*! \brief The maximum amount to be passed to the data handler. */ - int max_output_len; - - /*! \brief TRUE if we are in transparent (i.e. uncompressable) mode */ - int transparent; - /*! \brief Next empty dictionary entry */ - uint16_t v42bis_parm_c1; - /*! \brief Current codeword size */ - uint16_t v42bis_parm_c2; - /*! \brief Threshold for codeword size change */ - uint16_t v42bis_parm_c3; - /*! \brief The current update point in the dictionary */ - uint16_t update_at; - /*! \brief The last entry matched in the dictionary */ - uint16_t last_matched; - /*! \brief The last entry added to the dictionary */ - uint16_t last_added; - /*! \brief Total number of codewords in the dictionary */ - int v42bis_parm_n2; - /*! \brief Maximum permitted string length */ - int v42bis_parm_n7; - /*! \brief The dictionary */ - v42bis_dict_node_t dict[V42BIS_MAX_CODEWORDS]; - - /*! \brief The octet string in progress */ - uint8_t string[V42BIS_MAX_STRING_SIZE]; - /*! \brief The current length of the octet string in progress */ - int string_length; - /*! \brief The amount of the octet string in progress which has already - been flushed out of the buffer */ - int flushed_length; - - /*! \brief Compression performance metric */ - uint16_t compression_performance; - - /*! \brief Outgoing bit buffer (compression), or incoming bit buffer (decompression) */ - uint32_t bit_buffer; - /*! \brief Outgoing bit count (compression), or incoming bit count (decompression) */ - int bit_count; - - /*! \brief The output composition buffer */ - uint8_t output_buf[V42BIS_MAX_OUTPUT_LENGTH]; - /*! \brief The length of the contents of the output composition buffer */ - int output_octet_count; - - /*! \brief The current value of the escape code */ - uint8_t escape_code; - /*! \brief TRUE if we just hit an escape code, and are waiting for the following octet */ - int escaped; -} v42bis_comp_state_t; - -/*! - V.42bis compression/decompression descriptor. This defines the working state for a - single instance of V.42bis compress/decompression. -*/ -struct v42bis_state_s -{ - /*! \brief Compression state. */ - v42bis_comp_state_t compress; - /*! \brief Decompression state. */ - v42bis_comp_state_t decompress; - - /*! \brief Error and flow logging control */ -}; - -#endif -/*- End of file ------------------------------------------------------------*/ diff --git a/openbsc/include/openbsc/vty.h b/openbsc/include/openbsc/vty.h deleted file mode 100644 index 60b7d2d7..00000000 --- a/openbsc/include/openbsc/vty.h +++ /dev/null @@ -1,51 +0,0 @@ -#ifndef OPENBSC_VTY_H -#define OPENBSC_VTY_H - -#include -#include -#include - -struct gsm_network; -struct vty; - -void openbsc_vty_print_statistics(struct vty *vty, struct gsm_network *); - -struct buffer *vty_argv_to_buffer(int argc, const char *argv[], int base); - -extern struct cmd_element cfg_description_cmd; -extern struct cmd_element cfg_no_description_cmd; - -enum bsc_vty_node { - GSMNET_NODE = _LAST_OSMOVTY_NODE + 1, - BTS_NODE, - TRX_NODE, - TS_NODE, - SUBSCR_NODE, - MGCP_NODE, - GBPROXY_NODE, - SGSN_NODE, - OML_NODE, - NAT_NODE, - NAT_BSC_NODE, - MSC_NODE, - OM2K_NODE, - OM2K_CON_GROUP_NODE, - TRUNK_NODE, - PGROUP_NODE, - MNCC_INT_NODE, - NITB_NODE, - BSC_NODE, - SMPP_NODE, - SMPP_ESME_NODE, - GTPHUB_NODE, -}; - -extern int bsc_vty_is_config_node(struct vty *vty, int node); - -struct log_info; -int bsc_vty_init(struct gsm_network *network); -int bsc_vty_init_extra(void); - -struct gsm_network *gsmnet_from_vty(struct vty *vty); - -#endif diff --git a/openbsc/src/Makefile.am b/openbsc/src/Makefile.am deleted file mode 100644 index cfad7dfe..00000000 --- a/openbsc/src/Makefile.am +++ /dev/null @@ -1,60 +0,0 @@ -AM_CPPFLAGS = \ - $(all_includes) \ - -I$(top_srcdir)/include \ - -I$(top_builddir) \ - $(NULL) - -AM_CFLAGS = \ - -Wall \ - $(LIBOSMOCORE_CFLAGS) \ - $(LIBOSMOGSM_CFLAGS) \ - $(LIBOSMOGSM_CFLAGS) \ - $(LIBOSMOVTY_CFLAGS) \ - $(COVERAGE_CFLAGS) \ - $(NULL) - -AM_LDFLAGS = \ - $(LIBOSMOCORE_LIBS) \ - $(LIBOSMOGSM_LIBS) \ - $(COVERAGE_LDFLAGS) \ - $(NULL) - -# Libraries -SUBDIRS = \ - libcommon \ - libmgcp \ - libbsc \ - libmsc \ - libtrau \ - libfilter \ - libcommon-cs \ - $(NULL) - -# Conditional Libraries -if BUILD_IU -SUBDIRS += \ - libiu \ - $(NULL) -endif - -# Programs -SUBDIRS += \ - osmo-nitb \ - osmo-bsc_mgcp \ - utils \ - ipaccess \ - gprs \ - $(NULL) - -# Conditional Programs -if BUILD_NAT -SUBDIRS += \ - osmo-bsc_nat \ - $(NULL) -endif - -if BUILD_BSC -SUBDIRS += \ - osmo-bsc \ - $(NULL) -endif diff --git a/openbsc/src/gprs/.gitignore b/openbsc/src/gprs/.gitignore deleted file mode 100644 index 7cfefbac..00000000 --- a/openbsc/src/gprs/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -gsn_restart -osmo_*.cfg* diff --git a/openbsc/src/gprs/Makefile.am b/openbsc/src/gprs/Makefile.am deleted file mode 100644 index cb099790..00000000 --- a/openbsc/src/gprs/Makefile.am +++ /dev/null @@ -1,132 +0,0 @@ -AM_CPPFLAGS = \ - $(all_includes) \ - -I$(top_srcdir)/include \ - -I$(top_builddir) \ - $(NULL) - -AM_CFLAGS = \ - -Wall \ - -fno-strict-aliasing \ - $(LIBOSMOCORE_CFLAGS) \ - $(LIBOSMOGSM_CFLAGS) \ - $(LIBOSMOVTY_CFLAGS) \ - $(LIBOSMOCTRL_CFLAGS) \ - $(LIBOSMOABIS_CFLAGS) \ - $(LIBOSMOGB_CFLAGS) \ - $(COVERAGE_CFLAGS) \ - $(LIBCARES_CFLAGS) \ - $(LIBCRYPTO_CFLAGS) \ - $(LIBGTP_CFLAGS) \ - $(NULL) -if BUILD_IU -AM_CFLAGS += \ - $(LIBASN1C_CFLAGS) \ - $(LIBOSMOSIGTRAN_CFLAGS) \ - $(LIBOSMORANAP_CFLAGS) \ - $(NULL) -endif - -OSMO_LIBS = \ - $(LIBOSMOCORE_LIBS) \ - $(LIBOSMOABIS_LIBS) \ - $(LIBOSMOGSM_LIBS) \ - $(LIBOSMOVTY_LIBS) \ - $(LIBOSMOCTRL_LIBS) \ - $(LIBOSMOGB_LIBS) \ - $(LIBGTP_LIBS) \ - $(NULL) - -bin_PROGRAMS = \ - osmo-gbproxy \ - $(NULL) -if HAVE_LIBGTP -if HAVE_LIBCARES -bin_PROGRAMS += \ - osmo-sgsn \ - osmo-gtphub \ - $(NULL) -endif -endif - -osmo_gbproxy_SOURCES = \ - gb_proxy.c \ - gb_proxy_main.c \ - gb_proxy_vty.c \ - gb_proxy_patch.c \ - gb_proxy_tlli.c \ - gb_proxy_peer.c \ - gprs_gb_parse.c \ - gprs_llc_parse.c \ - crc24.c \ - gprs_utils.c \ - $(NULL) -osmo_gbproxy_LDADD = \ - $(top_builddir)/src/libcommon/libcommon.a \ - $(OSMO_LIBS) \ - $(LIBCRYPTO_LIBS) \ - -lrt \ - $(NULL) - -osmo_sgsn_SOURCES = \ - gprs_gmm.c \ - gprs_sgsn.c \ - gprs_sndcp.c \ - gprs_sndcp_comp.c \ - gprs_sndcp_dcomp.c \ - gprs_sndcp_pcomp.c \ - gprs_sndcp_vty.c \ - gprs_sndcp_xid.c \ - sgsn_main.c \ - sgsn_vty.c \ - sgsn_libgtp.c \ - gprs_llc.c \ - gprs_llc_parse.c \ - gprs_llc_vty.c \ - crc24.c \ - sgsn_ctrl.c \ - sgsn_auth.c \ - gprs_subscriber.c \ - gprs_utils.c \ - sgsn_cdr.c \ - sgsn_ares.c \ - slhc.c \ - gprs_llc_xid.c \ - v42bis.c \ - $(NULL) -osmo_sgsn_LDADD = \ - $(top_builddir)/src/libcommon/libcommon.a \ - $(OSMO_LIBS) \ - $(LIBOSMOABIS_LIBS) \ - $(LIBCARES_LIBS) \ - $(LIBCRYPTO_LIBS) \ - $(LIBGTP_LIBS) \ - -lrt \ - -lm \ - $(NULL) -if BUILD_IU -osmo_sgsn_LDADD += \ - $(top_builddir)/src/libiu/libiu.a \ - $(LIBOSMOSIGTRAN_LIBS) \ - $(LIBOSMORANAP_LIBS) \ - $(LIBASN1C_LIBS) \ - $(NULL) -endif - -osmo_gtphub_SOURCES = \ - gtphub_main.c \ - gtphub.c \ - gtphub_sock.c \ - gtphub_ares.c \ - gtphub_vty.c \ - sgsn_ares.c \ - gprs_utils.c \ - $(NULL) -osmo_gtphub_LDADD = \ - $(top_builddir)/src/libcommon/libcommon.a \ - $(LIBOSMOCORE_LIBS) \ - $(LIBOSMOGSM_LIBS) \ - $(LIBOSMOVTY_LIBS) \ - $(LIBCARES_LIBS) \ - $(LIBGTP_LIBS) \ - -lrt \ - $(NULL) diff --git a/openbsc/src/gprs/crc24.c b/openbsc/src/gprs/crc24.c deleted file mode 100644 index 1a420ed6..00000000 --- a/openbsc/src/gprs/crc24.c +++ /dev/null @@ -1,67 +0,0 @@ -/* GPRS LLC CRC-24 Implementation */ - -/* (C) 2008-2009 by Harald Welte - * - * All Rights Reserved - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation; either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - * - */ - -#include - -/* CRC24 table - FCS */ -static const uint32_t tbl_crc24[256] = { - 0x00000000, 0x00d6a776, 0x00f64557, 0x0020e221, 0x00b78115, 0x00612663, 0x0041c442, 0x00976334, - 0x00340991, 0x00e2aee7, 0x00c24cc6, 0x0014ebb0, 0x00838884, 0x00552ff2, 0x0075cdd3, 0x00a36aa5, - 0x00681322, 0x00beb454, 0x009e5675, 0x0048f103, 0x00df9237, 0x00093541, 0x0029d760, 0x00ff7016, - 0x005c1ab3, 0x008abdc5, 0x00aa5fe4, 0x007cf892, 0x00eb9ba6, 0x003d3cd0, 0x001ddef1, 0x00cb7987, - 0x00d02644, 0x00068132, 0x00266313, 0x00f0c465, 0x0067a751, 0x00b10027, 0x0091e206, 0x00474570, - 0x00e42fd5, 0x003288a3, 0x00126a82, 0x00c4cdf4, 0x0053aec0, 0x008509b6, 0x00a5eb97, 0x00734ce1, - 0x00b83566, 0x006e9210, 0x004e7031, 0x0098d747, 0x000fb473, 0x00d91305, 0x00f9f124, 0x002f5652, - 0x008c3cf7, 0x005a9b81, 0x007a79a0, 0x00acded6, 0x003bbde2, 0x00ed1a94, 0x00cdf8b5, 0x001b5fc3, - 0x00fb4733, 0x002de045, 0x000d0264, 0x00dba512, 0x004cc626, 0x009a6150, 0x00ba8371, 0x006c2407, - 0x00cf4ea2, 0x0019e9d4, 0x00390bf5, 0x00efac83, 0x0078cfb7, 0x00ae68c1, 0x008e8ae0, 0x00582d96, - 0x00935411, 0x0045f367, 0x00651146, 0x00b3b630, 0x0024d504, 0x00f27272, 0x00d29053, 0x00043725, - 0x00a75d80, 0x0071faf6, 0x005118d7, 0x0087bfa1, 0x0010dc95, 0x00c67be3, 0x00e699c2, 0x00303eb4, - 0x002b6177, 0x00fdc601, 0x00dd2420, 0x000b8356, 0x009ce062, 0x004a4714, 0x006aa535, 0x00bc0243, - 0x001f68e6, 0x00c9cf90, 0x00e92db1, 0x003f8ac7, 0x00a8e9f3, 0x007e4e85, 0x005eaca4, 0x00880bd2, - 0x00437255, 0x0095d523, 0x00b53702, 0x00639074, 0x00f4f340, 0x00225436, 0x0002b617, 0x00d41161, - 0x00777bc4, 0x00a1dcb2, 0x00813e93, 0x005799e5, 0x00c0fad1, 0x00165da7, 0x0036bf86, 0x00e018f0, - 0x00ad85dd, 0x007b22ab, 0x005bc08a, 0x008d67fc, 0x001a04c8, 0x00cca3be, 0x00ec419f, 0x003ae6e9, - 0x00998c4c, 0x004f2b3a, 0x006fc91b, 0x00b96e6d, 0x002e0d59, 0x00f8aa2f, 0x00d8480e, 0x000eef78, - 0x00c596ff, 0x00133189, 0x0033d3a8, 0x00e574de, 0x007217ea, 0x00a4b09c, 0x008452bd, 0x0052f5cb, - 0x00f19f6e, 0x00273818, 0x0007da39, 0x00d17d4f, 0x00461e7b, 0x0090b90d, 0x00b05b2c, 0x0066fc5a, - 0x007da399, 0x00ab04ef, 0x008be6ce, 0x005d41b8, 0x00ca228c, 0x001c85fa, 0x003c67db, 0x00eac0ad, - 0x0049aa08, 0x009f0d7e, 0x00bfef5f, 0x00694829, 0x00fe2b1d, 0x00288c6b, 0x00086e4a, 0x00dec93c, - 0x0015b0bb, 0x00c317cd, 0x00e3f5ec, 0x0035529a, 0x00a231ae, 0x007496d8, 0x005474f9, 0x0082d38f, - 0x0021b92a, 0x00f71e5c, 0x00d7fc7d, 0x00015b0b, 0x0096383f, 0x00409f49, 0x00607d68, 0x00b6da1e, - 0x0056c2ee, 0x00806598, 0x00a087b9, 0x007620cf, 0x00e143fb, 0x0037e48d, 0x001706ac, 0x00c1a1da, - 0x0062cb7f, 0x00b46c09, 0x00948e28, 0x0042295e, 0x00d54a6a, 0x0003ed1c, 0x00230f3d, 0x00f5a84b, - 0x003ed1cc, 0x00e876ba, 0x00c8949b, 0x001e33ed, 0x008950d9, 0x005ff7af, 0x007f158e, 0x00a9b2f8, - 0x000ad85d, 0x00dc7f2b, 0x00fc9d0a, 0x002a3a7c, 0x00bd5948, 0x006bfe3e, 0x004b1c1f, 0x009dbb69, - 0x0086e4aa, 0x005043dc, 0x0070a1fd, 0x00a6068b, 0x003165bf, 0x00e7c2c9, 0x00c720e8, 0x0011879e, - 0x00b2ed3b, 0x00644a4d, 0x0044a86c, 0x00920f1a, 0x00056c2e, 0x00d3cb58, 0x00f32979, 0x00258e0f, - 0x00eef788, 0x003850fe, 0x0018b2df, 0x00ce15a9, 0x0059769d, 0x008fd1eb, 0x00af33ca, 0x007994bc, - 0x00dafe19, 0x000c596f, 0x002cbb4e, 0x00fa1c38, 0x006d7f0c, 0x00bbd87a, 0x009b3a5b, 0x004d9d2d -}; - -#define INIT_CRC24 0xffffff - -uint32_t crc24_calc(uint32_t fcs, uint8_t *cp, unsigned int len) -{ - while (len--) - fcs = (fcs >> 8) ^ tbl_crc24[(fcs ^ *cp++) & 0xff]; - return fcs; -} diff --git a/openbsc/src/gprs/gb_proxy.c b/openbsc/src/gprs/gb_proxy.c deleted file mode 100644 index d95139f8..00000000 --- a/openbsc/src/gprs/gb_proxy.c +++ /dev/null @@ -1,1437 +0,0 @@ -/* NS-over-IP proxy */ - -/* (C) 2010 by Harald Welte - * (C) 2010-2013 by On-Waves - * (C) 2013 by Holger Hans Peter Freyther - * All Rights Reserved - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation; either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - * - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include - -#include -#include - -#include - -#include -#include -#include -#include - -#include -#include -#include -#include - -#include - -static const struct rate_ctr_desc global_ctr_description[] = { - { "inv-bvci", "Invalid BVC Identifier " }, - { "inv-lai", "Invalid Location Area Identifier" }, - { "inv-rai", "Invalid Routing Area Identifier " }, - { "inv-nsei", "No BVC established for NSEI " }, - { "proto-err.bss", "BSSGP protocol error (BSS )" }, - { "proto-err.sgsn", "BSSGP protocol error (SGSN)" }, - { "not-supp.bss", "Feature not supported (BSS )" }, - { "not-supp.sgsn", "Feature not supported (SGSN)" }, - { "restart.sgsn", "Restarted RESET procedure (SGSN)" }, - { "tx-err.sgsn", "NS Transmission error (SGSN)" }, - { "error", "Other error " }, - { "mod-peer-err", "Patch error: no peer " }, -}; - -static const struct rate_ctr_group_desc global_ctrg_desc = { - .group_name_prefix = "gbproxy.global", - .group_description = "GBProxy Global Statistics", - .num_ctr = ARRAY_SIZE(global_ctr_description), - .ctr_desc = global_ctr_description, - .class_id = OSMO_STATS_CLASS_GLOBAL, -}; - -static int gbprox_relay2peer(struct msgb *old_msg, struct gbproxy_peer *peer, - uint16_t ns_bvci); -static int gbprox_relay2sgsn(struct gbproxy_config *cfg, struct msgb *old_msg, - uint16_t ns_bvci, uint16_t sgsn_nsei); -static void gbproxy_reset_imsi_acquisition(struct gbproxy_link_info* link_info); - -static int check_peer_nsei(struct gbproxy_peer *peer, uint16_t nsei) -{ - if (peer->nsei != nsei) { - LOGP(DGPRS, LOGL_NOTICE, "Peer entry doesn't match current NSEI " - "BVCI=%u via NSEI=%u (expected NSEI=%u)\n", - peer->bvci, nsei, peer->nsei); - rate_ctr_inc(&peer->ctrg->ctr[GBPROX_PEER_CTR_INV_NSEI]); - return 0; - } - - return 1; -} - -/* strip off the NS header */ -static void strip_ns_hdr(struct msgb *msg) -{ - int strip_len = msgb_bssgph(msg) - msg->data; - msgb_pull(msg, strip_len); -} - -/* Transmit Chapter 9.2.10 Identity Request */ -static void gprs_put_identity_req(struct msgb *msg, uint8_t id_type) -{ - struct gsm48_hdr *gh; - - id_type &= GSM_MI_TYPE_MASK; - - gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh) + 1); - gh->proto_discr = GSM48_PDISC_MM_GPRS; - gh->msg_type = GSM48_MT_GMM_ID_REQ; - gh->data[0] = id_type; -} - -/* Transmit Chapter 9.4.6.2 Detach Accept (mobile originated detach) */ -static void gprs_put_mo_detach_acc(struct msgb *msg) -{ - struct gsm48_hdr *gh; - - gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh) + 1); - gh->proto_discr = GSM48_PDISC_MM_GPRS; - gh->msg_type = GSM48_MT_GMM_DETACH_ACK; - gh->data[0] = 0; /* no force to standby */ -} - -static void gprs_push_llc_ui(struct msgb *msg, - int is_uplink, unsigned sapi, unsigned nu) -{ - const uint8_t e_bit = 0; - const uint8_t pm_bit = 1; - const uint8_t cr_bit = is_uplink ? 0 : 1; - uint8_t *llc; - uint8_t *fcs_field; - uint32_t fcs; - - nu &= 0x01ff; /* 9 Bit */ - - llc = msgb_push(msg, 3); - llc[0] = (cr_bit << 6) | (sapi & 0x0f); - llc[1] = 0xc0 | (nu >> 6); /* UI frame */ - llc[2] = (nu << 2) | ((e_bit & 1) << 1) | (pm_bit & 1); - - fcs = gprs_llc_fcs(llc, msgb_length(msg)); - fcs_field = msgb_put(msg, 3); - fcs_field[0] = (uint8_t)(fcs >> 0); - fcs_field[1] = (uint8_t)(fcs >> 8); - fcs_field[2] = (uint8_t)(fcs >> 16); -} - -static void gprs_push_bssgp_dl_unitdata(struct msgb *msg, - uint32_t tlli) -{ - struct bssgp_ud_hdr *budh; - uint8_t *llc = msgb_data(msg); - size_t llc_size = msgb_length(msg); - const size_t llc_ie_hdr_size = 3; - const uint8_t qos_profile[] = {0x00, 0x50, 0x20}; /* hard-coded */ - const uint8_t lifetime[] = {0x02, 0x58}; /* 6s hard-coded */ - - const size_t bssgp_overhead = sizeof(*budh) + - TVLV_GROSS_LEN(sizeof(lifetime)) + llc_ie_hdr_size; - uint8_t *ie; - uint32_t tlli_be = htonl(tlli); - - budh = (struct bssgp_ud_hdr *)msgb_push(msg, bssgp_overhead); - - budh->pdu_type = BSSGP_PDUT_DL_UNITDATA; - memcpy(&budh->tlli, &tlli_be, sizeof(budh->tlli)); - memcpy(&budh->qos_profile, qos_profile, sizeof(budh->qos_profile)); - - ie = budh->data; - tvlv_put(ie, BSSGP_IE_PDU_LIFETIME, sizeof(lifetime), lifetime); - ie += TVLV_GROSS_LEN(sizeof(lifetime)); - - /* Note: Add alignment before the LLC IE if inserting other IE */ - - *(ie++) = BSSGP_IE_LLC_PDU; - *(ie++) = llc_size / 256; - *(ie++) = llc_size % 256; - - OSMO_ASSERT(ie == llc); - - msgb_bssgph(msg) = (uint8_t *)budh; - msgb_tlli(msg) = tlli; -} - -/* update peer according to the BSS message */ -static void gbprox_update_current_raid(uint8_t *raid_enc, - struct gbproxy_peer *peer, - const char *log_text) -{ - struct gbproxy_patch_state *state = &peer->patch_state; - const int old_local_mcc = state->local_mcc; - const int old_local_mnc = state->local_mnc; - struct gprs_ra_id raid; - - if (!raid_enc) - return; - - gsm48_parse_ra(&raid, raid_enc); - - /* save source side MCC/MNC */ - if (!peer->cfg->core_mcc || raid.mcc == peer->cfg->core_mcc) { - state->local_mcc = 0; - } else { - state->local_mcc = raid.mcc; - } - - if (!peer->cfg->core_mnc || raid.mnc == peer->cfg->core_mnc) { - state->local_mnc = 0; - } else { - state->local_mnc = raid.mnc; - } - - if (old_local_mcc != state->local_mcc || - old_local_mnc != state->local_mnc) - LOGP(DGPRS, LOGL_NOTICE, - "Patching RAID %sactivated, msg: %s, " - "local: %d-%d, core: %d-%d\n", - state->local_mcc || state->local_mnc ? - "" : "de", - log_text, - state->local_mcc, state->local_mnc, - peer->cfg->core_mcc, peer->cfg->core_mnc); -} - -uint32_t gbproxy_make_bss_ptmsi(struct gbproxy_peer *peer, - uint32_t sgsn_ptmsi) -{ - uint32_t bss_ptmsi; - int max_retries = 23; - if (!peer->cfg->patch_ptmsi) { - bss_ptmsi = sgsn_ptmsi; - } else { - do { - if (RAND_bytes((uint8_t *) &bss_ptmsi, sizeof(bss_ptmsi)) != 1) { - bss_ptmsi = GSM_RESERVED_TMSI; - break; - } - - bss_ptmsi = bss_ptmsi | 0xC0000000; - - if (gbproxy_link_info_by_ptmsi(peer, bss_ptmsi)) - bss_ptmsi = GSM_RESERVED_TMSI; - } while (bss_ptmsi == GSM_RESERVED_TMSI && max_retries--); - } - - if (bss_ptmsi == GSM_RESERVED_TMSI) - LOGP(DGPRS, LOGL_ERROR, "Failed to allocate a BSS P-TMSI\n"); - - return bss_ptmsi; -} - -uint32_t gbproxy_make_sgsn_tlli(struct gbproxy_peer *peer, - struct gbproxy_link_info *link_info, - uint32_t bss_tlli) -{ - uint32_t sgsn_tlli; - int max_retries = 23; - if (!peer->cfg->patch_ptmsi) { - sgsn_tlli = bss_tlli; - } else if (link_info->sgsn_tlli.ptmsi != GSM_RESERVED_TMSI && - gprs_tlli_type(bss_tlli) == TLLI_FOREIGN) { - sgsn_tlli = gprs_tmsi2tlli(link_info->sgsn_tlli.ptmsi, - TLLI_FOREIGN); - } else if (link_info->sgsn_tlli.ptmsi != GSM_RESERVED_TMSI && - gprs_tlli_type(bss_tlli) == TLLI_LOCAL) { - sgsn_tlli = gprs_tmsi2tlli(link_info->sgsn_tlli.ptmsi, - TLLI_LOCAL); - } else { - do { - /* create random TLLI, 0b01111xxx... */ - if (RAND_bytes((uint8_t *) &sgsn_tlli, sizeof(sgsn_tlli)) != 1) { - sgsn_tlli = 0; - break; - } - - sgsn_tlli = (sgsn_tlli & 0x7fffffff) | 0x78000000; - - if (gbproxy_link_info_by_any_sgsn_tlli(peer, sgsn_tlli)) - sgsn_tlli = 0; - } while (!sgsn_tlli && max_retries--); - } - - if (!sgsn_tlli) - LOGP(DGPRS, LOGL_ERROR, "Failed to allocate an SGSN TLLI\n"); - - return sgsn_tlli; -} - -void gbproxy_reset_link(struct gbproxy_link_info *link_info) -{ - gbproxy_reset_imsi_acquisition(link_info); -} - -/* Returns != 0 iff IMSI acquisition was in progress */ -static int gbproxy_restart_imsi_acquisition(struct gbproxy_link_info* link_info) -{ - int in_progress = 0; - if (!link_info) - return 0; - - if (link_info->imsi_acq_pending) - in_progress = 1; - - gbproxy_link_info_discard_messages(link_info); - link_info->imsi_acq_pending = 0; - - return in_progress; -} - -static void gbproxy_reset_imsi_acquisition(struct gbproxy_link_info* link_info) -{ - gbproxy_restart_imsi_acquisition(link_info); - link_info->vu_gen_tx_bss = GBPROXY_INIT_VU_GEN_TX; -} - -static int gbproxy_flush_stored_messages(struct gbproxy_peer *peer, - struct msgb *msg, - time_t now, - struct gbproxy_link_info* link_info, - struct gprs_gb_parse_context *parse_ctx) -{ - int rc; - struct msgb *stored_msg; - /* Got identity response with IMSI, assuming the request had - * been generated by the gbproxy */ - - LOGP(DLLC, LOGL_DEBUG, - "NSEI=%d(BSS) IMSI acquisition succeeded, " - "flushing stored messages\n", - msgb_nsei(msg)); - - /* Patch and flush stored messages towards the SGSN */ - while ((stored_msg = msgb_dequeue(&link_info->stored_msgs))) { - struct gprs_gb_parse_context tmp_parse_ctx = {0}; - tmp_parse_ctx.to_bss = 0; - tmp_parse_ctx.peer_nsei = msgb_nsei(stored_msg); - int len_change = 0; - - gprs_gb_parse_bssgp(msgb_bssgph(stored_msg), - msgb_bssgp_len(stored_msg), - &tmp_parse_ctx); - gbproxy_patch_bssgp(msg, msgb_bssgph(stored_msg), - msgb_bssgp_len(stored_msg), - peer, link_info, &len_change, - &tmp_parse_ctx); - - rc = gbproxy_update_link_state_after(peer, link_info, now, - &tmp_parse_ctx); - if (rc == 1) { - LOGP(DLLC, LOGL_NOTICE, "link_info deleted while flushing stored messages\n"); - msgb_free(stored_msg); - return -1; - } - - rc = gbprox_relay2sgsn(peer->cfg, stored_msg, - msgb_bvci(msg), link_info->sgsn_nsei); - - if (rc < 0) - LOGP(DLLC, LOGL_ERROR, - "NSEI=%d(BSS) failed to send stored message " - "(%s)\n", - msgb_nsei(msg), - parse_ctx->llc_msg_name ? - parse_ctx->llc_msg_name : "BSSGP"); - msgb_free(stored_msg); - } - - return 0; -} - -static int gbproxy_gsm48_to_peer(struct gbproxy_peer *peer, - struct gbproxy_link_info* link_info, - uint16_t bvci, - struct msgb *msg /* Takes msg ownership */) -{ - int rc; - - /* Workaround to avoid N(U) collisions and to enable a restart - * of the IMSI acquisition procedure. This will work unless the - * SGSN has an initial V(UT) within [256-32, 256+n_retries] - * (see GSM 04.64, 8.4.2). */ - gprs_push_llc_ui(msg, 0, GPRS_SAPI_GMM, link_info->vu_gen_tx_bss); - link_info->vu_gen_tx_bss = (link_info->vu_gen_tx_bss + 1) % 512; - - gprs_push_bssgp_dl_unitdata(msg, link_info->tlli.current); - rc = gbprox_relay2peer(msg, peer, bvci); - msgb_free(msg); - return rc; -} - -static void gbproxy_acquire_imsi(struct gbproxy_peer *peer, - struct gbproxy_link_info* link_info, - uint16_t bvci) -{ - struct msgb *idreq_msg; - - /* Send IDENT REQ */ - idreq_msg = gsm48_msgb_alloc_name("GSM 04.08 ACQ IMSI"); - gprs_put_identity_req(idreq_msg, GSM_MI_TYPE_IMSI); - gbproxy_gsm48_to_peer(peer, link_info, bvci, idreq_msg); -} - -static void gbproxy_tx_detach_acc(struct gbproxy_peer *peer, - struct gbproxy_link_info* link_info, - uint16_t bvci) -{ - struct msgb *detacc_msg; - - /* Send DETACH ACC */ - detacc_msg = gsm48_msgb_alloc_name("GSM 04.08 DET ACC"); - gprs_put_mo_detach_acc(detacc_msg); - gbproxy_gsm48_to_peer(peer, link_info, bvci, detacc_msg); -} - -/* Return != 0 iff msg still needs to be processed */ -static int gbproxy_imsi_acquisition(struct gbproxy_peer *peer, - struct msgb *msg, - time_t now, - struct gbproxy_link_info* link_info, - struct gprs_gb_parse_context *parse_ctx) -{ - struct msgb *stored_msg; - - if (!link_info) - return 1; - - if (!link_info->imsi_acq_pending && link_info->imsi_len > 0) - return 1; - - if (parse_ctx->g48_hdr) - switch (parse_ctx->g48_hdr->msg_type) - { - case GSM48_MT_GMM_RA_UPD_REQ: - case GSM48_MT_GMM_ATTACH_REQ: - if (gbproxy_restart_imsi_acquisition(link_info)) { - LOGP(DLLC, LOGL_INFO, - "NSEI=%d(BSS) IMSI acquisition was in progress " - "when receiving an %s.\n", - msgb_nsei(msg), parse_ctx->llc_msg_name); - } - break; - case GSM48_MT_GMM_DETACH_REQ: - /* Nothing has been sent to the SGSN yet */ - if (link_info->imsi_acq_pending) { - LOGP(DLLC, LOGL_INFO, - "NSEI=%d(BSS) IMSI acquisition was in progress " - "when receiving a DETACH_REQ.\n", - msgb_nsei(msg)); - } - if (!parse_ctx->invalidate_tlli) { - LOGP(DLLC, LOGL_INFO, - "NSEI=%d(BSS) IMSI not yet acquired, " - "faking a DETACH_ACC.\n", - msgb_nsei(msg)); - gbproxy_tx_detach_acc(peer, link_info, msgb_bvci(msg)); - parse_ctx->invalidate_tlli = 1; - } - gbproxy_reset_imsi_acquisition(link_info); - gbproxy_update_link_state_after(peer, link_info, now, - parse_ctx); - return 0; - } - - if (link_info->imsi_acq_pending && link_info->imsi_len > 0) { - int is_ident_resp = - parse_ctx->g48_hdr && - gsm48_hdr_pdisc(parse_ctx->g48_hdr) == GSM48_PDISC_MM_GPRS && - gsm48_hdr_msg_type(parse_ctx->g48_hdr) == GSM48_MT_GMM_ID_RESP; - - /* The IMSI is now available. If flushing the messages fails, - * then link_info has been deleted and we should return - * immediately. */ - if (gbproxy_flush_stored_messages(peer, msg, now, link_info, - parse_ctx) < 0) - return 0; - - gbproxy_reset_imsi_acquisition(link_info); - - /* This message is most probably the response to the ident - * request sent by gbproxy_acquire_imsi(). Don't forward it to - * the SGSN. */ - return !is_ident_resp; - } - - /* The message cannot be processed since the IMSI is still missing */ - - /* Enqueue unpatched messages */ - LOGP(DLLC, LOGL_INFO, - "NSEI=%d(BSS) IMSI acquisition in progress, " - "storing message (%s)\n", - msgb_nsei(msg), - parse_ctx->llc_msg_name ? parse_ctx->llc_msg_name : "BSSGP"); - - stored_msg = gprs_msgb_copy(msg, "process_bssgp_ul"); - msgb_enqueue(&link_info->stored_msgs, stored_msg); - - if (!link_info->imsi_acq_pending) { - LOGP(DLLC, LOGL_INFO, - "NSEI=%d(BSS) IMSI is required but not available, " - "initiating identification procedure (%s)\n", - msgb_nsei(msg), - parse_ctx->llc_msg_name ? parse_ctx->llc_msg_name : "BSSGP"); - - gbproxy_acquire_imsi(peer, link_info, msgb_bvci(msg)); - - /* There is no explicit retransmission handling, the - * implementation relies on the MS doing proper retransmissions - * of the triggering message instead */ - - link_info->imsi_acq_pending = 1; - } - - return 0; -} - -struct gbproxy_peer *gbproxy_find_peer(struct gbproxy_config *cfg, - struct msgb *msg, - struct gprs_gb_parse_context *parse_ctx) -{ - struct gbproxy_peer *peer = NULL; - - if (msgb_bvci(msg) >= 2) - peer = gbproxy_peer_by_bvci(cfg, msgb_bvci(msg)); - - if (!peer && !parse_ctx->to_bss) - peer = gbproxy_peer_by_nsei(cfg, msgb_nsei(msg)); - - if (!peer) - peer = gbproxy_peer_by_bssgp_tlv(cfg, &parse_ctx->bssgp_tp); - - if (!peer) { - LOGP(DLLC, LOGL_INFO, - "NSEI=%d(%s) patching: didn't find peer for message, " - "PDU %d\n", - msgb_nsei(msg), parse_ctx->to_bss ? "BSS" : "SGSN", - parse_ctx->pdu_type); - /* Increment counter */ - rate_ctr_inc(&cfg->ctrg->ctr[GBPROX_GLOB_CTR_PATCH_PEER_ERR]); - } - return peer; -} - -/* patch BSSGP message */ -static int gbprox_process_bssgp_ul(struct gbproxy_config *cfg, - struct msgb *msg, - struct gbproxy_peer *peer) -{ - struct gprs_gb_parse_context parse_ctx = {0}; - int rc; - int len_change = 0; - time_t now; - struct timespec ts = {0,}; - struct gbproxy_link_info *link_info = NULL; - uint32_t sgsn_nsei = cfg->nsip_sgsn_nsei; - - if (!cfg->core_mcc && !cfg->core_mnc && !cfg->core_apn && - !cfg->acquire_imsi && !cfg->patch_ptmsi && !cfg->route_to_sgsn2) - return 1; - - parse_ctx.to_bss = 0; - parse_ctx.peer_nsei = msgb_nsei(msg); - - /* Parse BSSGP/LLC */ - rc = gprs_gb_parse_bssgp(msgb_bssgph(msg), msgb_bssgp_len(msg), - &parse_ctx); - - if (!rc && !parse_ctx.need_decryption) { - LOGP(DGPRS, LOGL_ERROR, - "NSEI=%u(BSS) patching: failed to parse invalid %s message\n", - msgb_nsei(msg), gprs_gb_message_name(&parse_ctx, "NS_UNITDATA")); - gprs_gb_log_parse_context(LOGL_NOTICE, &parse_ctx, "NS_UNITDATA"); - LOGP(DGPRS, LOGL_NOTICE, - "NSEI=%u(BSS) invalid message was: %s\n", - msgb_nsei(msg), msgb_hexdump(msg)); - return 0; - } - - /* Get peer */ - if (!peer) - peer = gbproxy_find_peer(cfg, msg, &parse_ctx); - - if (!peer) - return 0; - - - clock_gettime(CLOCK_MONOTONIC, &ts); - now = ts.tv_sec; - - gbprox_update_current_raid(parse_ctx.bssgp_raid_enc, peer, - parse_ctx.llc_msg_name); - - gprs_gb_log_parse_context(LOGL_DEBUG, &parse_ctx, "NS_UNITDATA"); - - link_info = gbproxy_update_link_state_ul(peer, now, &parse_ctx); - - if (parse_ctx.g48_hdr) { - switch (parse_ctx.g48_hdr->msg_type) { - case GSM48_MT_GMM_ATTACH_REQ: - rate_ctr_inc(&peer->ctrg->ctr[GBPROX_PEER_CTR_ATTACH_REQS]); - break; - case GSM48_MT_GMM_DETACH_REQ: - rate_ctr_inc(&peer->ctrg->ctr[GBPROX_PEER_CTR_DETACH_REQS]); - break; - case GSM48_MT_GMM_ATTACH_COMPL: - rate_ctr_inc(&peer->ctrg->ctr[GBPROX_PEER_CTR_ATTACH_COMPLS]); - break; - case GSM48_MT_GMM_RA_UPD_REQ: - rate_ctr_inc(&peer->ctrg->ctr[GBPROX_PEER_CTR_RA_UPD_REQS]); - break; - case GSM48_MT_GMM_RA_UPD_COMPL: - rate_ctr_inc(&peer->ctrg->ctr[GBPROX_PEER_CTR_RA_UPD_COMPLS]); - break; - case GSM48_MT_GMM_STATUS: - rate_ctr_inc(&peer->ctrg->ctr[GBPROX_PEER_CTR_GMM_STATUS_BSS]); - break; - case GSM48_MT_GSM_ACT_PDP_REQ: - rate_ctr_inc(&peer->ctrg->ctr[GBPROX_PEER_CTR_PDP_ACT_REQS]); - break; - case GSM48_MT_GSM_DEACT_PDP_REQ: - rate_ctr_inc(&peer->ctrg->ctr[GBPROX_PEER_CTR_PDP_DEACT_REQS]); - break; - - default: - break; - } - } - - if (link_info && cfg->route_to_sgsn2) { - if (cfg->acquire_imsi && link_info->imsi_len == 0) - sgsn_nsei = 0xffff; - else if (gbproxy_imsi_matches(cfg, GBPROX_MATCH_ROUTING, - link_info)) - sgsn_nsei = cfg->nsip_sgsn2_nsei; - } - - if (link_info) - link_info->sgsn_nsei = sgsn_nsei; - - /* Handle IMSI acquisition */ - if (cfg->acquire_imsi) { - rc = gbproxy_imsi_acquisition(peer, msg, now, link_info, - &parse_ctx); - if (rc <= 0) - return rc; - } - - gbproxy_patch_bssgp(msg, msgb_bssgph(msg), msgb_bssgp_len(msg), - peer, link_info, &len_change, &parse_ctx); - - gbproxy_update_link_state_after(peer, link_info, now, &parse_ctx); - - if (sgsn_nsei != cfg->nsip_sgsn_nsei) { - /* Send message directly to the selected SGSN */ - rc = gbprox_relay2sgsn(cfg, msg, msgb_bvci(msg), sgsn_nsei); - /* Don't let the calling code handle the transmission */ - return 0; - } - - return 1; -} - -/* patch BSSGP message to use core_mcc/mnc on the SGSN side */ -static void gbprox_process_bssgp_dl(struct gbproxy_config *cfg, - struct msgb *msg, - struct gbproxy_peer *peer) -{ - struct gprs_gb_parse_context parse_ctx = {0}; - int rc; - int len_change = 0; - time_t now; - struct timespec ts = {0,}; - struct gbproxy_link_info *link_info = NULL; - - if (!cfg->core_mcc && !cfg->core_mnc && !cfg->core_apn && - !cfg->acquire_imsi && !cfg->patch_ptmsi && !cfg->route_to_sgsn2) - return; - - parse_ctx.to_bss = 1; - parse_ctx.peer_nsei = msgb_nsei(msg); - - rc = gprs_gb_parse_bssgp(msgb_bssgph(msg), msgb_bssgp_len(msg), - &parse_ctx); - - if (!rc && !parse_ctx.need_decryption) { - LOGP(DGPRS, LOGL_ERROR, - "NSEI=%u(SGSN) patching: failed to parse invalid %s message\n", - msgb_nsei(msg), gprs_gb_message_name(&parse_ctx, "NS_UNITDATA")); - gprs_gb_log_parse_context(LOGL_NOTICE, &parse_ctx, "NS_UNITDATA"); - LOGP(DGPRS, LOGL_NOTICE, - "NSEI=%u(SGSN) invalid message was: %s\n", - msgb_nsei(msg), msgb_hexdump(msg)); - return; - } - - /* Get peer */ - if (!peer) - peer = gbproxy_find_peer(cfg, msg, &parse_ctx); - - if (!peer) - return; - - clock_gettime(CLOCK_MONOTONIC, &ts); - now = ts.tv_sec; - - if (parse_ctx.g48_hdr) { - switch (parse_ctx.g48_hdr->msg_type) { - case GSM48_MT_GMM_ATTACH_ACK: - rate_ctr_inc(&peer->ctrg->ctr[GBPROX_PEER_CTR_ATTACH_ACKS]); - break; - case GSM48_MT_GMM_ATTACH_REJ: - rate_ctr_inc(&peer->ctrg->ctr[GBPROX_PEER_CTR_ATTACH_REJS]); - break; - case GSM48_MT_GMM_DETACH_ACK: - rate_ctr_inc(&peer->ctrg->ctr[GBPROX_PEER_CTR_DETACH_ACKS]); - break; - case GSM48_MT_GMM_RA_UPD_ACK: - rate_ctr_inc(&peer->ctrg->ctr[GBPROX_PEER_CTR_RA_UPD_ACKS]); - break; - case GSM48_MT_GMM_RA_UPD_REJ: - rate_ctr_inc(&peer->ctrg->ctr[GBPROX_PEER_CTR_RA_UPD_REJS]); - break; - case GSM48_MT_GMM_STATUS: - rate_ctr_inc(&peer->ctrg->ctr[GBPROX_PEER_CTR_GMM_STATUS_SGSN]); - break; - case GSM48_MT_GSM_ACT_PDP_ACK: - rate_ctr_inc(&peer->ctrg->ctr[GBPROX_PEER_CTR_PDP_ACT_ACKS]); - break; - case GSM48_MT_GSM_ACT_PDP_REJ: - rate_ctr_inc(&peer->ctrg->ctr[GBPROX_PEER_CTR_PDP_ACT_REJS]); - break; - case GSM48_MT_GSM_DEACT_PDP_ACK: - rate_ctr_inc(&peer->ctrg->ctr[GBPROX_PEER_CTR_PDP_DEACT_ACKS]); - break; - - default: - break; - } - } - - gprs_gb_log_parse_context(LOGL_DEBUG, &parse_ctx, "NS_UNITDATA"); - - link_info = gbproxy_update_link_state_dl(peer, now, &parse_ctx); - - gbproxy_patch_bssgp(msg, msgb_bssgph(msg), msgb_bssgp_len(msg), - peer, link_info, &len_change, &parse_ctx); - - gbproxy_update_link_state_after(peer, link_info, now, &parse_ctx); - - return; -} - -/* feed a message down the NS-VC associated with the specified peer */ -static int gbprox_relay2sgsn(struct gbproxy_config *cfg, struct msgb *old_msg, - uint16_t ns_bvci, uint16_t sgsn_nsei) -{ - /* create a copy of the message so the old one can - * be free()d safely when we return from gbprox_rcvmsg() */ - struct msgb *msg = gprs_msgb_copy(old_msg, "msgb_relay2sgsn"); - int rc; - - DEBUGP(DGPRS, "NSEI=%u proxying BTS->SGSN (NS_BVCI=%u, NSEI=%u)\n", - msgb_nsei(msg), ns_bvci, sgsn_nsei); - - msgb_bvci(msg) = ns_bvci; - msgb_nsei(msg) = sgsn_nsei; - - strip_ns_hdr(msg); - - rc = gprs_ns_sendmsg(bssgp_nsi, msg); - if (rc < 0) - rate_ctr_inc(&cfg->ctrg->ctr[GBPROX_GLOB_CTR_TX_ERR_SGSN]); - - return rc; -} - -/* feed a message down the NS-VC associated with the specified peer */ -static int gbprox_relay2peer(struct msgb *old_msg, struct gbproxy_peer *peer, - uint16_t ns_bvci) -{ - /* create a copy of the message so the old one can - * be free()d safely when we return from gbprox_rcvmsg() */ - struct msgb *msg = gprs_msgb_copy(old_msg, "msgb_relay2peer"); - int rc; - - DEBUGP(DGPRS, "NSEI=%u proxying SGSN->BSS (NS_BVCI=%u, NSEI=%u)\n", - msgb_nsei(msg), ns_bvci, peer->nsei); - - msgb_bvci(msg) = ns_bvci; - msgb_nsei(msg) = peer->nsei; - - /* Strip the old NS header, it will be replaced with a new one */ - strip_ns_hdr(msg); - - rc = gprs_ns_sendmsg(bssgp_nsi, msg); - if (rc < 0) - rate_ctr_inc(&peer->ctrg->ctr[GBPROX_PEER_CTR_TX_ERR]); - - return rc; -} - -static int block_unblock_peer(struct gbproxy_config *cfg, uint16_t ptp_bvci, uint8_t pdu_type) -{ - struct gbproxy_peer *peer; - - peer = gbproxy_peer_by_bvci(cfg, ptp_bvci); - if (!peer) { - LOGP(DGPRS, LOGL_ERROR, "BVCI=%u: Cannot find BSS\n", - ptp_bvci); - rate_ctr_inc(&cfg->ctrg->ctr[GBPROX_GLOB_CTR_INV_BVCI]); - return -ENOENT; - } - - switch (pdu_type) { - case BSSGP_PDUT_BVC_BLOCK_ACK: - peer->blocked = 1; - rate_ctr_inc(&peer->ctrg->ctr[GBPROX_PEER_CTR_BLOCKED]); - break; - case BSSGP_PDUT_BVC_UNBLOCK_ACK: - peer->blocked = 0; - rate_ctr_inc(&peer->ctrg->ctr[GBPROX_PEER_CTR_UNBLOCKED]); - break; - default: - break; - } - return 0; -} - -/* Send a message to a peer identified by ptp_bvci but using ns_bvci - * in the NS hdr */ -static int gbprox_relay2bvci(struct gbproxy_config *cfg, struct msgb *msg, uint16_t ptp_bvci, - uint16_t ns_bvci) -{ - struct gbproxy_peer *peer; - - peer = gbproxy_peer_by_bvci(cfg, ptp_bvci); - if (!peer) { - LOGP(DGPRS, LOGL_ERROR, "BVCI=%u: Cannot find BSS\n", - ptp_bvci); - rate_ctr_inc(&cfg->ctrg->ctr[GBPROX_GLOB_CTR_INV_BVCI]); - return -ENOENT; - } - - return gbprox_relay2peer(msg, peer, ns_bvci); -} - -int bssgp_prim_cb(struct osmo_prim_hdr *oph, void *ctx) -{ - return 0; -} - -/* Receive an incoming PTP message from a BSS-side NS-VC */ -static int gbprox_rx_ptp_from_bss(struct gbproxy_config *cfg, - struct msgb *msg, uint16_t nsei, - uint16_t nsvci, uint16_t ns_bvci) -{ - struct gbproxy_peer *peer; - struct bssgp_normal_hdr *bgph = (struct bssgp_normal_hdr *) msgb_bssgph(msg); - uint8_t pdu_type = bgph->pdu_type; - int rc; - - peer = gbproxy_peer_by_bvci(cfg, ns_bvci); - if (!peer) { - LOGP(DGPRS, LOGL_NOTICE, "Didn't find peer for " - "BVCI=%u for PTP message from NSVC=%u/NSEI=%u (BSS), " - "discarding message\n", - ns_bvci, nsvci, nsei); - return bssgp_tx_status(BSSGP_CAUSE_UNKNOWN_BVCI, - &ns_bvci, msg); - } - - check_peer_nsei(peer, nsei); - - rc = gbprox_process_bssgp_ul(cfg, msg, peer); - if (!rc) - return 0; - - switch (pdu_type) { - case BSSGP_PDUT_FLOW_CONTROL_BVC: - if (!cfg->route_to_sgsn2) - break; - - /* Send a copy to the secondary SGSN */ - gbprox_relay2sgsn(cfg, msg, ns_bvci, cfg->nsip_sgsn2_nsei); - break; - default: - break; - } - - - return gbprox_relay2sgsn(cfg, msg, ns_bvci, cfg->nsip_sgsn_nsei); -} - -/* Receive an incoming PTP message from a SGSN-side NS-VC */ -static int gbprox_rx_ptp_from_sgsn(struct gbproxy_config *cfg, - struct msgb *msg, uint16_t nsei, - uint16_t nsvci, uint16_t ns_bvci) -{ - struct gbproxy_peer *peer; - struct bssgp_normal_hdr *bgph = (struct bssgp_normal_hdr *) msgb_bssgph(msg); - uint8_t pdu_type = bgph->pdu_type; - - peer = gbproxy_peer_by_bvci(cfg, ns_bvci); - - /* Send status messages before patching */ - - if (!peer) { - LOGP(DGPRS, LOGL_INFO, "Didn't find peer for " - "BVCI=%u for message from NSVC=%u/NSEI=%u (SGSN)\n", - ns_bvci, nsvci, nsei); - rate_ctr_inc(&cfg->ctrg-> - ctr[GBPROX_GLOB_CTR_INV_BVCI]); - return bssgp_tx_status(BSSGP_CAUSE_UNKNOWN_BVCI, - &ns_bvci, msg); - } - - if (peer->blocked) { - LOGP(DGPRS, LOGL_NOTICE, "Dropping PDU for " - "blocked BVCI=%u via NSVC=%u/NSEI=%u\n", - ns_bvci, nsvci, nsei); - rate_ctr_inc(&peer->ctrg->ctr[GBPROX_PEER_CTR_DROPPED]); - return bssgp_tx_status(BSSGP_CAUSE_BVCI_BLOCKED, &ns_bvci, msg); - } - - switch (pdu_type) { - case BSSGP_PDUT_FLOW_CONTROL_BVC_ACK: - case BSSGP_PDUT_BVC_BLOCK_ACK: - case BSSGP_PDUT_BVC_UNBLOCK_ACK: - if (cfg->route_to_sgsn2 && nsei == cfg->nsip_sgsn2_nsei) - /* Hide ACKs from the secondary SGSN, the primary SGSN - * is responsible to send them. */ - return 0; - break; - default: - break; - } - - /* Optionally patch the message */ - gbprox_process_bssgp_dl(cfg, msg, peer); - - return gbprox_relay2peer(msg, peer, ns_bvci); -} - -/* Receive an incoming signalling message from a BSS-side NS-VC */ -static int gbprox_rx_sig_from_bss(struct gbproxy_config *cfg, - struct msgb *msg, uint16_t nsei, - uint16_t ns_bvci) -{ - struct bssgp_normal_hdr *bgph = (struct bssgp_normal_hdr *) msgb_bssgph(msg); - struct tlv_parsed tp; - uint8_t pdu_type = bgph->pdu_type; - int data_len = msgb_bssgp_len(msg) - sizeof(*bgph); - struct gbproxy_peer *from_peer = NULL; - struct gprs_ra_id raid; - int copy_to_sgsn2 = 0; - int rc; - - if (ns_bvci != 0 && ns_bvci != 1) { - LOGP(DGPRS, LOGL_NOTICE, "NSEI=%u BVCI=%u is not signalling\n", - nsei, ns_bvci); - return -EINVAL; - } - - /* we actually should never see those two for BVCI == 0, but double-check - * just to make sure */ - if (pdu_type == BSSGP_PDUT_UL_UNITDATA || - pdu_type == BSSGP_PDUT_DL_UNITDATA) { - LOGP(DGPRS, LOGL_NOTICE, "NSEI=%u UNITDATA not allowed in " - "signalling\n", nsei); - return -EINVAL; - } - - bssgp_tlv_parse(&tp, bgph->data, data_len); - - switch (pdu_type) { - case BSSGP_PDUT_SUSPEND: - case BSSGP_PDUT_RESUME: - /* We implement RAI snooping during SUSPEND/RESUME, since it - * establishes a relationsip between BVCI/peer and the routeing - * area identification. The snooped information is then used - * for routing the {SUSPEND,RESUME}_[N]ACK back to the correct - * BSSGP */ - if (!TLVP_PRESENT(&tp, BSSGP_IE_ROUTEING_AREA)) - goto err_mand_ie; - from_peer = gbproxy_peer_by_nsei(cfg, nsei); - if (!from_peer) - goto err_no_peer; - memcpy(from_peer->ra, TLVP_VAL(&tp, BSSGP_IE_ROUTEING_AREA), - sizeof(from_peer->ra)); - gsm48_parse_ra(&raid, from_peer->ra); - LOGP(DGPRS, LOGL_INFO, "NSEI=%u BSSGP SUSPEND/RESUME " - "RAI snooping: RAI %u-%u-%u-%u behind BVCI=%u\n", - nsei, raid.mcc, raid.mnc, raid.lac, - raid.rac , from_peer->bvci); - /* FIXME: This only supports one BSS per RA */ - break; - case BSSGP_PDUT_BVC_RESET: - /* If we receive a BVC reset on the signalling endpoint, we - * don't want the SGSN to reset, as the signalling endpoint - * is common for all point-to-point BVCs (and thus all BTS) */ - if (TLVP_PRESENT(&tp, BSSGP_IE_BVCI)) { - uint16_t bvci = ntohs(tlvp_val16_unal(&tp, BSSGP_IE_BVCI)); - LOGP(DGPRS, LOGL_INFO, "NSEI=%u Rx BVC RESET (BVCI=%u)\n", - nsei, bvci); - if (bvci == 0) { - /* FIXME: only do this if SGSN is alive! */ - LOGP(DGPRS, LOGL_INFO, "NSEI=%u Tx fake " - "BVC RESET ACK of BVCI=0\n", nsei); - return bssgp_tx_simple_bvci(BSSGP_PDUT_BVC_RESET_ACK, - nsei, 0, ns_bvci); - } - from_peer = gbproxy_peer_by_bvci(cfg, bvci); - if (!from_peer) { - /* if a PTP-BVC is reset, and we don't know that - * PTP-BVCI yet, we should allocate a new peer */ - LOGP(DGPRS, LOGL_INFO, "Allocationg new peer for " - "BVCI=%u via NSEI=%u\n", bvci, nsei); - from_peer = gbproxy_peer_alloc(cfg, bvci); - from_peer->nsei = nsei; - } - - if (!check_peer_nsei(from_peer, nsei)) - from_peer->nsei = nsei; - - if (TLVP_PRESENT(&tp, BSSGP_IE_CELL_ID)) { - struct gprs_ra_id raid; - /* We have a Cell Identifier present in this - * PDU, this means we can extend our local - * state information about this particular cell - * */ - memcpy(from_peer->ra, - TLVP_VAL(&tp, BSSGP_IE_CELL_ID), - sizeof(from_peer->ra)); - gsm48_parse_ra(&raid, from_peer->ra); - LOGP(DGPRS, LOGL_INFO, "NSEI=%u/BVCI=%u " - "Cell ID %u-%u-%u-%u\n", nsei, - bvci, raid.mcc, raid.mnc, raid.lac, - raid.rac); - } - if (cfg->route_to_sgsn2) - copy_to_sgsn2 = 1; - } - break; - } - - /* Normally, we can simply pass on all signalling messages from BSS to - * SGSN */ - rc = gbprox_process_bssgp_ul(cfg, msg, from_peer); - if (!rc) - return 0; - - if (copy_to_sgsn2) - gbprox_relay2sgsn(cfg, msg, ns_bvci, cfg->nsip_sgsn2_nsei); - - return gbprox_relay2sgsn(cfg, msg, ns_bvci, cfg->nsip_sgsn_nsei); -err_no_peer: - LOGP(DGPRS, LOGL_ERROR, "NSEI=%u(BSS) cannot find peer based on NSEI\n", - nsei); - rate_ctr_inc(&cfg->ctrg->ctr[GBPROX_GLOB_CTR_INV_NSEI]); - return bssgp_tx_status(BSSGP_CAUSE_INV_MAND_INF, NULL, msg); -err_mand_ie: - LOGP(DGPRS, LOGL_ERROR, "NSEI=%u(BSS) missing mandatory RA IE\n", - nsei); - rate_ctr_inc(&cfg->ctrg->ctr[GBPROX_GLOB_CTR_PROTO_ERR_BSS]); - return bssgp_tx_status(BSSGP_CAUSE_MISSING_MAND_IE, NULL, msg); -} - -/* Receive paging request from SGSN, we need to relay to proper BSS */ -static int gbprox_rx_paging(struct gbproxy_config *cfg, struct msgb *msg, struct tlv_parsed *tp, - uint32_t nsei, uint16_t ns_bvci) -{ - struct gbproxy_peer *peer = NULL; - int errctr = GBPROX_GLOB_CTR_PROTO_ERR_SGSN; - - LOGP(DGPRS, LOGL_INFO, "NSEI=%u(SGSN) BSSGP PAGING ", - nsei); - if (TLVP_PRESENT(tp, BSSGP_IE_BVCI)) { - uint16_t bvci = ntohs(tlvp_val16_unal(tp, BSSGP_IE_BVCI)); - LOGPC(DGPRS, LOGL_INFO, "routing by BVCI to peer BVCI=%u\n", - bvci); - errctr = GBPROX_GLOB_CTR_OTHER_ERR; - } else if (TLVP_PRESENT(tp, BSSGP_IE_ROUTEING_AREA)) { - peer = gbproxy_peer_by_rai(cfg, TLVP_VAL(tp, BSSGP_IE_ROUTEING_AREA)); - LOGPC(DGPRS, LOGL_INFO, "routing by RAI to peer BVCI=%u\n", - peer ? peer->bvci : -1); - errctr = GBPROX_GLOB_CTR_INV_RAI; - } else if (TLVP_PRESENT(tp, BSSGP_IE_LOCATION_AREA)) { - peer = gbproxy_peer_by_lai(cfg, TLVP_VAL(tp, BSSGP_IE_LOCATION_AREA)); - LOGPC(DGPRS, LOGL_INFO, "routing by LAI to peer BVCI=%u\n", - peer ? peer->bvci : -1); - errctr = GBPROX_GLOB_CTR_INV_LAI; - } else - LOGPC(DGPRS, LOGL_INFO, "\n"); - - if (!peer) { - LOGP(DGPRS, LOGL_ERROR, "NSEI=%u(SGSN) BSSGP PAGING: " - "unable to route, missing IE\n", nsei); - rate_ctr_inc(&cfg->ctrg->ctr[errctr]); - return -EINVAL; - } - return gbprox_relay2peer(msg, peer, ns_bvci); -} - -/* Receive an incoming BVC-RESET message from the SGSN */ -static int rx_reset_from_sgsn(struct gbproxy_config *cfg, - struct msgb *orig_msg, - struct msgb *msg, struct tlv_parsed *tp, - uint32_t nsei, uint16_t ns_bvci) -{ - struct gbproxy_peer *peer; - uint16_t ptp_bvci; - - if (!TLVP_PRESENT(tp, BSSGP_IE_BVCI)) { - rate_ctr_inc(&cfg->ctrg-> - ctr[GBPROX_GLOB_CTR_PROTO_ERR_SGSN]); - return bssgp_tx_status(BSSGP_CAUSE_MISSING_MAND_IE, - NULL, orig_msg); - } - ptp_bvci = ntohs(tlvp_val16_unal(tp, BSSGP_IE_BVCI)); - - if (ptp_bvci >= 2) { - /* A reset for a PTP BVC was received, forward it to its - * respective peer */ - peer = gbproxy_peer_by_bvci(cfg, ptp_bvci); - if (!peer) { - LOGP(DGPRS, LOGL_ERROR, "NSEI=%u BVCI=%u: Cannot find BSS\n", - nsei, ptp_bvci); - rate_ctr_inc(&cfg->ctrg-> - ctr[GBPROX_GLOB_CTR_INV_BVCI]); - return bssgp_tx_status(BSSGP_CAUSE_UNKNOWN_BVCI, - &ptp_bvci, orig_msg); - } - return gbprox_relay2peer(msg, peer, ns_bvci); - } - - /* A reset for the Signalling entity has been received - * from the SGSN. As the signalling BVCI is shared - * among all the BSS's that we multiplex, it needs to - * be relayed */ - llist_for_each_entry(peer, &cfg->bts_peers, list) - gbprox_relay2peer(msg, peer, ns_bvci); - - return 0; -} - -/* Receive an incoming signalling message from the SGSN-side NS-VC */ -static int gbprox_rx_sig_from_sgsn(struct gbproxy_config *cfg, - struct msgb *orig_msg, uint32_t nsei, - uint16_t ns_bvci) -{ - struct bssgp_normal_hdr *bgph = - (struct bssgp_normal_hdr *) msgb_bssgph(orig_msg); - struct tlv_parsed tp; - uint8_t pdu_type = bgph->pdu_type; - int data_len; - struct gbproxy_peer *peer; - uint16_t bvci; - struct msgb *msg; - int rc = 0; - int cause; - - if (ns_bvci != 0 && ns_bvci != 1) { - LOGP(DGPRS, LOGL_NOTICE, "NSEI=%u(SGSN) BVCI=%u is not " - "signalling\n", nsei, ns_bvci); - /* FIXME: Send proper error message */ - return -EINVAL; - } - - /* we actually should never see those two for BVCI == 0, but double-check - * just to make sure */ - if (pdu_type == BSSGP_PDUT_UL_UNITDATA || - pdu_type == BSSGP_PDUT_DL_UNITDATA) { - LOGP(DGPRS, LOGL_NOTICE, "NSEI=%u(SGSN) UNITDATA not allowed in " - "signalling\n", nsei); - return bssgp_tx_status(BSSGP_CAUSE_PROTO_ERR_UNSPEC, NULL, orig_msg); - } - - msg = gprs_msgb_copy(orig_msg, "rx_sig_from_sgsn"); - gbprox_process_bssgp_dl(cfg, msg, NULL); - /* Update message info */ - bgph = (struct bssgp_normal_hdr *) msgb_bssgph(msg); - data_len = msgb_bssgp_len(orig_msg) - sizeof(*bgph); - rc = bssgp_tlv_parse(&tp, bgph->data, data_len); - - switch (pdu_type) { - case BSSGP_PDUT_BVC_RESET: - rc = rx_reset_from_sgsn(cfg, msg, orig_msg, &tp, nsei, ns_bvci); - break; - case BSSGP_PDUT_BVC_RESET_ACK: - if (cfg->route_to_sgsn2 && nsei == cfg->nsip_sgsn2_nsei) - break; - /* fall through */ - case BSSGP_PDUT_FLUSH_LL: - /* simple case: BVCI IE is mandatory */ - if (!TLVP_PRESENT(&tp, BSSGP_IE_BVCI)) - goto err_mand_ie; - bvci = ntohs(tlvp_val16_unal(&tp, BSSGP_IE_BVCI)); - rc = gbprox_relay2bvci(cfg, msg, bvci, ns_bvci); - break; - case BSSGP_PDUT_PAGING_PS: - case BSSGP_PDUT_PAGING_CS: - /* process the paging request (LAI/RAI lookup) */ - rc = gbprox_rx_paging(cfg, msg, &tp, nsei, ns_bvci); - break; - case BSSGP_PDUT_STATUS: - /* Some exception has occurred */ - LOGP(DGPRS, LOGL_NOTICE, - "NSEI=%u(SGSN) BSSGP STATUS ", nsei); - if (!TLVP_PRESENT(&tp, BSSGP_IE_CAUSE)) { - LOGPC(DGPRS, LOGL_NOTICE, "\n"); - goto err_mand_ie; - } - cause = *TLVP_VAL(&tp, BSSGP_IE_CAUSE); - LOGPC(DGPRS, LOGL_NOTICE, - "cause=0x%02x(%s) ", *TLVP_VAL(&tp, BSSGP_IE_CAUSE), - bssgp_cause_str(cause)); - if (TLVP_PRESENT(&tp, BSSGP_IE_BVCI)) { - bvci = ntohs(tlvp_val16_unal(&tp, BSSGP_IE_BVCI)); - LOGPC(DGPRS, LOGL_NOTICE, "BVCI=%u\n", bvci); - - if (cause == BSSGP_CAUSE_UNKNOWN_BVCI) - rc = gbprox_relay2bvci(cfg, msg, bvci, ns_bvci); - } else - LOGPC(DGPRS, LOGL_NOTICE, "\n"); - break; - /* those only exist in the SGSN -> BSS direction */ - case BSSGP_PDUT_SUSPEND_ACK: - case BSSGP_PDUT_SUSPEND_NACK: - case BSSGP_PDUT_RESUME_ACK: - case BSSGP_PDUT_RESUME_NACK: - /* RAI IE is mandatory */ - if (!TLVP_PRESENT(&tp, BSSGP_IE_ROUTEING_AREA)) - goto err_mand_ie; - peer = gbproxy_peer_by_rai(cfg, TLVP_VAL(&tp, BSSGP_IE_ROUTEING_AREA)); - if (!peer) - goto err_no_peer; - rc = gbprox_relay2peer(msg, peer, ns_bvci); - break; - case BSSGP_PDUT_BVC_BLOCK_ACK: - case BSSGP_PDUT_BVC_UNBLOCK_ACK: - if (!TLVP_PRESENT(&tp, BSSGP_IE_BVCI)) - goto err_mand_ie; - bvci = ntohs(tlvp_val16_unal(&tp, BSSGP_IE_BVCI)); - if (bvci == 0) { - LOGP(DGPRS, LOGL_NOTICE, "NSEI=%u(SGSN) BSSGP " - "%sBLOCK_ACK for signalling BVCI ?!?\n", nsei, - pdu_type == BSSGP_PDUT_BVC_UNBLOCK_ACK ? "UN":""); - /* should we send STATUS ? */ - rate_ctr_inc(&cfg->ctrg-> - ctr[GBPROX_GLOB_CTR_INV_BVCI]); - } else { - /* Mark BVC as (un)blocked */ - block_unblock_peer(cfg, bvci, pdu_type); - } - rc = gbprox_relay2bvci(cfg, msg, bvci, ns_bvci); - break; - case BSSGP_PDUT_SGSN_INVOKE_TRACE: - LOGP(DGPRS, LOGL_ERROR, - "NSEI=%u(SGSN) BSSGP INVOKE TRACE not supported\n",nsei); - rate_ctr_inc(&cfg->ctrg-> - ctr[GBPROX_GLOB_CTR_NOT_SUPPORTED_SGSN]); - rc = bssgp_tx_status(BSSGP_CAUSE_PDU_INCOMP_FEAT, NULL, orig_msg); - break; - default: - LOGP(DGPRS, LOGL_NOTICE, "BSSGP PDU type 0x%02x unknown\n", - pdu_type); - rate_ctr_inc(&cfg->ctrg-> - ctr[GBPROX_GLOB_CTR_PROTO_ERR_SGSN]); - rc = bssgp_tx_status(BSSGP_CAUSE_PROTO_ERR_UNSPEC, NULL, orig_msg); - break; - } - - msgb_free(msg); - - return rc; -err_mand_ie: - LOGP(DGPRS, LOGL_ERROR, "NSEI=%u(SGSN) missing mandatory IE\n", - nsei); - rate_ctr_inc(&cfg->ctrg-> - ctr[GBPROX_GLOB_CTR_PROTO_ERR_SGSN]); - msgb_free(msg); - return bssgp_tx_status(BSSGP_CAUSE_MISSING_MAND_IE, NULL, orig_msg); -err_no_peer: - LOGP(DGPRS, LOGL_ERROR, "NSEI=%u(SGSN) cannot find peer based on RAI\n", - nsei); - rate_ctr_inc(&cfg->ctrg-> ctr[GBPROX_GLOB_CTR_INV_RAI]); - msgb_free(msg); - return bssgp_tx_status(BSSGP_CAUSE_INV_MAND_INF, NULL, orig_msg); -} - -static int gbproxy_is_sgsn_nsei(struct gbproxy_config *cfg, uint16_t nsei) -{ - return nsei == cfg->nsip_sgsn_nsei || - (cfg->route_to_sgsn2 && nsei == cfg->nsip_sgsn2_nsei); -} - -/* Main input function for Gb proxy */ -int gbprox_rcvmsg(struct gbproxy_config *cfg, struct msgb *msg, uint16_t nsei, - uint16_t ns_bvci, uint16_t nsvci) -{ - int rc; - int remote_end_is_sgsn = gbproxy_is_sgsn_nsei(cfg, nsei); - - /* Only BVCI=0 messages need special treatment */ - if (ns_bvci == 0 || ns_bvci == 1) { - if (remote_end_is_sgsn) - rc = gbprox_rx_sig_from_sgsn(cfg, msg, nsei, ns_bvci); - else - rc = gbprox_rx_sig_from_bss(cfg, msg, nsei, ns_bvci); - } else { - /* All other BVCI are PTP */ - if (remote_end_is_sgsn) - rc = gbprox_rx_ptp_from_sgsn(cfg, msg, nsei, nsvci, - ns_bvci); - else - rc = gbprox_rx_ptp_from_bss(cfg, msg, nsei, nsvci, - ns_bvci); - } - - return rc; -} - -int gbprox_reset_persistent_nsvcs(struct gprs_ns_inst *nsi) -{ - struct gprs_nsvc *nsvc; - - llist_for_each_entry(nsvc, &nsi->gprs_nsvcs, list) { - if (!nsvc->persistent) - continue; - gprs_nsvc_reset(nsvc, NS_CAUSE_OM_INTERVENTION); - } - return 0; -} - -/* Signal handler for signals from NS layer */ -int gbprox_signal(unsigned int subsys, unsigned int signal, - void *handler_data, void *signal_data) -{ - struct gbproxy_config *cfg = handler_data; - struct ns_signal_data *nssd = signal_data; - struct gprs_nsvc *nsvc = nssd->nsvc; - struct gbproxy_peer *peer; - int remote_end_is_sgsn = gbproxy_is_sgsn_nsei(cfg, nsvc->nsei); - - if (subsys != SS_L_NS) - return 0; - - if (signal == S_NS_RESET && remote_end_is_sgsn) { - /* We have received a NS-RESET from the NSEI and NSVC - * of the SGSN. This might happen with SGSN that start - * their own NS-RESET procedure without waiting for our - * NS-RESET */ - nsvc->remote_end_is_sgsn = 1; - } - - if (signal == S_NS_ALIVE_EXP && nsvc->remote_end_is_sgsn) { - LOGP(DGPRS, LOGL_NOTICE, "Tns alive expired too often, " - "re-starting RESET procedure\n"); - rate_ctr_inc(&cfg->ctrg-> - ctr[GBPROX_GLOB_CTR_RESTART_RESET_SGSN]); - gprs_ns_nsip_connect(nsvc->nsi, &nsvc->ip.bts_addr, - nsvc->nsei, nsvc->nsvci); - } - - if (!nsvc->remote_end_is_sgsn) { - /* from BSS to SGSN */ - peer = gbproxy_peer_by_nsei(cfg, nsvc->nsei); - if (!peer) { - LOGP(DGPRS, LOGL_NOTICE, "signal %u for unknown peer " - "NSEI=%u/NSVCI=%u\n", signal, nsvc->nsei, - nsvc->nsvci); - return 0; - } - switch (signal) { - case S_NS_RESET: - case S_NS_BLOCK: - if (!peer->blocked) - break; - LOGP(DGPRS, LOGL_NOTICE, "Converting NS_RESET from " - "NSEI=%u/NSVCI=%u into BSSGP_BVC_BLOCK to SGSN\n", - nsvc->nsei, nsvc->nsvci); - bssgp_tx_simple_bvci(BSSGP_PDUT_BVC_BLOCK, nsvc->nsei, - peer->bvci, 0); - break; - } - } else { - /* Forward this message to all NS-VC to BSS */ - struct gprs_ns_inst *nsi = cfg->nsi; - struct gprs_nsvc *next_nsvc; - - llist_for_each_entry(next_nsvc, &nsi->gprs_nsvcs, list) { - if (next_nsvc->remote_end_is_sgsn) - continue; - - /* Note that the following does not start the full - * procedures including timer based retransmissions. */ - switch (signal) { - case S_NS_RESET: - gprs_ns_tx_reset(next_nsvc, nssd->cause); - break; - case S_NS_BLOCK: - gprs_ns_tx_block(next_nsvc, nssd->cause); - break; - case S_NS_UNBLOCK: - gprs_ns_tx_unblock(next_nsvc); - break; - } - } - } - return 0; -} - -void gbprox_reset(struct gbproxy_config *cfg) -{ - struct gbproxy_peer *peer, *tmp; - - llist_for_each_entry_safe(peer, tmp, &cfg->bts_peers, list) - gbproxy_peer_free(peer); - - rate_ctr_group_free(cfg->ctrg); - gbproxy_init_config(cfg); -} - -int gbproxy_init_config(struct gbproxy_config *cfg) -{ - struct timespec tp; - - INIT_LLIST_HEAD(&cfg->bts_peers); - cfg->ctrg = rate_ctr_group_alloc(tall_bsc_ctx, &global_ctrg_desc, 0); - clock_gettime(CLOCK_REALTIME, &tp); - - return 0; -} diff --git a/openbsc/src/gprs/gb_proxy_main.c b/openbsc/src/gprs/gb_proxy_main.c deleted file mode 100644 index 69a93b6f..00000000 --- a/openbsc/src/gprs/gb_proxy_main.c +++ /dev/null @@ -1,315 +0,0 @@ -/* NS-over-IP proxy */ - -/* (C) 2010 by Harald Welte - * (C) 2010 by On-Waves - * All Rights Reserved - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation; either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - * - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include - -#include -#include - -#include -#include -#include -#include - -#include -#include -#include -#include -#include - -#include "../../bscconfig.h" - -#define _GNU_SOURCE -#include - -void *tall_bsc_ctx; - -const char *openbsc_copyright = - "Copyright (C) 2010 Harald Welte and On-Waves\r\n" - "License AGPLv3+: GNU AGPL version 3 or later \r\n" - "This is free software: you are free to change and redistribute it.\r\n" - "There is NO WARRANTY, to the extent permitted by law.\r\n"; - -static char *config_file = "osmo_gbproxy.cfg"; -struct gbproxy_config gbcfg = {0}; -static int daemonize = 0; - -/* Pointer to the SGSN peer */ -extern struct gbprox_peer *gbprox_peer_sgsn; - -/* call-back function for the NS protocol */ -static int proxy_ns_cb(enum gprs_ns_evt event, struct gprs_nsvc *nsvc, - struct msgb *msg, uint16_t bvci) -{ - int rc = 0; - - switch (event) { - case GPRS_NS_EVT_UNIT_DATA: - rc = gbprox_rcvmsg(&gbcfg, msg, nsvc->nsei, bvci, nsvc->nsvci); - break; - default: - LOGP(DGPRS, LOGL_ERROR, "SGSN: Unknown event %u from NS\n", event); - if (msg) - msgb_free(msg); - rc = -EIO; - break; - } - return rc; -} - -static void signal_handler(int signal) -{ - fprintf(stdout, "signal %u received\n", signal); - - switch (signal) { - case SIGINT: - osmo_signal_dispatch(SS_L_GLOBAL, S_L_GLOBAL_SHUTDOWN, NULL); - sleep(1); - exit(0); - break; - case SIGABRT: - /* in case of abort, we want to obtain a talloc report - * and then return to the caller, who will abort the process */ - case SIGUSR1: - talloc_report(tall_vty_ctx, stderr); - talloc_report_full(tall_bsc_ctx, stderr); - break; - case SIGUSR2: - talloc_report_full(tall_vty_ctx, stderr); - break; - default: - break; - } -} - -static void print_usage() -{ - printf("Usage: bsc_hack\n"); -} - -static void print_help() -{ - printf(" Some useful help...\n"); - printf(" -h --help this text\n"); - printf(" -d option --debug=DNS:DGPRS,0:0 enable debugging\n"); - printf(" -D --daemonize Fork the process into a background daemon\n"); - printf(" -c --config-file filename The config file to use.\n"); - printf(" -s --disable-color\n"); - printf(" -T --timestamp Prefix every log line with a timestamp\n"); - printf(" -V --version. Print the version of OpenBSC.\n"); - printf(" -e --log-level number. Set a global loglevel.\n"); -} - -static void handle_options(int argc, char **argv) -{ - while (1) { - int option_index = 0, c; - static struct option long_options[] = { - { "help", 0, 0, 'h' }, - { "debug", 1, 0, 'd' }, - { "daemonize", 0, 0, 'D' }, - { "config-file", 1, 0, 'c' }, - { "disable-color", 0, 0, 's' }, - { "timestamp", 0, 0, 'T' }, - { "version", 0, 0, 'V' }, - { "log-level", 1, 0, 'e' }, - { 0, 0, 0, 0 } - }; - - c = getopt_long(argc, argv, "hd:Dc:sTVe:", - long_options, &option_index); - if (c == -1) - break; - - switch (c) { - case 'h': - print_usage(); - print_help(); - exit(0); - case 's': - log_set_use_color(osmo_stderr_target, 0); - break; - case 'd': - log_parse_category_mask(osmo_stderr_target, optarg); - break; - case 'D': - daemonize = 1; - break; - case 'c': - config_file = optarg; - break; - case 'T': - log_set_print_timestamp(osmo_stderr_target, 1); - break; - case 'e': - log_set_log_level(osmo_stderr_target, atoi(optarg)); - break; - case 'V': - print_version(1); - exit(0); - break; - default: - break; - } - } -} - -extern int bsc_vty_go_parent(struct vty *vty); - -static struct vty_app_info vty_info = { - .name = "OsmoGbProxy", - .version = PACKAGE_VERSION, - .go_parent_cb = bsc_vty_go_parent, - .is_config_node = bsc_vty_is_config_node, -}; - -/* default categories */ -static struct log_info_cat gprs_categories[] = { - [DGPRS] = { - .name = "DGPRS", - .description = "GPRS Packet Service", - .enabled = 1, .loglevel = LOGL_DEBUG, - }, - [DNS] = { - .name = "DNS", - .description = "GPRS Network Service (NS)", - .enabled = 1, .loglevel = LOGL_INFO, - }, - [DBSSGP] = { - .name = "DBSSGP", - .description = "GPRS BSS Gateway Protocol (BSSGP)", - .enabled = 1, .loglevel = LOGL_DEBUG, - }, -}; - -static const struct log_info gprs_log_info = { - .filter_fn = gprs_log_filter_fn, - .cat = gprs_categories, - .num_cat = ARRAY_SIZE(gprs_categories), -}; - -int main(int argc, char **argv) -{ - struct gsm_network dummy_network; - int rc; - - tall_bsc_ctx = talloc_named_const(NULL, 0, "nsip_proxy"); - msgb_talloc_ctx_init(tall_bsc_ctx, 0); - - signal(SIGINT, &signal_handler); - signal(SIGABRT, &signal_handler); - signal(SIGUSR1, &signal_handler); - signal(SIGUSR2, &signal_handler); - osmo_init_ignore_signals(); - - osmo_init_logging(&gprs_log_info); - - vty_info.copyright = openbsc_copyright; - vty_init(&vty_info); - logging_vty_add_cmds(NULL); - osmo_stats_vty_add_cmds(&gprs_log_info); - gbproxy_vty_init(); - - handle_options(argc, argv); - - rate_ctr_init(tall_bsc_ctx); - osmo_stats_init(tall_bsc_ctx); - - bssgp_nsi = gprs_ns_instantiate(&proxy_ns_cb, tall_bsc_ctx); - if (!bssgp_nsi) { - LOGP(DGPRS, LOGL_ERROR, "Unable to instantiate NS\n"); - exit(1); - } - gbproxy_init_config(&gbcfg); - gbcfg.nsi = bssgp_nsi; - gprs_ns_vty_init(bssgp_nsi); - gprs_ns_set_log_ss(DNS); - bssgp_set_log_ss(DBSSGP); - osmo_signal_register_handler(SS_L_NS, &gbprox_signal, &gbcfg); - - rc = gbproxy_parse_config(config_file, &gbcfg); - if (rc < 0) { - LOGP(DGPRS, LOGL_FATAL, "Cannot parse config file\n"); - exit(2); - } - - /* start telnet after reading config for vty_get_bind_addr() */ - rc = telnet_init_dynif(tall_bsc_ctx, &dummy_network, - vty_get_bind_addr(), OSMO_VTY_PORT_GBPROXY); - if (rc < 0) - exit(1); - - if (!gprs_nsvc_by_nsei(gbcfg.nsi, gbcfg.nsip_sgsn_nsei)) { - LOGP(DGPRS, LOGL_FATAL, "You cannot proxy to NSEI %u " - "without creating that NSEI before\n", - gbcfg.nsip_sgsn_nsei); - exit(2); - } - - rc = gprs_ns_nsip_listen(bssgp_nsi); - if (rc < 0) { - LOGP(DGPRS, LOGL_FATAL, "Cannot bind/listen on NSIP socket\n"); - exit(2); - } - - rc = gprs_ns_frgre_listen(bssgp_nsi); - if (rc < 0) { - LOGP(DGPRS, LOGL_FATAL, "Cannot bind/listen GRE " - "socket. Do you have CAP_NET_RAW?\n"); - exit(2); - } - - if (daemonize) { - rc = osmo_daemonize(); - if (rc < 0) { - perror("Error during daemonize"); - exit(1); - } - } - - /* Reset all the persistent NS-VCs that we've read from the config */ - gbprox_reset_persistent_nsvcs(bssgp_nsi); - - while (1) { - rc = osmo_select_main(0); - if (rc < 0) - exit(3); - } - - exit(0); -} diff --git a/openbsc/src/gprs/gb_proxy_patch.c b/openbsc/src/gprs/gb_proxy_patch.c deleted file mode 100644 index 7bddc449..00000000 --- a/openbsc/src/gprs/gb_proxy_patch.c +++ /dev/null @@ -1,458 +0,0 @@ -/* Gb-proxy message patching */ - -/* (C) 2014 by On-Waves - * All Rights Reserved - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation; either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - * - */ - -#include - -#include -#include - -#include -#include - -#include -#include - -/* patch RA identifier in place */ -static void gbproxy_patch_raid(uint8_t *raid_enc, struct gbproxy_peer *peer, - int to_bss, const char *log_text) -{ - struct gbproxy_patch_state *state = &peer->patch_state; - int old_mcc; - int old_mnc; - struct gprs_ra_id raid; - enum gbproxy_peer_ctr counter = - to_bss ? - GBPROX_PEER_CTR_RAID_PATCHED_SGSN : - GBPROX_PEER_CTR_RAID_PATCHED_BSS; - - if (!state->local_mcc || !state->local_mnc) - return; - - gsm48_parse_ra(&raid, raid_enc); - - old_mcc = raid.mcc; - old_mnc = raid.mnc; - - if (!to_bss) { - /* BSS -> SGSN */ - if (state->local_mcc) - raid.mcc = peer->cfg->core_mcc; - - if (state->local_mnc) - raid.mnc = peer->cfg->core_mnc; - } else { - /* SGSN -> BSS */ - if (state->local_mcc) - raid.mcc = state->local_mcc; - - if (state->local_mnc) - raid.mnc = state->local_mnc; - } - - LOGP(DGPRS, LOGL_DEBUG, - "Patching %s to %s: " - "%d-%d-%d-%d -> %d-%d-%d-%d\n", - log_text, - to_bss ? "BSS" : "SGSN", - old_mcc, old_mnc, raid.lac, raid.rac, - raid.mcc, raid.mnc, raid.lac, raid.rac); - - gsm48_construct_ra(raid_enc, &raid); - rate_ctr_inc(&peer->ctrg->ctr[counter]); -} - -static void gbproxy_patch_apn_ie(struct msgb *msg, - uint8_t *apn_ie, size_t apn_ie_len, - struct gbproxy_peer *peer, - size_t *new_apn_ie_len, const char *log_text) -{ - struct apn_ie_hdr { - uint8_t iei; - uint8_t apn_len; - uint8_t apn[0]; - } *hdr = (void *)apn_ie; - - size_t apn_len = hdr->apn_len; - uint8_t *apn = hdr->apn; - - OSMO_ASSERT(apn_ie_len == apn_len + sizeof(struct apn_ie_hdr)); - OSMO_ASSERT(apn_ie_len > 2 && apn_ie_len <= 102); - - if (peer->cfg->core_apn_size == 0) { - char str1[110]; - /* Remove the IE */ - LOGP(DGPRS, LOGL_DEBUG, - "Patching %s to SGSN: Removing APN '%s'\n", - log_text, - gprs_apn_to_str(str1, apn, apn_len)); - - *new_apn_ie_len = 0; - gprs_msgb_resize_area(msg, apn_ie, apn_ie_len, 0); - } else { - /* Resize the IE */ - char str1[110]; - char str2[110]; - - OSMO_ASSERT(peer->cfg->core_apn_size <= 100); - - LOGP(DGPRS, LOGL_DEBUG, - "Patching %s to SGSN: " - "Replacing APN '%s' -> '%s'\n", - log_text, - gprs_apn_to_str(str1, apn, apn_len), - gprs_apn_to_str(str2, peer->cfg->core_apn, - peer->cfg->core_apn_size)); - - *new_apn_ie_len = peer->cfg->core_apn_size + 2; - gprs_msgb_resize_area(msg, apn, apn_len, peer->cfg->core_apn_size); - memcpy(apn, peer->cfg->core_apn, peer->cfg->core_apn_size); - hdr->apn_len = peer->cfg->core_apn_size; - } - - rate_ctr_inc(&peer->ctrg->ctr[GBPROX_PEER_CTR_APN_PATCHED]); -} - -static int gbproxy_patch_tlli(uint8_t *tlli_enc, - struct gbproxy_peer *peer, - uint32_t new_tlli, - int to_bss, const char *log_text) -{ - uint32_t tlli_be; - uint32_t tlli; - enum gbproxy_peer_ctr counter = - to_bss ? - GBPROX_PEER_CTR_TLLI_PATCHED_SGSN : - GBPROX_PEER_CTR_TLLI_PATCHED_BSS; - - memcpy(&tlli_be, tlli_enc, sizeof(tlli_be)); - tlli = ntohl(tlli_be); - - if (tlli == new_tlli) - return 0; - - LOGP(DGPRS, LOGL_DEBUG, - "Patching %ss: " - "Replacing %08x -> %08x\n", - log_text, tlli, new_tlli); - - tlli_be = htonl(new_tlli); - memcpy(tlli_enc, &tlli_be, sizeof(tlli_be)); - - rate_ctr_inc(&peer->ctrg->ctr[counter]); - - return 1; -} - -static int gbproxy_patch_ptmsi(uint8_t *ptmsi_enc, - struct gbproxy_peer *peer, - uint32_t new_ptmsi, - int to_bss, const char *log_text) -{ - uint32_t ptmsi_be; - uint32_t ptmsi; - enum gbproxy_peer_ctr counter = - to_bss ? - GBPROX_PEER_CTR_PTMSI_PATCHED_SGSN : - GBPROX_PEER_CTR_PTMSI_PATCHED_BSS; - memcpy(&ptmsi_be, ptmsi_enc, sizeof(ptmsi_be)); - ptmsi = ntohl(ptmsi_be); - - if (ptmsi == new_ptmsi) - return 0; - - LOGP(DGPRS, LOGL_DEBUG, - "Patching %ss: " - "Replacing %08x -> %08x\n", - log_text, ptmsi, new_ptmsi); - - ptmsi_be = htonl(new_ptmsi); - memcpy(ptmsi_enc, &ptmsi_be, sizeof(ptmsi_be)); - - rate_ctr_inc(&peer->ctrg->ctr[counter]); - - return 1; -} - -int gbproxy_patch_llc(struct msgb *msg, uint8_t *llc, size_t llc_len, - struct gbproxy_peer *peer, - struct gbproxy_link_info *link_info, int *len_change, - struct gprs_gb_parse_context *parse_ctx) -{ - struct gprs_llc_hdr_parsed *ghp = &parse_ctx->llc_hdr_parsed; - int have_patched = 0; - int fcs; - struct gbproxy_config *cfg = peer->cfg; - - if (parse_ctx->ptmsi_enc && link_info && - !parse_ctx->old_raid_is_foreign && peer->cfg->patch_ptmsi) { - uint32_t ptmsi; - if (parse_ctx->to_bss) - ptmsi = link_info->tlli.ptmsi; - else - ptmsi = link_info->sgsn_tlli.ptmsi; - - if (ptmsi != GSM_RESERVED_TMSI) { - if (gbproxy_patch_ptmsi(parse_ctx->ptmsi_enc, peer, - ptmsi, parse_ctx->to_bss, "P-TMSI")) - have_patched = 1; - } else { - /* TODO: invalidate old RAI if present (see below) */ - } - } - - if (parse_ctx->new_ptmsi_enc && link_info && cfg->patch_ptmsi) { - uint32_t ptmsi; - if (parse_ctx->to_bss) - ptmsi = link_info->tlli.ptmsi; - else - ptmsi = link_info->sgsn_tlli.ptmsi; - - OSMO_ASSERT(ptmsi); - if (gbproxy_patch_ptmsi(parse_ctx->new_ptmsi_enc, peer, - ptmsi, parse_ctx->to_bss, "new P-TMSI")) - have_patched = 1; - } - - if (parse_ctx->raid_enc) { - gbproxy_patch_raid(parse_ctx->raid_enc, peer, parse_ctx->to_bss, - parse_ctx->llc_msg_name); - have_patched = 1; - } - - if (parse_ctx->old_raid_enc && !parse_ctx->old_raid_is_foreign) { - /* TODO: Patch to invalid if P-TMSI unknown. */ - gbproxy_patch_raid(parse_ctx->old_raid_enc, peer, parse_ctx->to_bss, - parse_ctx->llc_msg_name); - have_patched = 1; - } - - if (parse_ctx->apn_ie && - cfg->core_apn && - !parse_ctx->to_bss && - gbproxy_imsi_matches(cfg, GBPROX_MATCH_PATCHING, link_info) && - cfg->core_apn) { - size_t new_len; - gbproxy_patch_apn_ie(msg, - parse_ctx->apn_ie, parse_ctx->apn_ie_len, - peer, &new_len, parse_ctx->llc_msg_name); - *len_change += (int)new_len - (int)parse_ctx->apn_ie_len; - - have_patched = 1; - } - - if (have_patched) { - llc_len += *len_change; - ghp->crc_length += *len_change; - - /* Fix FCS */ - fcs = gprs_llc_fcs(llc, ghp->crc_length); - LOGP(DLLC, LOGL_DEBUG, "Updated LLC message, CRC: %06x -> %06x\n", - ghp->fcs, fcs); - - llc[llc_len - 3] = fcs & 0xff; - llc[llc_len - 2] = (fcs >> 8) & 0xff; - llc[llc_len - 1] = (fcs >> 16) & 0xff; - } - - return have_patched; -} - -/* patch BSSGP message to use core_mcc/mnc on the SGSN side */ -void gbproxy_patch_bssgp(struct msgb *msg, uint8_t *bssgp, size_t bssgp_len, - struct gbproxy_peer *peer, - struct gbproxy_link_info *link_info, int *len_change, - struct gprs_gb_parse_context *parse_ctx) -{ - const char *err_info = NULL; - int err_ctr = -1; - - if (parse_ctx->bssgp_raid_enc) - gbproxy_patch_raid(parse_ctx->bssgp_raid_enc, peer, - parse_ctx->to_bss, "BSSGP"); - - if (parse_ctx->need_decryption && - (peer->cfg->patch_ptmsi || peer->cfg->core_apn)) { - /* Patching LLC messages has been requested - * explicitly, but the message (including the - * type) is encrypted, so we possibly fail to - * patch the LLC part of the message. */ - err_ctr = GBPROX_PEER_CTR_PATCH_CRYPT_ERR; - err_info = "GMM message is encrypted"; - goto patch_error; - } - - if (!link_info && parse_ctx->tlli_enc && parse_ctx->to_bss) { - /* Happens with unknown (not cached) TLLI coming from - * the SGSN */ - /* TODO: What shall be done with the message in this case? */ - err_ctr = GBPROX_PEER_CTR_TLLI_UNKNOWN; - err_info = "TLLI sent by the SGSN is unknown"; - goto patch_error; - } - - if (!link_info) - return; - - if (parse_ctx->tlli_enc && peer->cfg->patch_ptmsi) { - uint32_t tlli = gbproxy_map_tlli(parse_ctx->tlli, - link_info, parse_ctx->to_bss); - - if (tlli) { - gbproxy_patch_tlli(parse_ctx->tlli_enc, peer, tlli, - parse_ctx->to_bss, "TLLI"); - parse_ctx->tlli = tlli; - } else { - /* Internal error */ - err_ctr = GBPROX_PEER_CTR_PATCH_ERR; - err_info = "Replacement TLLI is 0"; - goto patch_error; - } - } - - if (parse_ctx->bssgp_ptmsi_enc && peer->cfg->patch_ptmsi) { - uint32_t ptmsi; - if (parse_ctx->to_bss) - ptmsi = link_info->tlli.ptmsi; - else - ptmsi = link_info->sgsn_tlli.ptmsi; - - if (ptmsi != GSM_RESERVED_TMSI) - gbproxy_patch_ptmsi( - parse_ctx->bssgp_ptmsi_enc, peer, - ptmsi, parse_ctx->to_bss, "BSSGP P-TMSI"); - } - - if (parse_ctx->llc) { - uint8_t *llc = parse_ctx->llc; - size_t llc_len = parse_ctx->llc_len; - int llc_len_change = 0; - - gbproxy_patch_llc(msg, llc, llc_len, peer, link_info, - &llc_len_change, parse_ctx); - /* Note that the APN might have been resized here, but no - * pointer int the parse_ctx will refer to an adress after the - * APN. So it's possible to patch first and do the TLLI - * handling afterwards. */ - - if (llc_len_change) { - llc_len += llc_len_change; - - /* Fix LLC IE len */ - /* TODO: This is a kludge, but the a pointer to the - * start of the IE is not available here */ - if (llc[-2] == BSSGP_IE_LLC_PDU && llc[-1] & 0x80) { - /* most probably a one byte length */ - if (llc_len > 127) { - err_info = "Cannot increase size"; - err_ctr = GBPROX_PEER_CTR_PATCH_ERR; - goto patch_error; - } - llc[-1] = llc_len | 0x80; - } else { - llc[-2] = (llc_len >> 8) & 0x7f; - llc[-1] = llc_len & 0xff; - } - *len_change += llc_len_change; - } - /* Note that the tp struct might contain invalid pointers here - * if the LLC field has changed its size */ - parse_ctx->llc_len = llc_len; - } - return; - -patch_error: - OSMO_ASSERT(err_ctr >= 0); - rate_ctr_inc(&peer->ctrg->ctr[err_ctr]); - LOGP(DGPRS, LOGL_ERROR, - "NSEI=%u(%s) failed to patch BSSGP message as requested: %s.\n", - msgb_nsei(msg), parse_ctx->to_bss ? "SGSN" : "BSS", - err_info); -} - -void gbproxy_clear_patch_filter(struct gbproxy_match *match) -{ - if (match->enable) { - regfree(&match->re_comp); - match->enable = 0; - } - talloc_free(match->re_str); - match->re_str = NULL; -} - -int gbproxy_set_patch_filter(struct gbproxy_match *match, const char *filter, - const char **err_msg) -{ - static char err_buf[300]; - int rc; - - gbproxy_clear_patch_filter(match); - - if (!filter) - return 0; - - rc = regcomp(&match->re_comp, filter, - REG_EXTENDED | REG_NOSUB | REG_ICASE); - - if (rc == 0) { - match->enable = 1; - match->re_str = talloc_strdup(tall_bsc_ctx, filter); - return 0; - } - - if (err_msg) { - regerror(rc, &match->re_comp, - err_buf, sizeof(err_buf)); - *err_msg = err_buf; - } - - return -1; -} - -int gbproxy_check_imsi(struct gbproxy_match *match, - const uint8_t *imsi, size_t imsi_len) -{ - char mi_buf[200]; - int rc; - - if (!match->enable) - return 1; - - rc = gprs_is_mi_imsi(imsi, imsi_len); - if (rc > 0) - rc = gsm48_mi_to_string(mi_buf, sizeof(mi_buf), imsi, imsi_len); - if (rc <= 0) { - LOGP(DGPRS, LOGL_NOTICE, "Invalid IMSI %s\n", - osmo_hexdump(imsi, imsi_len)); - return -1; - } - - LOGP(DGPRS, LOGL_DEBUG, "Checking IMSI '%s' (%d)\n", mi_buf, rc); - - rc = regexec(&match->re_comp, mi_buf, 0, NULL, 0); - if (rc == REG_NOMATCH) { - LOGP(DGPRS, LOGL_INFO, - "IMSI '%s' doesn't match pattern '%s'\n", - mi_buf, match->re_str); - return 0; - } - - return 1; -} - diff --git a/openbsc/src/gprs/gb_proxy_peer.c b/openbsc/src/gprs/gb_proxy_peer.c deleted file mode 100644 index 5365ff0f..00000000 --- a/openbsc/src/gprs/gb_proxy_peer.c +++ /dev/null @@ -1,218 +0,0 @@ -/* Gb proxy peer handling */ - -/* (C) 2010 by Harald Welte - * (C) 2010-2013 by On-Waves - * (C) 2013 by Holger Hans Peter Freyther - * All Rights Reserved - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation; either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - * - */ - -#include - -#include -#include -#include - -#include -#include -#include -#include - -#include - -static const struct rate_ctr_desc peer_ctr_description[] = { - { "blocked", "BVC Block " }, - { "unblocked", "BVC Unblock " }, - { "dropped", "BVC blocked, dropped packet " }, - { "inv-nsei", "NSEI mismatch " }, - { "tx-err", "NS Transmission error " }, - { "raid-mod.bss", "RAID patched (BSS )" }, - { "raid-mod.sgsn", "RAID patched (SGSN)" }, - { "apn-mod.sgsn", "APN patched " }, - { "tlli-mod.bss", "TLLI patched (BSS )" }, - { "tlli-mod.sgsn", "TLLI patched (SGSN)" }, - { "ptmsi-mod.bss", "P-TMSI patched (BSS )" }, - { "ptmsi-mod.sgsn","P-TMSI patched (SGSN)" }, - { "mod-crypt-err", "Patch error: encrypted " }, - { "mod-err", "Patch error: other " }, - { "attach-reqs", "Attach Request count " }, - { "attach-rejs", "Attach Reject count " }, - { "attach-acks", "Attach Accept count " }, - { "attach-cpls", "Attach Completed count " }, - { "ra-upd-reqs", "RoutingArea Update Request count" }, - { "ra-upd-rejs", "RoutingArea Update Reject count " }, - { "ra-upd-acks", "RoutingArea Update Accept count " }, - { "ra-upd-cpls", "RoutingArea Update Compltd count" }, - { "gmm-status", "GMM Status count (BSS)" }, - { "gmm-status", "GMM Status count (SGSN)" }, - { "detach-reqs", "Detach Request count " }, - { "detach-acks", "Detach Accept count " }, - { "pdp-act-reqs", "PDP Activation Request count " }, - { "pdp-act-rejs", "PDP Activation Reject count " }, - { "pdp-act-acks", "PDP Activation Accept count " }, - { "pdp-deact-reqs","PDP Deactivation Request count " }, - { "pdp-deact-acks","PDP Deactivation Accept count " }, - { "tlli-unknown", "TLLI from SGSN unknown " }, - { "tlli-cache", "TLLI cache size " }, -}; - -osmo_static_assert(ARRAY_SIZE(peer_ctr_description) == GBPROX_PEER_CTR_LAST, everything_described); - -static const struct rate_ctr_group_desc peer_ctrg_desc = { - .group_name_prefix = "gbproxy.peer", - .group_description = "GBProxy Peer Statistics", - .num_ctr = ARRAY_SIZE(peer_ctr_description), - .ctr_desc = peer_ctr_description, - .class_id = OSMO_STATS_CLASS_PEER, -}; - - -/* Find the gbprox_peer by its BVCI */ -struct gbproxy_peer *gbproxy_peer_by_bvci(struct gbproxy_config *cfg, uint16_t bvci) -{ - struct gbproxy_peer *peer; - llist_for_each_entry(peer, &cfg->bts_peers, list) { - if (peer->bvci == bvci) - return peer; - } - return NULL; -} - -/* Find the gbprox_peer by its NSEI */ -struct gbproxy_peer *gbproxy_peer_by_nsei(struct gbproxy_config *cfg, - uint16_t nsei) -{ - struct gbproxy_peer *peer; - llist_for_each_entry(peer, &cfg->bts_peers, list) { - if (peer->nsei == nsei) - return peer; - } - return NULL; -} - -/* look-up a peer by its Routeing Area Identification (RAI) */ -struct gbproxy_peer *gbproxy_peer_by_rai(struct gbproxy_config *cfg, - const uint8_t *ra) -{ - struct gbproxy_peer *peer; - llist_for_each_entry(peer, &cfg->bts_peers, list) { - if (!memcmp(peer->ra, ra, 6)) - return peer; - } - return NULL; -} - -/* look-up a peer by its Location Area Identification (LAI) */ -struct gbproxy_peer *gbproxy_peer_by_lai(struct gbproxy_config *cfg, - const uint8_t *la) -{ - struct gbproxy_peer *peer; - llist_for_each_entry(peer, &cfg->bts_peers, list) { - if (!memcmp(peer->ra, la, 5)) - return peer; - } - return NULL; -} - -/* look-up a peer by its Location Area Code (LAC) */ -struct gbproxy_peer *gbproxy_peer_by_lac(struct gbproxy_config *cfg, - const uint8_t *la) -{ - struct gbproxy_peer *peer; - llist_for_each_entry(peer, &cfg->bts_peers, list) { - if (!memcmp(peer->ra + 3, la + 3, 2)) - return peer; - } - return NULL; -} - -struct gbproxy_peer *gbproxy_peer_by_bssgp_tlv(struct gbproxy_config *cfg, - struct tlv_parsed *tp) -{ - if (TLVP_PRESENT(tp, BSSGP_IE_BVCI)) { - uint16_t bvci; - - bvci = ntohs(tlvp_val16_unal(tp, BSSGP_IE_BVCI)); - if (bvci >= 2) - return gbproxy_peer_by_bvci(cfg, bvci); - } - - if (TLVP_PRESENT(tp, BSSGP_IE_ROUTEING_AREA)) { - uint8_t *rai = (uint8_t *)TLVP_VAL(tp, BSSGP_IE_ROUTEING_AREA); - /* Only compare LAC part, since MCC/MNC are possibly patched. - * Since the LAC of different BSS must be different when - * MCC/MNC are patched, collisions shouldn't happen. */ - return gbproxy_peer_by_lac(cfg, rai); - } - - if (TLVP_PRESENT(tp, BSSGP_IE_LOCATION_AREA)) { - uint8_t *lai = (uint8_t *)TLVP_VAL(tp, BSSGP_IE_LOCATION_AREA); - return gbproxy_peer_by_lac(cfg, lai); - } - - return NULL; -} - - -struct gbproxy_peer *gbproxy_peer_alloc(struct gbproxy_config *cfg, uint16_t bvci) -{ - struct gbproxy_peer *peer; - - peer = talloc_zero(tall_bsc_ctx, struct gbproxy_peer); - if (!peer) - return NULL; - - peer->bvci = bvci; - peer->ctrg = rate_ctr_group_alloc(peer, &peer_ctrg_desc, bvci); - peer->cfg = cfg; - - llist_add(&peer->list, &cfg->bts_peers); - - INIT_LLIST_HEAD(&peer->patch_state.logical_links); - - return peer; -} - -void gbproxy_peer_free(struct gbproxy_peer *peer) -{ - llist_del(&peer->list); - - gbproxy_delete_link_infos(peer); - - rate_ctr_group_free(peer->ctrg); - peer->ctrg = NULL; - - talloc_free(peer); -} - -int gbproxy_cleanup_peers(struct gbproxy_config *cfg, uint16_t nsei, uint16_t bvci) -{ - int counter = 0; - struct gbproxy_peer *peer, *tmp; - - llist_for_each_entry_safe(peer, tmp, &cfg->bts_peers, list) { - if (peer->nsei != nsei) - continue; - if (bvci && peer->bvci != bvci) - continue; - - gbproxy_peer_free(peer); - counter += 1; - } - - return counter; -} - diff --git a/openbsc/src/gprs/gb_proxy_tlli.c b/openbsc/src/gprs/gb_proxy_tlli.c deleted file mode 100644 index 3b3b976a..00000000 --- a/openbsc/src/gprs/gb_proxy_tlli.c +++ /dev/null @@ -1,723 +0,0 @@ -/* Gb-proxy TLLI state handling */ - -/* (C) 2014 by On-Waves - * All Rights Reserved - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation; either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - * - */ - -#include - -#include - -#include -#include - -#include - -#include - -#include -#include - -struct gbproxy_link_info *gbproxy_link_info_by_tlli(struct gbproxy_peer *peer, - uint32_t tlli) -{ - struct gbproxy_link_info *link_info; - struct gbproxy_patch_state *state = &peer->patch_state; - - if (!tlli) - return NULL; - - llist_for_each_entry(link_info, &state->logical_links, list) - if (link_info->tlli.current == tlli || - link_info->tlli.assigned == tlli) - return link_info; - - return NULL; -} - -struct gbproxy_link_info *gbproxy_link_info_by_ptmsi( - struct gbproxy_peer *peer, - uint32_t ptmsi) -{ - struct gbproxy_link_info *link_info; - struct gbproxy_patch_state *state = &peer->patch_state; - - if (ptmsi == GSM_RESERVED_TMSI) - return NULL; - - llist_for_each_entry(link_info, &state->logical_links, list) - if (link_info->tlli.ptmsi == ptmsi) - return link_info; - - return NULL; -} - -struct gbproxy_link_info *gbproxy_link_info_by_any_sgsn_tlli( - struct gbproxy_peer *peer, - uint32_t tlli) -{ - struct gbproxy_link_info *link_info; - struct gbproxy_patch_state *state = &peer->patch_state; - - if (!tlli) - return NULL; - - /* Don't care about the NSEI */ - llist_for_each_entry(link_info, &state->logical_links, list) - if (link_info->sgsn_tlli.current == tlli || - link_info->sgsn_tlli.assigned == tlli) - return link_info; - - return NULL; -} - -struct gbproxy_link_info *gbproxy_link_info_by_sgsn_tlli( - struct gbproxy_peer *peer, - uint32_t tlli, uint32_t sgsn_nsei) -{ - struct gbproxy_link_info *link_info; - struct gbproxy_patch_state *state = &peer->patch_state; - - if (!tlli) - return NULL; - - llist_for_each_entry(link_info, &state->logical_links, list) - if ((link_info->sgsn_tlli.current == tlli || - link_info->sgsn_tlli.assigned == tlli) && - link_info->sgsn_nsei == sgsn_nsei) - return link_info; - - return NULL; -} - -struct gbproxy_link_info *gbproxy_link_info_by_imsi( - struct gbproxy_peer *peer, - const uint8_t *imsi, - size_t imsi_len) -{ - struct gbproxy_link_info *link_info; - struct gbproxy_patch_state *state = &peer->patch_state; - - if (!gprs_is_mi_imsi(imsi, imsi_len)) - return NULL; - - llist_for_each_entry(link_info, &state->logical_links, list) { - if (link_info->imsi_len != imsi_len) - continue; - if (memcmp(link_info->imsi, imsi, imsi_len) != 0) - continue; - - return link_info; - } - - return NULL; -} - -void gbproxy_link_info_discard_messages(struct gbproxy_link_info *link_info) -{ - struct msgb *msg, *nxt; - - llist_for_each_entry_safe(msg, nxt, &link_info->stored_msgs, list) { - llist_del(&msg->list); - msgb_free(msg); - } -} - -void gbproxy_delete_link_info(struct gbproxy_peer *peer, - struct gbproxy_link_info *link_info) -{ - struct gbproxy_patch_state *state = &peer->patch_state; - - gbproxy_link_info_discard_messages(link_info); - - llist_del(&link_info->list); - talloc_free(link_info); - state->logical_link_count -= 1; - - peer->ctrg->ctr[GBPROX_PEER_CTR_TLLI_CACHE_SIZE].current = - state->logical_link_count; -} - -void gbproxy_delete_link_infos(struct gbproxy_peer *peer) -{ - struct gbproxy_link_info *link_info, *nxt; - struct gbproxy_patch_state *state = &peer->patch_state; - - llist_for_each_entry_safe(link_info, nxt, &state->logical_links, list) - gbproxy_delete_link_info(peer, link_info); - - OSMO_ASSERT(state->logical_link_count == 0); - OSMO_ASSERT(llist_empty(&state->logical_links)); -} - -void gbproxy_attach_link_info(struct gbproxy_peer *peer, time_t now, - struct gbproxy_link_info *link_info) -{ - struct gbproxy_patch_state *state = &peer->patch_state; - - link_info->timestamp = now; - llist_add(&link_info->list, &state->logical_links); - state->logical_link_count += 1; - - peer->ctrg->ctr[GBPROX_PEER_CTR_TLLI_CACHE_SIZE].current = - state->logical_link_count; -} - -int gbproxy_remove_stale_link_infos(struct gbproxy_peer *peer, time_t now) -{ - struct gbproxy_patch_state *state = &peer->patch_state; - int exceeded_max_len = 0; - int deleted_count = 0; - int check_for_age; - - if (peer->cfg->tlli_max_len > 0) - exceeded_max_len = - state->logical_link_count - peer->cfg->tlli_max_len; - - check_for_age = peer->cfg->tlli_max_age > 0; - - for (; exceeded_max_len > 0; exceeded_max_len--) { - struct gbproxy_link_info *link_info; - OSMO_ASSERT(!llist_empty(&state->logical_links)); - link_info = llist_entry(state->logical_links.prev, - struct gbproxy_link_info, - list); - LOGP(DGPRS, LOGL_INFO, - "Removing TLLI %08x from list " - "(stale, length %d, max_len exceeded)\n", - link_info->tlli.current, state->logical_link_count); - - gbproxy_delete_link_info(peer, link_info); - deleted_count += 1; - } - - while (check_for_age && !llist_empty(&state->logical_links)) { - time_t age; - struct gbproxy_link_info *link_info; - link_info = llist_entry(state->logical_links.prev, - struct gbproxy_link_info, - list); - age = now - link_info->timestamp; - /* age < 0 only happens after system time jumps, discard entry */ - if (age <= peer->cfg->tlli_max_age && age >= 0) { - check_for_age = 0; - continue; - } - - LOGP(DGPRS, LOGL_INFO, - "Removing TLLI %08x from list " - "(stale, age %d, max_age exceeded)\n", - link_info->tlli.current, (int)age); - - gbproxy_delete_link_info(peer, link_info); - deleted_count += 1; - } - - return deleted_count; -} - -struct gbproxy_link_info *gbproxy_link_info_alloc( struct gbproxy_peer *peer) -{ - struct gbproxy_link_info *link_info; - - link_info = talloc_zero(peer, struct gbproxy_link_info); - link_info->tlli.ptmsi = GSM_RESERVED_TMSI; - link_info->sgsn_tlli.ptmsi = GSM_RESERVED_TMSI; - - link_info->vu_gen_tx_bss = GBPROXY_INIT_VU_GEN_TX; - - INIT_LLIST_HEAD(&link_info->stored_msgs); - - return link_info; -} - -void gbproxy_detach_link_info( - struct gbproxy_peer *peer, - struct gbproxy_link_info *link_info) -{ - struct gbproxy_patch_state *state = &peer->patch_state; - - llist_del(&link_info->list); - OSMO_ASSERT(state->logical_link_count > 0); - state->logical_link_count -= 1; - - peer->ctrg->ctr[GBPROX_PEER_CTR_TLLI_CACHE_SIZE].current = - state->logical_link_count; -} - -void gbproxy_update_link_info(struct gbproxy_link_info *link_info, - const uint8_t *imsi, size_t imsi_len) -{ - if (!gprs_is_mi_imsi(imsi, imsi_len)) - return; - - link_info->imsi_len = imsi_len; - link_info->imsi = - talloc_realloc_size(link_info, link_info->imsi, imsi_len); - OSMO_ASSERT(link_info->imsi != NULL); - memcpy(link_info->imsi, imsi, imsi_len); -} - -void gbproxy_reassign_tlli(struct gbproxy_tlli_state *tlli_state, - struct gbproxy_peer *peer, uint32_t new_tlli) -{ - if (new_tlli == tlli_state->current) - return; - - LOGP(DGPRS, LOGL_INFO, - "The TLLI has been reassigned from %08x to %08x\n", - tlli_state->current, new_tlli); - - /* Remember assigned TLLI */ - tlli_state->assigned = new_tlli; - tlli_state->bss_validated = 0; - tlli_state->net_validated = 0; -} - -uint32_t gbproxy_map_tlli(uint32_t other_tlli, - struct gbproxy_link_info *link_info, int to_bss) -{ - uint32_t tlli = 0; - struct gbproxy_tlli_state *src, *dst; - if (to_bss) { - src = &link_info->sgsn_tlli; - dst = &link_info->tlli; - } else { - src = &link_info->tlli; - dst = &link_info->sgsn_tlli; - } - if (src->current == other_tlli) - tlli = dst->current; - else if (src->assigned == other_tlli) - tlli = dst->assigned; - - return tlli; -} - -static void gbproxy_validate_tlli(struct gbproxy_tlli_state *tlli_state, - uint32_t tlli, int to_bss) -{ - LOGP(DGPRS, LOGL_DEBUG, - "%s({current = %08x, assigned = %08x, net_vld = %d, bss_vld = %d}, %08x)\n", - __func__, tlli_state->current, tlli_state->assigned, - tlli_state->net_validated, tlli_state->bss_validated, tlli); - - if (!tlli_state->assigned || tlli_state->assigned != tlli) - return; - - /* TODO: Is this ok? Check spec */ - if (gprs_tlli_type(tlli) != TLLI_LOCAL) - return; - - /* See GSM 04.08, 4.7.1.5 */ - if (to_bss) - tlli_state->net_validated = 1; - else - tlli_state->bss_validated = 1; - - if (!tlli_state->bss_validated || !tlli_state->net_validated) - return; - - LOGP(DGPRS, LOGL_INFO, - "The TLLI %08x has been validated (was %08x)\n", - tlli_state->assigned, tlli_state->current); - - tlli_state->current = tlli; - tlli_state->assigned = 0; -} - -static void gbproxy_touch_link_info(struct gbproxy_peer *peer, - struct gbproxy_link_info *link_info, - time_t now) -{ - gbproxy_detach_link_info(peer, link_info); - gbproxy_attach_link_info(peer, now, link_info); -} - -static int gbproxy_unregister_link_info(struct gbproxy_peer *peer, - struct gbproxy_link_info *link_info) -{ - if (!link_info) - return 1; - - if (link_info->tlli.ptmsi == GSM_RESERVED_TMSI && !link_info->imsi_len) { - LOGP(DGPRS, LOGL_INFO, - "Removing TLLI %08x from list (P-TMSI or IMSI are not set)\n", - link_info->tlli.current); - gbproxy_delete_link_info(peer, link_info); - return 1; - } - - link_info->tlli.current = 0; - link_info->tlli.assigned = 0; - link_info->sgsn_tlli.current = 0; - link_info->sgsn_tlli.assigned = 0; - - link_info->is_deregistered = 1; - - gbproxy_reset_link(link_info); - - return 0; -} - -int gbproxy_imsi_matches(struct gbproxy_config *cfg, - enum gbproxy_match_id match_id, - struct gbproxy_link_info *link_info) -{ - struct gbproxy_match *match; - OSMO_ASSERT(match_id >= 0 && match_id < ARRAY_SIZE(cfg->matches)); - - match = &cfg->matches[match_id]; - if (!match->enable) - return 1; - - return link_info != NULL && link_info->is_matching[match_id]; -} - -void gbproxy_assign_imsi(struct gbproxy_peer *peer, - struct gbproxy_link_info *link_info, - struct gprs_gb_parse_context *parse_ctx) -{ - int imsi_matches; - struct gbproxy_link_info *other_link_info; - enum gbproxy_match_id match_id; - - /* Make sure that there is a second entry with the same IMSI */ - other_link_info = gbproxy_link_info_by_imsi( - peer, parse_ctx->imsi, parse_ctx->imsi_len); - - if (other_link_info && other_link_info != link_info) { - char mi_buf[200]; - mi_buf[0] = '\0'; - gsm48_mi_to_string(mi_buf, sizeof(mi_buf), - parse_ctx->imsi, parse_ctx->imsi_len); - LOGP(DGPRS, LOGL_INFO, - "Removing TLLI %08x from list (IMSI %s re-used)\n", - other_link_info->tlli.current, mi_buf); - gbproxy_delete_link_info(peer, other_link_info); - } - - /* Update the IMSI field */ - gbproxy_update_link_info(link_info, - parse_ctx->imsi, parse_ctx->imsi_len); - - /* Check, whether the IMSI matches */ - OSMO_ASSERT(ARRAY_SIZE(link_info->is_matching) == - ARRAY_SIZE(peer->cfg->matches)); - for (match_id = 0; match_id < ARRAY_SIZE(link_info->is_matching); - ++match_id) { - imsi_matches = gbproxy_check_imsi( - &peer->cfg->matches[match_id], - parse_ctx->imsi, parse_ctx->imsi_len); - if (imsi_matches >= 0) - link_info->is_matching[match_id] = imsi_matches; - } -} - -static int gbproxy_tlli_match(const struct gbproxy_tlli_state *a, - const struct gbproxy_tlli_state *b) -{ - if (a->current && a->current == b->current) - return 1; - - if (a->assigned && a->assigned == b->assigned) - return 1; - - if (a->ptmsi != GSM_RESERVED_TMSI && a->ptmsi == b->ptmsi) - return 1; - - return 0; -} - -static void gbproxy_remove_matching_link_infos( - struct gbproxy_peer *peer, struct gbproxy_link_info *link_info) -{ - struct gbproxy_link_info *info, *nxt; - struct gbproxy_patch_state *state = &peer->patch_state; - - /* Make sure that there is no second entry with the same P-TMSI or TLLI */ - llist_for_each_entry_safe(info, nxt, &state->logical_links, list) { - if (info == link_info) - continue; - - if (!gbproxy_tlli_match(&link_info->tlli, &info->tlli) && - (link_info->sgsn_nsei != info->sgsn_nsei || - !gbproxy_tlli_match(&link_info->sgsn_tlli, &info->sgsn_tlli))) - continue; - - LOGP(DGPRS, LOGL_INFO, - "Removing TLLI %08x from list (P-TMSI/TLLI re-used)\n", - info->tlli.current); - gbproxy_delete_link_info(peer, info); - } -} - -static struct gbproxy_link_info *gbproxy_get_link_info_ul( - struct gbproxy_peer *peer, - int *tlli_is_valid, - struct gprs_gb_parse_context *parse_ctx) -{ - struct gbproxy_link_info *link_info = NULL; - - if (parse_ctx->tlli_enc) { - link_info = gbproxy_link_info_by_tlli(peer, parse_ctx->tlli); - - if (link_info) { - *tlli_is_valid = 1; - return link_info; - } - } - - *tlli_is_valid = 0; - - if (!link_info && parse_ctx->imsi) { - link_info = gbproxy_link_info_by_imsi( - peer, parse_ctx->imsi, parse_ctx->imsi_len); - } - - if (!link_info && parse_ctx->ptmsi_enc && !parse_ctx->old_raid_is_foreign) { - uint32_t bss_ptmsi; - gprs_parse_tmsi(parse_ctx->ptmsi_enc, &bss_ptmsi); - link_info = gbproxy_link_info_by_ptmsi(peer, bss_ptmsi); - } - - if (!link_info) - return NULL; - - link_info->is_deregistered = 0; - - return link_info; -} - -struct gbproxy_link_info *gbproxy_update_link_state_ul( - struct gbproxy_peer *peer, - time_t now, - struct gprs_gb_parse_context *parse_ctx) -{ - struct gbproxy_link_info *link_info; - int tlli_is_valid; - - link_info = gbproxy_get_link_info_ul(peer, &tlli_is_valid, parse_ctx); - - if (parse_ctx->tlli_enc && parse_ctx->llc) { - uint32_t sgsn_tlli; - - if (!link_info) { - LOGP(DGPRS, LOGL_INFO, "Adding TLLI %08x to list\n", - parse_ctx->tlli); - link_info = gbproxy_link_info_alloc(peer); - gbproxy_attach_link_info(peer, now, link_info); - - /* Setup TLLIs */ - sgsn_tlli = gbproxy_make_sgsn_tlli(peer, link_info, - parse_ctx->tlli); - link_info->sgsn_tlli.current = sgsn_tlli; - link_info->tlli.current = parse_ctx->tlli; - } else if (!tlli_is_valid) { - /* New TLLI (info found by IMSI or P-TMSI) */ - link_info->tlli.current = parse_ctx->tlli; - link_info->tlli.assigned = 0; - link_info->sgsn_tlli.current = - gbproxy_make_sgsn_tlli(peer, link_info, - parse_ctx->tlli); - link_info->sgsn_tlli.assigned = 0; - gbproxy_touch_link_info(peer, link_info, now); - } else { - sgsn_tlli = gbproxy_map_tlli(parse_ctx->tlli, link_info, 0); - if (!sgsn_tlli) - sgsn_tlli = gbproxy_make_sgsn_tlli(peer, link_info, - parse_ctx->tlli); - - gbproxy_validate_tlli(&link_info->tlli, - parse_ctx->tlli, 0); - gbproxy_validate_tlli(&link_info->sgsn_tlli, - sgsn_tlli, 0); - gbproxy_touch_link_info(peer, link_info, now); - } - } else if (link_info) { - gbproxy_touch_link_info(peer, link_info, now); - } - - if (parse_ctx->imsi && link_info && link_info->imsi_len == 0) - gbproxy_assign_imsi(peer, link_info, parse_ctx); - - return link_info; -} - -static struct gbproxy_link_info *gbproxy_get_link_info_dl( - struct gbproxy_peer *peer, - struct gprs_gb_parse_context *parse_ctx) -{ - struct gbproxy_link_info *link_info = NULL; - - /* Which key to use depends on its availability only, if that fails, do - * not retry it with another key (e.g. IMSI). */ - if (parse_ctx->tlli_enc) - link_info = gbproxy_link_info_by_sgsn_tlli(peer, parse_ctx->tlli, - parse_ctx->peer_nsei); - - /* TODO: Get link_info by (SGSN) P-TMSI if that is available (see - * GSM 08.18, 7.2) instead of using the IMSI as key. */ - else if (parse_ctx->imsi) - link_info = gbproxy_link_info_by_imsi( - peer, parse_ctx->imsi, parse_ctx->imsi_len); - - if (link_info) - link_info->is_deregistered = 0; - - return link_info; -} - -struct gbproxy_link_info *gbproxy_update_link_state_dl( - struct gbproxy_peer *peer, - time_t now, - struct gprs_gb_parse_context *parse_ctx) -{ - struct gbproxy_link_info *link_info = NULL; - - link_info = gbproxy_get_link_info_dl(peer, parse_ctx); - - if (parse_ctx->tlli_enc && parse_ctx->new_ptmsi_enc && link_info) { - /* A new P-TMSI has been signalled in the message, - * register new TLLI */ - uint32_t new_sgsn_ptmsi; - uint32_t new_bss_ptmsi = GSM_RESERVED_TMSI; - gprs_parse_tmsi(parse_ctx->new_ptmsi_enc, &new_sgsn_ptmsi); - - if (link_info->sgsn_tlli.ptmsi == new_sgsn_ptmsi) - new_bss_ptmsi = link_info->tlli.ptmsi; - - if (new_bss_ptmsi == GSM_RESERVED_TMSI) - new_bss_ptmsi = gbproxy_make_bss_ptmsi(peer, new_sgsn_ptmsi); - - LOGP(DGPRS, LOGL_INFO, - "Got new PTMSI %08x from SGSN, using %08x for BSS\n", - new_sgsn_ptmsi, new_bss_ptmsi); - /* Setup PTMSIs */ - link_info->sgsn_tlli.ptmsi = new_sgsn_ptmsi; - link_info->tlli.ptmsi = new_bss_ptmsi; - } else if (parse_ctx->tlli_enc && parse_ctx->new_ptmsi_enc && !link_info && - !peer->cfg->patch_ptmsi) { - /* A new P-TMSI has been signalled in the message with an unknown - * TLLI, create a new link_info */ - /* TODO: Add a test case for this branch */ - uint32_t new_ptmsi; - gprs_parse_tmsi(parse_ctx->new_ptmsi_enc, &new_ptmsi); - - LOGP(DGPRS, LOGL_INFO, - "Adding TLLI %08x to list (SGSN, new P-TMSI is %08x)\n", - parse_ctx->tlli, new_ptmsi); - - link_info = gbproxy_link_info_alloc(peer); - link_info->sgsn_tlli.current = parse_ctx->tlli; - link_info->tlli.current = parse_ctx->tlli; - link_info->sgsn_tlli.ptmsi = new_ptmsi; - link_info->tlli.ptmsi = new_ptmsi; - gbproxy_attach_link_info(peer, now, link_info); - } else if (parse_ctx->tlli_enc && parse_ctx->llc && !link_info && - !peer->cfg->patch_ptmsi) { - /* Unknown SGSN TLLI, create a new link_info */ - uint32_t new_ptmsi; - link_info = gbproxy_link_info_alloc(peer); - LOGP(DGPRS, LOGL_INFO, "Adding TLLI %08x to list (SGSN)\n", - parse_ctx->tlli); - - gbproxy_attach_link_info(peer, now, link_info); - - /* Setup TLLIs */ - link_info->sgsn_tlli.current = parse_ctx->tlli; - link_info->tlli.current = parse_ctx->tlli; - - if (!parse_ctx->new_ptmsi_enc) - return link_info; - /* A new P-TMSI has been signalled in the message */ - - gprs_parse_tmsi(parse_ctx->new_ptmsi_enc, &new_ptmsi); - LOGP(DGPRS, LOGL_INFO, - "Assigning new P-TMSI %08x\n", new_ptmsi); - /* Setup P-TMSIs */ - link_info->sgsn_tlli.ptmsi = new_ptmsi; - link_info->tlli.ptmsi = new_ptmsi; - } else if (parse_ctx->tlli_enc && parse_ctx->llc && link_info) { - uint32_t bss_tlli = gbproxy_map_tlli(parse_ctx->tlli, - link_info, 1); - gbproxy_validate_tlli(&link_info->sgsn_tlli, parse_ctx->tlli, 1); - gbproxy_validate_tlli(&link_info->tlli, bss_tlli, 1); - gbproxy_touch_link_info(peer, link_info, now); - } else if (link_info) { - gbproxy_touch_link_info(peer, link_info, now); - } - - if (parse_ctx->imsi && link_info && link_info->imsi_len == 0) - gbproxy_assign_imsi(peer, link_info, parse_ctx); - - return link_info; -} - -int gbproxy_update_link_state_after( - struct gbproxy_peer *peer, - struct gbproxy_link_info *link_info, - time_t now, - struct gprs_gb_parse_context *parse_ctx) -{ - int rc = 0; - if (parse_ctx->invalidate_tlli && link_info) { - int keep_info = - peer->cfg->keep_link_infos == GBPROX_KEEP_ALWAYS || - (peer->cfg->keep_link_infos == GBPROX_KEEP_REATTACH && - parse_ctx->await_reattach) || - (peer->cfg->keep_link_infos == GBPROX_KEEP_IDENTIFIED && - link_info->imsi_len > 0); - if (keep_info) { - LOGP(DGPRS, LOGL_INFO, "Unregistering TLLI %08x\n", - link_info->tlli.current); - rc = gbproxy_unregister_link_info(peer, link_info); - } else { - LOGP(DGPRS, LOGL_INFO, "Removing TLLI %08x from list\n", - link_info->tlli.current); - gbproxy_delete_link_info(peer, link_info); - rc = 1; - } - } else if (parse_ctx->to_bss && parse_ctx->tlli_enc && - parse_ctx->new_ptmsi_enc && link_info) { - /* A new PTMSI has been signaled in the message, - * register new TLLI */ - uint32_t new_sgsn_ptmsi = link_info->sgsn_tlli.ptmsi; - uint32_t new_bss_ptmsi = link_info->tlli.ptmsi; - uint32_t new_sgsn_tlli; - uint32_t new_bss_tlli = 0; - - new_sgsn_tlli = gprs_tmsi2tlli(new_sgsn_ptmsi, TLLI_LOCAL); - if (new_bss_ptmsi != GSM_RESERVED_TMSI) - new_bss_tlli = gprs_tmsi2tlli(new_bss_ptmsi, TLLI_LOCAL); - LOGP(DGPRS, LOGL_INFO, - "Assigning new TLLI %08x to SGSN, %08x to BSS\n", - new_sgsn_tlli, new_bss_tlli); - - gbproxy_reassign_tlli(&link_info->sgsn_tlli, - peer, new_sgsn_tlli); - gbproxy_reassign_tlli(&link_info->tlli, - peer, new_bss_tlli); - gbproxy_remove_matching_link_infos(peer, link_info); - } - - gbproxy_remove_stale_link_infos(peer, now); - - return rc; -} - - diff --git a/openbsc/src/gprs/gb_proxy_vty.c b/openbsc/src/gprs/gb_proxy_vty.c deleted file mode 100644 index 933b6b01..00000000 --- a/openbsc/src/gprs/gb_proxy_vty.c +++ /dev/null @@ -1,852 +0,0 @@ -/* - * (C) 2010 by Harald Welte - * (C) 2010 by On-Waves - * All Rights Reserved - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation; either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - * - */ - -#include -#include -#include -#include -#include - -#include -#include - -#include -#include - -#include -#include -#include -#include - -#include -#include -#include - -static struct gbproxy_config *g_cfg = NULL; - -/* - * vty code for mgcp below - */ -static struct cmd_node gbproxy_node = { - GBPROXY_NODE, - "%s(config-gbproxy)# ", - 1, -}; - -static const struct value_string keep_modes[] = { - {GBPROX_KEEP_NEVER, "never"}, - {GBPROX_KEEP_REATTACH, "re-attach"}, - {GBPROX_KEEP_IDENTIFIED, "identified"}, - {GBPROX_KEEP_ALWAYS, "always"}, - {0, NULL} -}; - -static const struct value_string match_ids[] = { - {GBPROX_MATCH_PATCHING, "patching"}, - {GBPROX_MATCH_ROUTING, "routing"}, - {0, NULL} -}; - -static void gbprox_vty_print_peer(struct vty *vty, struct gbproxy_peer *peer) -{ - struct gprs_ra_id raid; - gsm48_parse_ra(&raid, peer->ra); - - vty_out(vty, "NSEI %5u, PTP-BVCI %5u, " - "RAI %u-%u-%u-%u", - peer->nsei, peer->bvci, - raid.mcc, raid.mnc, raid.lac, raid.rac); - if (peer->blocked) - vty_out(vty, " [BVC-BLOCKED]"); - - vty_out(vty, "%s", VTY_NEWLINE); -} - -static int config_write_gbproxy(struct vty *vty) -{ - enum gbproxy_match_id match_id; - - vty_out(vty, "gbproxy%s", VTY_NEWLINE); - - vty_out(vty, " sgsn nsei %u%s", g_cfg->nsip_sgsn_nsei, - VTY_NEWLINE); - - if (g_cfg->core_mcc > 0) - vty_out(vty, " core-mobile-country-code %d%s", - g_cfg->core_mcc, VTY_NEWLINE); - if (g_cfg->core_mnc > 0) - vty_out(vty, " core-mobile-network-code %d%s", - g_cfg->core_mnc, VTY_NEWLINE); - - for (match_id = 0; match_id < ARRAY_SIZE(g_cfg->matches); ++match_id) { - struct gbproxy_match *match = &g_cfg->matches[match_id]; - if (match->re_str) - vty_out(vty, " match-imsi %s %s%s", - get_value_string(match_ids, match_id), - match->re_str, VTY_NEWLINE); - } - - if (g_cfg->core_apn != NULL) { - if (g_cfg->core_apn_size > 0) { - char str[500] = {0}; - vty_out(vty, " core-access-point-name %s%s", - gprs_apn_to_str(str, g_cfg->core_apn, - g_cfg->core_apn_size), - VTY_NEWLINE); - } else { - vty_out(vty, " core-access-point-name none%s", - VTY_NEWLINE); - } - } - - if (g_cfg->route_to_sgsn2) - vty_out(vty, " secondary-sgsn nsei %u%s", g_cfg->nsip_sgsn2_nsei, - VTY_NEWLINE); - - if (g_cfg->tlli_max_age > 0) - vty_out(vty, " link-list max-age %d%s", - g_cfg->tlli_max_age, VTY_NEWLINE); - if (g_cfg->tlli_max_len > 0) - vty_out(vty, " link-list max-length %d%s", - g_cfg->tlli_max_len, VTY_NEWLINE); - vty_out(vty, " link-list keep-mode %s%s", - get_value_string(keep_modes, g_cfg->keep_link_infos), - VTY_NEWLINE); - - - return CMD_SUCCESS; -} - -DEFUN(cfg_gbproxy, - cfg_gbproxy_cmd, - "gbproxy", - "Configure the Gb proxy") -{ - vty->node = GBPROXY_NODE; - return CMD_SUCCESS; -} - -DEFUN(cfg_nsip_sgsn_nsei, - cfg_nsip_sgsn_nsei_cmd, - "sgsn nsei <0-65534>", - "SGSN information\n" - "NSEI to be used in the connection with the SGSN\n" - "The NSEI\n") -{ - unsigned int nsei = atoi(argv[0]); - - if (g_cfg->route_to_sgsn2 && g_cfg->nsip_sgsn2_nsei == nsei) { - vty_out(vty, "SGSN NSEI %d conflicts with secondary SGSN NSEI%s", - nsei, VTY_NEWLINE); - return CMD_WARNING; - } - - g_cfg->nsip_sgsn_nsei = nsei; - return CMD_SUCCESS; -} - -#define GBPROXY_CORE_MNC_STR "Use this network code for the core network\n" - -DEFUN(cfg_gbproxy_core_mnc, - cfg_gbproxy_core_mnc_cmd, - "core-mobile-network-code <1-999>", - GBPROXY_CORE_MNC_STR "NCC value\n") -{ - g_cfg->core_mnc = atoi(argv[0]); - return CMD_SUCCESS; -} - -DEFUN(cfg_gbproxy_no_core_mnc, - cfg_gbproxy_no_core_mnc_cmd, - "no core-mobile-network-code", - NO_STR GBPROXY_CORE_MNC_STR) -{ - g_cfg->core_mnc = 0; - return CMD_SUCCESS; -} - -#define GBPROXY_CORE_MCC_STR "Use this country code for the core network\n" - -DEFUN(cfg_gbproxy_core_mcc, - cfg_gbproxy_core_mcc_cmd, - "core-mobile-country-code <1-999>", - GBPROXY_CORE_MCC_STR "MCC value\n") -{ - g_cfg->core_mcc = atoi(argv[0]); - return CMD_SUCCESS; -} - -DEFUN(cfg_gbproxy_no_core_mcc, - cfg_gbproxy_no_core_mcc_cmd, - "no core-mobile-country-code", - NO_STR GBPROXY_CORE_MCC_STR) -{ - g_cfg->core_mcc = 0; - return CMD_SUCCESS; -} - -#define GBPROXY_MATCH_IMSI_STR "Restrict actions to certain IMSIs\n" - -DEFUN(cfg_gbproxy_match_imsi, - cfg_gbproxy_match_imsi_cmd, - "match-imsi (patching|routing) .REGEXP", - GBPROXY_MATCH_IMSI_STR - "Patch MS related information elements on match only\n" - "Route to the secondary SGSN on match only\n" - "Regular expression for the IMSI match\n") -{ - const char *filter = argv[1]; - const char *err_msg = NULL; - struct gbproxy_match *match; - enum gbproxy_match_id match_id = get_string_value(match_ids, argv[0]); - - OSMO_ASSERT(match_id >= GBPROX_MATCH_PATCHING && - match_id < GBPROX_MATCH_LAST); - match = &g_cfg->matches[match_id]; - - if (gbproxy_set_patch_filter(match, filter, &err_msg) != 0) { - vty_out(vty, "Match expression invalid: %s%s", - err_msg, VTY_NEWLINE); - return CMD_WARNING; - } - - g_cfg->acquire_imsi = 1; - - return CMD_SUCCESS; -} - -DEFUN(cfg_gbproxy_no_match_imsi, - cfg_gbproxy_no_match_imsi_cmd, - "no match-imsi", - NO_STR GBPROXY_MATCH_IMSI_STR) -{ - enum gbproxy_match_id match_id; - - for (match_id = 0; match_id < ARRAY_SIZE(g_cfg->matches); ++match_id) - gbproxy_clear_patch_filter(&g_cfg->matches[match_id]); - - g_cfg->acquire_imsi = 0; - - return CMD_SUCCESS; -} - -#define GBPROXY_CORE_APN_STR "Use this access point name (APN) for the backbone\n" -#define GBPROXY_CORE_APN_ARG_STR "Replace APN by this string\n" "Remove APN\n" - -static int set_core_apn(struct vty *vty, const char *apn) -{ - int apn_len; - - if (!apn) { - talloc_free(g_cfg->core_apn); - g_cfg->core_apn = NULL; - g_cfg->core_apn_size = 0; - return CMD_SUCCESS; - } - - apn_len = strlen(apn); - - if (apn_len >= 100) { - vty_out(vty, "APN string too long (max 99 chars)%s", - VTY_NEWLINE); - return CMD_WARNING; - } - - if (apn_len == 0) { - talloc_free(g_cfg->core_apn); - /* TODO: replace NULL */ - g_cfg->core_apn = talloc_zero_size(NULL, 2); - g_cfg->core_apn_size = 0; - } else { - /* TODO: replace NULL */ - g_cfg->core_apn = - talloc_realloc_size(NULL, g_cfg->core_apn, apn_len + 1); - g_cfg->core_apn_size = - gprs_str_to_apn(g_cfg->core_apn, apn_len + 1, apn); - } - - return CMD_SUCCESS; -} - -DEFUN(cfg_gbproxy_core_apn, - cfg_gbproxy_core_apn_cmd, - "core-access-point-name (APN|none)", - GBPROXY_CORE_APN_STR GBPROXY_CORE_APN_ARG_STR) -{ - if (strcmp(argv[0], "none") == 0) - return set_core_apn(vty, ""); - else - return set_core_apn(vty, argv[0]); -} - -DEFUN(cfg_gbproxy_no_core_apn, - cfg_gbproxy_no_core_apn_cmd, - "no core-access-point-name", - NO_STR GBPROXY_CORE_APN_STR) -{ - return set_core_apn(vty, NULL); -} - -/* TODO: Remove the patch-ptmsi command, since P-TMSI patching is enabled - * automatically when needed. This command is only left for manual testing - * (e.g. doing P-TMSI patching without using a secondary SGSN) - */ -#define GBPROXY_PATCH_PTMSI_STR "Patch P-TMSI/TLLI\n" - -DEFUN(cfg_gbproxy_patch_ptmsi, - cfg_gbproxy_patch_ptmsi_cmd, - "patch-ptmsi", - GBPROXY_PATCH_PTMSI_STR) -{ - g_cfg->patch_ptmsi = 1; - - return CMD_SUCCESS; -} - -DEFUN(cfg_gbproxy_no_patch_ptmsi, - cfg_gbproxy_no_patch_ptmsi_cmd, - "no patch-ptmsi", - NO_STR GBPROXY_PATCH_PTMSI_STR) -{ - g_cfg->patch_ptmsi = 0; - - return CMD_SUCCESS; -} - -/* TODO: Remove the acquire-imsi command, since that feature is enabled - * automatically when IMSI matching is enabled. This command is only left for - * manual testing (e.g. doing IMSI acquisition without IMSI based patching) - */ -#define GBPROXY_ACQUIRE_IMSI_STR "Acquire the IMSI before establishing a LLC connection (Experimental)\n" - -DEFUN(cfg_gbproxy_acquire_imsi, - cfg_gbproxy_acquire_imsi_cmd, - "acquire-imsi", - GBPROXY_ACQUIRE_IMSI_STR) -{ - g_cfg->acquire_imsi = 1; - - return CMD_SUCCESS; -} - -DEFUN(cfg_gbproxy_no_acquire_imsi, - cfg_gbproxy_no_acquire_imsi_cmd, - "no acquire-imsi", - NO_STR GBPROXY_ACQUIRE_IMSI_STR) -{ - g_cfg->acquire_imsi = 0; - - return CMD_SUCCESS; -} - -#define GBPROXY_SECOND_SGSN_STR "Route matching LLC connections to a second SGSN (Experimental)\n" - -DEFUN(cfg_gbproxy_secondary_sgsn, - cfg_gbproxy_secondary_sgsn_cmd, - "secondary-sgsn nsei <0-65534>", - GBPROXY_SECOND_SGSN_STR - "NSEI to be used in the connection with the SGSN\n" - "The NSEI\n") -{ - unsigned int nsei = atoi(argv[0]); - - if (g_cfg->nsip_sgsn_nsei == nsei) { - vty_out(vty, "Secondary SGSN NSEI %d conflicts with primary SGSN NSEI%s", - nsei, VTY_NEWLINE); - return CMD_WARNING; - } - - g_cfg->route_to_sgsn2 = 1; - g_cfg->nsip_sgsn2_nsei = nsei; - - g_cfg->patch_ptmsi = 1; - - return CMD_SUCCESS; -} - -DEFUN(cfg_gbproxy_no_secondary_sgsn, - cfg_gbproxy_no_secondary_sgsn_cmd, - "no secondary-sgsn", - NO_STR GBPROXY_SECOND_SGSN_STR) -{ - g_cfg->route_to_sgsn2 = 0; - g_cfg->nsip_sgsn2_nsei = 0xFFFF; - - g_cfg->patch_ptmsi = 0; - - return CMD_SUCCESS; -} - -#define GBPROXY_LINK_LIST_STR "Set TLLI list parameters\n" -#define GBPROXY_MAX_AGE_STR "Limit maximum age\n" - -DEFUN(cfg_gbproxy_link_list_max_age, - cfg_gbproxy_link_list_max_age_cmd, - "link-list max-age <1-999999>", - GBPROXY_LINK_LIST_STR GBPROXY_MAX_AGE_STR - "Maximum age in seconds\n") -{ - g_cfg->tlli_max_age = atoi(argv[0]); - - return CMD_SUCCESS; -} - -DEFUN(cfg_gbproxy_link_list_no_max_age, - cfg_gbproxy_link_list_no_max_age_cmd, - "no link-list max-age", - NO_STR GBPROXY_LINK_LIST_STR GBPROXY_MAX_AGE_STR) -{ - g_cfg->tlli_max_age = 0; - - return CMD_SUCCESS; -} - -#define GBPROXY_MAX_LEN_STR "Limit list length\n" - -DEFUN(cfg_gbproxy_link_list_max_len, - cfg_gbproxy_link_list_max_len_cmd, - "link-list max-length <1-99999>", - GBPROXY_LINK_LIST_STR GBPROXY_MAX_LEN_STR - "Maximum number of logical links in the list\n") -{ - g_cfg->tlli_max_len = atoi(argv[0]); - - return CMD_SUCCESS; -} - -DEFUN(cfg_gbproxy_link_list_no_max_len, - cfg_gbproxy_link_list_no_max_len_cmd, - "no link-list max-length", - NO_STR GBPROXY_LINK_LIST_STR GBPROXY_MAX_LEN_STR) -{ - g_cfg->tlli_max_len = 0; - - return CMD_SUCCESS; -} - -DEFUN(cfg_gbproxy_link_list_keep_mode, - cfg_gbproxy_link_list_keep_mode_cmd, - "link-list keep-mode (never|re-attach|identified|always)", - GBPROXY_LINK_LIST_STR "How to keep entries for detached logical links\n" - "Discard entry immediately after detachment\n" - "Keep entry if a re-attachment has be requested\n" - "Keep entry if it associated with an IMSI\n" - "Don't discard entries after detachment\n") -{ - int val = get_string_value(keep_modes, argv[0]); - OSMO_ASSERT(val >= GBPROX_KEEP_NEVER && val <= GBPROX_KEEP_ALWAYS); - g_cfg->keep_link_infos = val; - - return CMD_SUCCESS; -} - - -DEFUN(show_gbproxy, show_gbproxy_cmd, "show gbproxy [stats]", - SHOW_STR "Display information about the Gb proxy\n" "Show statistics\n") -{ - struct gbproxy_peer *peer; - int show_stats = argc >= 1; - - if (show_stats) - vty_out_rate_ctr_group(vty, "", g_cfg->ctrg); - - llist_for_each_entry(peer, &g_cfg->bts_peers, list) { - gbprox_vty_print_peer(vty, peer); - - if (show_stats) - vty_out_rate_ctr_group(vty, " ", peer->ctrg); - } - return CMD_SUCCESS; -} - -DEFUN(show_gbproxy_links, show_gbproxy_links_cmd, "show gbproxy links", - SHOW_STR "Display information about the Gb proxy\n" "Show logical links\n") -{ - struct gbproxy_peer *peer; - char mi_buf[200]; - time_t now; - struct timespec ts = {0,}; - - clock_gettime(CLOCK_MONOTONIC, &ts); - now = ts.tv_sec; - - llist_for_each_entry(peer, &g_cfg->bts_peers, list) { - struct gbproxy_link_info *link_info; - struct gbproxy_patch_state *state = &peer->patch_state; - - gbprox_vty_print_peer(vty, peer); - - llist_for_each_entry(link_info, &state->logical_links, list) { - time_t age = now - link_info->timestamp; - int stored_msgs = 0; - struct llist_head *iter; - llist_for_each(iter, &link_info->stored_msgs) - stored_msgs++; - - if (link_info->imsi > 0) { - snprintf(mi_buf, sizeof(mi_buf), "(invalid)"); - gsm48_mi_to_string(mi_buf, sizeof(mi_buf), - link_info->imsi, - link_info->imsi_len); - } else { - snprintf(mi_buf, sizeof(mi_buf), "(none)"); - } - vty_out(vty, " TLLI %08x, IMSI %s, AGE %d", - link_info->tlli.current, mi_buf, (int)age); - - if (stored_msgs) - vty_out(vty, ", STORED %d", stored_msgs); - - if (g_cfg->route_to_sgsn2) - vty_out(vty, ", SGSN NSEI %d", - link_info->sgsn_nsei); - - if (link_info->is_deregistered) - vty_out(vty, ", DE-REGISTERED"); - - vty_out(vty, "%s", VTY_NEWLINE); - } - } - return CMD_SUCCESS; -} - -DEFUN(delete_gb_bvci, delete_gb_bvci_cmd, - "delete-gbproxy-peer <0-65534> bvci <2-65534>", - "Delete a GBProxy peer by NSEI and optionally BVCI\n" - "NSEI number\n" - "Only delete peer with a matching BVCI\n" - "BVCI number\n") -{ - const uint16_t nsei = atoi(argv[0]); - const uint16_t bvci = atoi(argv[1]); - int counter; - - counter = gbproxy_cleanup_peers(g_cfg, nsei, bvci); - - if (counter == 0) { - vty_out(vty, "BVC not found%s", VTY_NEWLINE); - return CMD_WARNING; - } - - return CMD_SUCCESS; -} - -DEFUN(delete_gb_nsei, delete_gb_nsei_cmd, - "delete-gbproxy-peer <0-65534> (only-bvc|only-nsvc|all) [dry-run]", - "Delete a GBProxy peer by NSEI and optionally BVCI\n" - "NSEI number\n" - "Only delete BSSGP connections (BVC)\n" - "Only delete dynamic NS connections (NS-VC)\n" - "Delete BVC and dynamic NS connections\n" - "Show what would be deleted instead of actually deleting\n" - ) -{ - const uint16_t nsei = atoi(argv[0]); - const char *mode = argv[1]; - int dry_run = argc > 2; - int delete_bvc = 0; - int delete_nsvc = 0; - int counter; - - if (strcmp(mode, "only-bvc") == 0) - delete_bvc = 1; - else if (strcmp(mode, "only-nsvc") == 0) - delete_nsvc = 1; - else - delete_bvc = delete_nsvc = 1; - - if (delete_bvc) { - if (!dry_run) - counter = gbproxy_cleanup_peers(g_cfg, nsei, 0); - else { - struct gbproxy_peer *peer; - counter = 0; - llist_for_each_entry(peer, &g_cfg->bts_peers, list) { - if (peer->nsei != nsei) - continue; - - vty_out(vty, "BVC: "); - gbprox_vty_print_peer(vty, peer); - counter += 1; - } - } - vty_out(vty, "%sDeleted %d BVC%s", - dry_run ? "Not " : "", counter, VTY_NEWLINE); - } - - if (delete_nsvc) { - struct gprs_ns_inst *nsi = g_cfg->nsi; - struct gprs_nsvc *nsvc, *nsvc2; - - counter = 0; - llist_for_each_entry_safe(nsvc, nsvc2, &nsi->gprs_nsvcs, list) { - if (nsvc->nsei != nsei) - continue; - if (nsvc->persistent) - continue; - - if (!dry_run) - gprs_nsvc_delete(nsvc); - else - vty_out(vty, "NS-VC: NSEI %5u, NS-VCI %5u, " - "remote %s%s", - nsvc->nsei, nsvc->nsvci, - gprs_ns_ll_str(nsvc), VTY_NEWLINE); - counter += 1; - } - vty_out(vty, "%sDeleted %d NS-VC%s", - dry_run ? "Not " : "", counter, VTY_NEWLINE); - } - - return CMD_SUCCESS; -} - -#define GBPROXY_DELETE_LINK_STR \ - "Delete a GBProxy logical link entry by NSEI and identification\nNSEI number\n" - -DEFUN(delete_gb_link_by_id, delete_gb_link_by_id_cmd, - "delete-gbproxy-link <0-65534> (tlli|imsi|sgsn-nsei) IDENT", - GBPROXY_DELETE_LINK_STR - "Delete entries with a matching TLLI (hex)\n" - "Delete entries with a matching IMSI\n" - "Delete entries with a matching SGSN NSEI\n" - "Identification to match\n") -{ - const uint16_t nsei = atoi(argv[0]); - enum {MATCH_TLLI = 't', MATCH_IMSI = 'i', MATCH_SGSN = 's'} match; - uint32_t ident = 0; - const char *imsi = NULL; - struct gbproxy_peer *peer = 0; - struct gbproxy_link_info *link_info, *nxt; - struct gbproxy_patch_state *state; - char mi_buf[200]; - int found = 0; - - match = argv[1][0]; - - switch (match) { - case MATCH_TLLI: ident = strtoll(argv[2], NULL, 16); break; - case MATCH_IMSI: imsi = argv[2]; break; - case MATCH_SGSN: ident = strtoll(argv[2], NULL, 0); break; - }; - - peer = gbproxy_peer_by_nsei(g_cfg, nsei); - if (!peer) { - vty_out(vty, "Didn't find peer with NSEI %d%s", - nsei, VTY_NEWLINE); - return CMD_WARNING; - } - - state = &peer->patch_state; - - llist_for_each_entry_safe(link_info, nxt, &state->logical_links, list) { - switch (match) { - case MATCH_TLLI: - if (link_info->tlli.current != ident) - continue; - break; - case MATCH_SGSN: - if (link_info->sgsn_nsei != ident) - continue; - break; - case MATCH_IMSI: - if (!link_info->imsi) - continue; - mi_buf[0] = '\0'; - gsm48_mi_to_string(mi_buf, sizeof(mi_buf), - link_info->imsi, - link_info->imsi_len); - - if (strcmp(mi_buf, imsi) != 0) - continue; - break; - } - - vty_out(vty, "Deleting link with TLLI %08x%s", link_info->tlli.current, - VTY_NEWLINE); - gbproxy_delete_link_info(peer, link_info); - found += 1; - } - - if (!found && argc >= 2) { - vty_out(vty, "Didn't find link entry with %s %s%s", - argv[1], argv[2], VTY_NEWLINE); - } - - return CMD_SUCCESS; -} - -DEFUN(delete_gb_link, delete_gb_link_cmd, - "delete-gbproxy-link <0-65534> (stale|de-registered)", - GBPROXY_DELETE_LINK_STR - "Delete stale entries\n" - "Delete de-registered entries\n") -{ - const uint16_t nsei = atoi(argv[0]); - enum {MATCH_STALE = 's', MATCH_DEREGISTERED = 'd'} match; - struct gbproxy_peer *peer = 0; - struct gbproxy_link_info *link_info, *nxt; - struct gbproxy_patch_state *state; - time_t now; - struct timespec ts = {0,}; - - int found = 0; - - match = argv[1][0]; - - peer = gbproxy_peer_by_nsei(g_cfg, nsei); - if (!peer) { - vty_out(vty, "Didn't find peer with NSEI %d%s", - nsei, VTY_NEWLINE); - return CMD_WARNING; - } - - state = &peer->patch_state; - - clock_gettime(CLOCK_MONOTONIC, &ts); - now = ts.tv_sec; - - if (match == MATCH_STALE) { - found = gbproxy_remove_stale_link_infos(peer, now); - if (found) - vty_out(vty, "Deleted %d stale logical link%s%s", - found, found == 1 ? "" : "s", VTY_NEWLINE); - } else { - llist_for_each_entry_safe(link_info, nxt, - &state->logical_links, list) { - if (!link_info->is_deregistered) - continue; - - gbproxy_delete_link_info(peer, link_info); - found += 1; - } - } - - if (found) - vty_out(vty, "Deleted %d %s logical link%s%s", - found, argv[1], found == 1 ? "" : "s", VTY_NEWLINE); - - return CMD_SUCCESS; -} - -/* - * legacy commands to provide an upgrade path from "broken" releases - * or pre-releases - */ -DEFUN_DEPRECATED(cfg_gbproxy_broken_apn_match, - cfg_gbproxy_broken_apn_match_cmd, - "core-access-point-name none match-imsi .REGEXP", - GBPROXY_CORE_APN_STR GBPROXY_MATCH_IMSI_STR "Remove APN\n" - "Patch MS related information elements on match only\n" - "Route to the secondary SGSN on match only\n" - "Regular expression for the IMSI match\n") -{ - const char *filter = argv[0]; - const char *err_msg = NULL; - struct gbproxy_match *match; - enum gbproxy_match_id match_id = get_string_value(match_ids, "patching"); - - /* apply APN none */ - set_core_apn(vty, ""); - - /* do the matching... with copy and paste */ - OSMO_ASSERT(match_id >= GBPROX_MATCH_PATCHING && - match_id < GBPROX_MATCH_LAST); - match = &g_cfg->matches[match_id]; - - if (gbproxy_set_patch_filter(match, filter, &err_msg) != 0) { - vty_out(vty, "Match expression invalid: %s%s", - err_msg, VTY_NEWLINE); - return CMD_WARNING; - } - - g_cfg->acquire_imsi = 1; - - return CMD_SUCCESS; -} - -#define GBPROXY_TLLI_LIST_STR "Set TLLI list parameters\n" -#define GBPROXY_MAX_LEN_STR "Limit list length\n" -DEFUN_DEPRECATED(cfg_gbproxy_depr_tlli_list_max_len, - cfg_gbproxy_depr_tlli_list_max_len_cmd, - "tlli-list max-length <1-99999>", - GBPROXY_TLLI_LIST_STR GBPROXY_MAX_LEN_STR - "Maximum number of TLLIs in the list\n") -{ - g_cfg->tlli_max_len = atoi(argv[0]); - - return CMD_SUCCESS; -} - -int gbproxy_vty_init(void) -{ - install_element_ve(&show_gbproxy_cmd); - install_element_ve(&show_gbproxy_links_cmd); - - install_element(ENABLE_NODE, &delete_gb_bvci_cmd); - install_element(ENABLE_NODE, &delete_gb_nsei_cmd); - install_element(ENABLE_NODE, &delete_gb_link_by_id_cmd); - install_element(ENABLE_NODE, &delete_gb_link_cmd); - - install_element(CONFIG_NODE, &cfg_gbproxy_cmd); - install_node(&gbproxy_node, config_write_gbproxy); - vty_install_default(GBPROXY_NODE); - install_element(GBPROXY_NODE, &cfg_nsip_sgsn_nsei_cmd); - install_element(GBPROXY_NODE, &cfg_gbproxy_core_mcc_cmd); - install_element(GBPROXY_NODE, &cfg_gbproxy_core_mnc_cmd); - install_element(GBPROXY_NODE, &cfg_gbproxy_match_imsi_cmd); - install_element(GBPROXY_NODE, &cfg_gbproxy_core_apn_cmd); - install_element(GBPROXY_NODE, &cfg_gbproxy_secondary_sgsn_cmd); - install_element(GBPROXY_NODE, &cfg_gbproxy_patch_ptmsi_cmd); - install_element(GBPROXY_NODE, &cfg_gbproxy_acquire_imsi_cmd); - install_element(GBPROXY_NODE, &cfg_gbproxy_link_list_max_age_cmd); - install_element(GBPROXY_NODE, &cfg_gbproxy_link_list_max_len_cmd); - install_element(GBPROXY_NODE, &cfg_gbproxy_link_list_keep_mode_cmd); - install_element(GBPROXY_NODE, &cfg_gbproxy_no_core_mcc_cmd); - install_element(GBPROXY_NODE, &cfg_gbproxy_no_core_mnc_cmd); - install_element(GBPROXY_NODE, &cfg_gbproxy_no_match_imsi_cmd); - install_element(GBPROXY_NODE, &cfg_gbproxy_no_core_apn_cmd); - install_element(GBPROXY_NODE, &cfg_gbproxy_no_secondary_sgsn_cmd); - install_element(GBPROXY_NODE, &cfg_gbproxy_no_patch_ptmsi_cmd); - install_element(GBPROXY_NODE, &cfg_gbproxy_no_acquire_imsi_cmd); - install_element(GBPROXY_NODE, &cfg_gbproxy_link_list_no_max_age_cmd); - install_element(GBPROXY_NODE, &cfg_gbproxy_link_list_no_max_len_cmd); - - /* broken or deprecated to allow an upgrade path */ - install_element(GBPROXY_NODE, &cfg_gbproxy_broken_apn_match_cmd); - install_element(GBPROXY_NODE, &cfg_gbproxy_depr_tlli_list_max_len_cmd); - - return 0; -} - -int gbproxy_parse_config(const char *config_file, struct gbproxy_config *cfg) -{ - int rc; - - g_cfg = cfg; - rc = vty_read_config_file(config_file, NULL); - if (rc < 0) { - fprintf(stderr, "Failed to parse the config file: '%s'\n", config_file); - return rc; - } - - return 0; -} - diff --git a/openbsc/src/gprs/gprs_gb_parse.c b/openbsc/src/gprs/gprs_gb_parse.c deleted file mode 100644 index d5a122bd..00000000 --- a/openbsc/src/gprs/gprs_gb_parse.c +++ /dev/null @@ -1,636 +0,0 @@ -/* GPRS Gb message parser */ - -/* (C) 2014 by On-Waves - * All Rights Reserved - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation; either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - * - */ - -#include -#include - -#include - -#include - -#include - -#include - -static int gprs_gb_parse_gmm_attach_req(uint8_t *data, size_t data_len, - struct gprs_gb_parse_context *parse_ctx) -{ - uint8_t *value; - size_t value_len; - - parse_ctx->llc_msg_name = "ATTACH_REQ"; - - /* Skip MS network capability */ - if (osmo_shift_lv(&data, &data_len, NULL, &value_len) <= 0 || - value_len < 1 || value_len > 8) - /* invalid */ - return 0; - - /* Skip Attach type */ - /* Skip Ciphering key sequence number */ - /* Skip DRX parameter */ - osmo_shift_v_fixed(&data, &data_len, 3, NULL); - - /* Get Mobile identity */ - if (osmo_shift_lv(&data, &data_len, &value, &value_len) <= 0 || - value_len < 5 || value_len > 8) - /* invalid */ - return 0; - - if (gprs_is_mi_tmsi(value, value_len)) { - parse_ctx->ptmsi_enc = value + 1; - } else if (gprs_is_mi_imsi(value, value_len)) { - parse_ctx->imsi = value; - parse_ctx->imsi_len = value_len; - } - - if (osmo_shift_v_fixed(&data, &data_len, 6, &value) <= 0) - return 0; - - parse_ctx->old_raid_enc = value; - - return 1; -} - -static int gprs_gb_parse_gmm_attach_ack(uint8_t *data, size_t data_len, - struct gprs_gb_parse_context *parse_ctx) -{ - uint8_t *value; - size_t value_len; - - parse_ctx->llc_msg_name = "ATTACH_ACK"; - - /* Skip Attach result */ - /* Skip Force to standby */ - /* Skip Periodic RA update timer */ - /* Skip Radio priority for SMS */ - /* Skip Spare half octet */ - osmo_shift_v_fixed(&data, &data_len, 3, NULL); - - if (osmo_shift_v_fixed(&data, &data_len, 6, &value) <= 0) - return 0; - - parse_ctx->raid_enc = value; - - /* Skip P-TMSI signature (P-TMSI signature, opt, TV, length 4) */ - osmo_match_shift_tv_fixed(&data, &data_len, GSM48_IE_GMM_PTMSI_SIG, 3, NULL); - - /* Skip Negotiated READY timer value (GPRS timer, opt, TV, length 2) */ - osmo_match_shift_tv_fixed(&data, &data_len, GSM48_IE_GMM_TIMER_READY, 1, NULL); - - /* Allocated P-TMSI (Mobile identity, opt, TLV, length 7) */ - if (osmo_match_shift_tlv(&data, &data_len, GSM48_IE_GMM_ALLOC_PTMSI, - &value, &value_len) > 0 && - gprs_is_mi_tmsi(value, value_len)) - parse_ctx->new_ptmsi_enc = value + 1; - return 1; -} - -static int gprs_gb_parse_gmm_attach_rej(uint8_t *data, size_t data_len, - struct gprs_gb_parse_context *parse_ctx) -{ - uint8_t *value; - - parse_ctx->llc_msg_name = "ATTACH_REJ"; - - /* GMM cause */ - if (osmo_shift_v_fixed(&data, &data_len, 1, &value) <= 0) - return 0; - - parse_ctx->invalidate_tlli = 1; - - return 1; -} - - -static int gprs_gb_parse_gmm_detach_req(uint8_t *data, size_t data_len, - struct gprs_gb_parse_context *parse_ctx) -{ - uint8_t *value; - size_t value_len; - int detach_type; - int power_off; - - parse_ctx->llc_msg_name = "DETACH_REQ"; - - /* Skip spare half octet */ - /* Get Detach type */ - if (osmo_shift_v_fixed(&data, &data_len, 1, &value) <= 0) - /* invalid */ - return 0; - - detach_type = *value & 0x07; - power_off = *value & 0x08 ? 1 : 0; - - if (parse_ctx->to_bss) { - /* Network originated */ - if (detach_type == GPRS_DET_T_MT_REATT_REQ) - parse_ctx->await_reattach = 1; - } else { - /* Mobile originated */ - - if (power_off) - parse_ctx->invalidate_tlli = 1; - - /* Get P-TMSI (Mobile identity), see GSM 24.008, 9.4.5.2 */ - if (osmo_match_shift_tlv(&data, &data_len, - GSM48_IE_GMM_ALLOC_PTMSI, &value, &value_len) > 0) - { - if (gprs_is_mi_tmsi(value, value_len)) - parse_ctx->ptmsi_enc = value + 1; - } - } - - return 1; -} - -static int gprs_gb_parse_gmm_ra_upd_req(uint8_t *data, size_t data_len, - struct gprs_gb_parse_context *parse_ctx) -{ - uint8_t *value; - - parse_ctx->llc_msg_name = "RA_UPD_REQ"; - - /* Skip Update type */ - /* Skip GPRS ciphering key sequence number */ - osmo_shift_v_fixed(&data, &data_len, 1, NULL); - - if (osmo_shift_v_fixed(&data, &data_len, 6, &value) <= 0) - return 0; - - parse_ctx->old_raid_enc = value; - - return 1; -} - -static int gprs_gb_parse_gmm_ra_upd_rej(uint8_t *data, size_t data_len, - struct gprs_gb_parse_context *parse_ctx) -{ - uint8_t *value; - uint8_t cause; - int force_standby; - - parse_ctx->llc_msg_name = "RA_UPD_REJ"; - - /* GMM cause */ - if (osmo_shift_v_fixed(&data, &data_len, 1, &value) <= 0) - return 0; - - cause = value[0]; - - /* Force to standby, 1/2 */ - /* spare bits, 1/2 */ - if (osmo_shift_v_fixed(&data, &data_len, 1, &value) <= 0) - return 0; - - force_standby = (value[0] & 0x07) == 0x01; - - if (cause == GMM_CAUSE_IMPL_DETACHED && !force_standby) - parse_ctx->await_reattach = 1; - - parse_ctx->invalidate_tlli = 1; - - return 1; -} - -static int gprs_gb_parse_gmm_ra_upd_ack(uint8_t *data, size_t data_len, - struct gprs_gb_parse_context *parse_ctx) -{ - uint8_t *value; - size_t value_len; - - parse_ctx->llc_msg_name = "RA_UPD_ACK"; - - /* Skip Force to standby */ - /* Skip Update result */ - /* Skip Periodic RA update timer */ - osmo_shift_v_fixed(&data, &data_len, 2, NULL); - - if (osmo_shift_v_fixed(&data, &data_len, 6, &value) <= 0) - return 0; - - parse_ctx->raid_enc = value; - - /* Skip P-TMSI signature (P-TMSI signature, opt, TV, length 4) */ - osmo_match_shift_tv_fixed(&data, &data_len, GSM48_IE_GMM_PTMSI_SIG, 3, NULL); - - /* Allocated P-TMSI (Mobile identity, opt, TLV, length 7) */ - if (osmo_match_shift_tlv(&data, &data_len, GSM48_IE_GMM_ALLOC_PTMSI, - &value, &value_len) > 0 && - gprs_is_mi_tmsi(value, value_len)) - parse_ctx->new_ptmsi_enc = value + 1; - - return 1; -} - -static int gprs_gb_parse_gmm_ptmsi_reall_cmd(uint8_t *data, size_t data_len, - struct gprs_gb_parse_context *parse_ctx) -{ - uint8_t *value; - size_t value_len; - - parse_ctx->llc_msg_name = "PTMSI_REALL_CMD"; - - LOGP(DLLC, LOGL_NOTICE, - "Got P-TMSI Reallocation Command which is not covered by unit tests yet.\n"); - - /* Allocated P-TMSI */ - if (osmo_shift_lv(&data, &data_len, &value, &value_len) > 0 && - gprs_is_mi_tmsi(value, value_len)) - parse_ctx->new_ptmsi_enc = value + 1; - - if (osmo_shift_v_fixed(&data, &data_len, 6, &value) <= 0) - return 0; - - parse_ctx->raid_enc = value; - - return 1; -} - -static int gprs_gb_parse_gmm_id_resp(uint8_t *data, size_t data_len, - struct gprs_gb_parse_context *parse_ctx) -{ - uint8_t *value; - size_t value_len; - - parse_ctx->llc_msg_name = "ID_RESP"; - - /* Mobile identity, Mobile identity 10.5.1.4, M LV 2-10 */ - if (osmo_shift_lv(&data, &data_len, &value, &value_len) <= 0 || - value_len < 1 || value_len > 9) - /* invalid */ - return 0; - - if (gprs_is_mi_tmsi(value, value_len)) { - parse_ctx->ptmsi_enc = value + 1; - } else if (gprs_is_mi_imsi(value, value_len)) { - parse_ctx->imsi = value; - parse_ctx->imsi_len = value_len; - } - - return 1; -} - -static int gprs_gb_parse_gsm_act_pdp_req(uint8_t *data, size_t data_len, - struct gprs_gb_parse_context *parse_ctx) -{ - ssize_t old_len; - uint8_t *value; - size_t value_len; - - parse_ctx->llc_msg_name = "ACT_PDP_REQ"; - - /* Skip Requested NSAPI */ - /* Skip Requested LLC SAPI */ - osmo_shift_v_fixed(&data, &data_len, 2, NULL); - - /* Skip Requested QoS (support 04.08 and 24.008) */ - if (osmo_shift_lv(&data, &data_len, NULL, &value_len) <= 0 || - value_len < 4 || value_len > 14) - /* invalid */ - return 0; - - /* Skip Requested PDP address */ - if (osmo_shift_lv(&data, &data_len, NULL, &value_len) <= 0 || - value_len < 2 || value_len > 18) - /* invalid */ - return 0; - - /* Access point name */ - old_len = osmo_match_shift_tlv(&data, &data_len, - GSM48_IE_GSM_APN, &value, &value_len); - - if (old_len > 0 && value_len >=1 && value_len <= 100) { - parse_ctx->apn_ie = data - old_len; - parse_ctx->apn_ie_len = old_len; - } - - return 1; -} - -int gprs_gb_parse_dtap(uint8_t *data, size_t data_len, - struct gprs_gb_parse_context *parse_ctx) -{ - struct gsm48_hdr *g48h; - uint8_t pdisc; - uint8_t msg_type; - - if (osmo_shift_v_fixed(&data, &data_len, sizeof(*g48h), (uint8_t **)&g48h) <= 0) - return 0; - - parse_ctx->g48_hdr = g48h; - - pdisc = gsm48_hdr_pdisc(g48h); - if (pdisc != GSM48_PDISC_MM_GPRS && pdisc != GSM48_PDISC_SM_GPRS) - return 1; - - msg_type = gsm48_hdr_msg_type(g48h); - switch (msg_type) { - case GSM48_MT_GMM_ATTACH_REQ: - return gprs_gb_parse_gmm_attach_req(data, data_len, parse_ctx); - - case GSM48_MT_GMM_ATTACH_REJ: - return gprs_gb_parse_gmm_attach_rej(data, data_len, parse_ctx); - - case GSM48_MT_GMM_ATTACH_ACK: - return gprs_gb_parse_gmm_attach_ack(data, data_len, parse_ctx); - - case GSM48_MT_GMM_RA_UPD_REQ: - return gprs_gb_parse_gmm_ra_upd_req(data, data_len, parse_ctx); - - case GSM48_MT_GMM_RA_UPD_REJ: - return gprs_gb_parse_gmm_ra_upd_rej(data, data_len, parse_ctx); - - case GSM48_MT_GMM_RA_UPD_ACK: - return gprs_gb_parse_gmm_ra_upd_ack(data, data_len, parse_ctx); - - case GSM48_MT_GMM_PTMSI_REALL_CMD: - return gprs_gb_parse_gmm_ptmsi_reall_cmd(data, data_len, parse_ctx); - - case GSM48_MT_GSM_ACT_PDP_REQ: - return gprs_gb_parse_gsm_act_pdp_req(data, data_len, parse_ctx); - - case GSM48_MT_GMM_ID_RESP: - return gprs_gb_parse_gmm_id_resp(data, data_len, parse_ctx); - - case GSM48_MT_GMM_DETACH_REQ: - return gprs_gb_parse_gmm_detach_req(data, data_len, parse_ctx); - - case GSM48_MT_GMM_DETACH_ACK: - parse_ctx->llc_msg_name = "DETACH_ACK"; - parse_ctx->invalidate_tlli = 1; - break; - - default: - LOGP(DLLC, LOGL_NOTICE, - "Unhandled GSM 04.08 message type %s for protocol discriminator %s.\n", - get_value_string(gprs_msgt_gmm_names, msg_type), get_value_string(gsm48_pdisc_names, pdisc)); - break; - }; - - return 1; -} - -int gprs_gb_parse_llc(uint8_t *llc, size_t llc_len, - struct gprs_gb_parse_context *parse_ctx) -{ - struct gprs_llc_hdr_parsed *ghp = &parse_ctx->llc_hdr_parsed; - int rc; - int fcs; - - /* parse LLC */ - rc = gprs_llc_hdr_parse(ghp, llc, llc_len); - gprs_llc_hdr_dump(ghp, NULL); - if (rc != 0) { - LOGP(DLLC, LOGL_NOTICE, "Error during LLC header parsing\n"); - return 0; - } - - fcs = gprs_llc_fcs(llc, ghp->crc_length); - LOGP(DLLC, LOGL_DEBUG, "Got LLC message, CRC: %06x (computed %06x)\n", - ghp->fcs, fcs); - - if (!ghp->data) - return 0; - - if (ghp->sapi != GPRS_SAPI_GMM) - return 1; - - if (ghp->cmd != GPRS_LLC_UI) - return 1; - - if (ghp->is_encrypted) { - parse_ctx->need_decryption = 1; - return 0; - } - - return gprs_gb_parse_dtap(ghp->data, ghp->data_len, parse_ctx); -} - -int gprs_gb_parse_bssgp(uint8_t *bssgp, size_t bssgp_len, - struct gprs_gb_parse_context *parse_ctx) -{ - struct bssgp_normal_hdr *bgph; - struct bssgp_ud_hdr *budh = NULL; - struct tlv_parsed *tp = &parse_ctx->bssgp_tp; - uint8_t pdu_type; - uint8_t *data; - size_t data_len; - int rc; - - if (bssgp_len < sizeof(struct bssgp_normal_hdr)) - return 0; - - bgph = (struct bssgp_normal_hdr *)bssgp; - pdu_type = bgph->pdu_type; - - if (pdu_type == BSSGP_PDUT_UL_UNITDATA || - pdu_type == BSSGP_PDUT_DL_UNITDATA) { - if (bssgp_len < sizeof(struct bssgp_ud_hdr)) - return 0; - budh = (struct bssgp_ud_hdr *)bssgp; - bgph = NULL; - data = budh->data; - data_len = bssgp_len - sizeof(*budh); - } else { - data = bgph->data; - data_len = bssgp_len - sizeof(*bgph); - } - - parse_ctx->pdu_type = pdu_type; - parse_ctx->bud_hdr = budh; - parse_ctx->bgp_hdr = bgph; - parse_ctx->bssgp_data = data; - parse_ctx->bssgp_data_len = data_len; - - if (bssgp_tlv_parse(tp, data, data_len) < 0) - return 0; - - if (budh) - parse_ctx->tlli_enc = (uint8_t *)&budh->tlli; - - if (TLVP_PRESENT(tp, BSSGP_IE_ROUTEING_AREA)) - parse_ctx->bssgp_raid_enc = (uint8_t *)TLVP_VAL(tp, BSSGP_IE_ROUTEING_AREA); - - if (TLVP_PRESENT(tp, BSSGP_IE_CELL_ID)) - parse_ctx->bssgp_raid_enc = (uint8_t *)TLVP_VAL(tp, BSSGP_IE_CELL_ID); - - if (TLVP_PRESENT(tp, BSSGP_IE_IMSI)) { - parse_ctx->imsi = (uint8_t *)TLVP_VAL(tp, BSSGP_IE_IMSI); - parse_ctx->imsi_len = TLVP_LEN(tp, BSSGP_IE_IMSI); - } - - if (TLVP_PRESENT(tp, BSSGP_IE_TLLI)) { - if (parse_ctx->tlli_enc) - /* This is TLLI old, don't confuse it with TLLI current */ - parse_ctx->old_tlli_enc = (uint8_t *)TLVP_VAL(tp, BSSGP_IE_TLLI); - else - parse_ctx->tlli_enc = (uint8_t *)TLVP_VAL(tp, BSSGP_IE_TLLI); - } - - if (TLVP_PRESENT(tp, BSSGP_IE_TMSI) && pdu_type == BSSGP_PDUT_PAGING_PS) - parse_ctx->bssgp_ptmsi_enc = (uint8_t *)TLVP_VAL(tp, BSSGP_IE_TMSI); - - if (TLVP_PRESENT(tp, BSSGP_IE_LLC_PDU)) { - uint8_t *llc = (uint8_t *)TLVP_VAL(tp, BSSGP_IE_LLC_PDU); - size_t llc_len = TLVP_LEN(tp, BSSGP_IE_LLC_PDU); - - rc = gprs_gb_parse_llc(llc, llc_len, parse_ctx); - if (!rc) - return 0; - - parse_ctx->llc = llc; - parse_ctx->llc_len = llc_len; - } - - if (parse_ctx->tlli_enc) { - uint32_t tmp_tlli; - memcpy(&tmp_tlli, parse_ctx->tlli_enc, sizeof(tmp_tlli)); - parse_ctx->tlli = ntohl(tmp_tlli); - } - - if (parse_ctx->bssgp_raid_enc && parse_ctx->old_raid_enc && - memcmp(parse_ctx->bssgp_raid_enc, parse_ctx->old_raid_enc, 6) != 0) - parse_ctx->old_raid_is_foreign = 1; - - return 1; -} - -void gprs_gb_log_parse_context(int log_level, - struct gprs_gb_parse_context *parse_ctx, - const char *default_msg_name) -{ - const char *msg_name; - const char *sep = ""; - - if (!parse_ctx->tlli_enc && - !parse_ctx->ptmsi_enc && - !parse_ctx->new_ptmsi_enc && - !parse_ctx->bssgp_ptmsi_enc && - !parse_ctx->imsi) - return; - - msg_name = gprs_gb_message_name(parse_ctx, default_msg_name); - - if (parse_ctx->llc_msg_name) - msg_name = parse_ctx->llc_msg_name; - - LOGP(DGPRS, log_level, "%s: Got", msg_name); - - if (parse_ctx->tlli_enc) { - LOGPC(DGPRS, log_level, "%s TLLI %08x", sep, parse_ctx->tlli); - sep = ","; - } - - if (parse_ctx->old_tlli_enc) { - LOGPC(DGPRS, log_level, "%s old TLLI %02x%02x%02x%02x", sep, - parse_ctx->old_tlli_enc[0], - parse_ctx->old_tlli_enc[1], - parse_ctx->old_tlli_enc[2], - parse_ctx->old_tlli_enc[3]); - sep = ","; - } - - if (parse_ctx->bssgp_raid_enc) { - struct gprs_ra_id raid; - gsm48_parse_ra(&raid, parse_ctx->bssgp_raid_enc); - LOGPC(DGPRS, log_level, "%s BSSGP RAID %u-%u-%u-%u", sep, - raid.mcc, raid.mnc, raid.lac, raid.rac); - sep = ","; - } - - if (parse_ctx->raid_enc) { - struct gprs_ra_id raid; - gsm48_parse_ra(&raid, parse_ctx->raid_enc); - LOGPC(DGPRS, log_level, "%s RAID %u-%u-%u-%u", sep, - raid.mcc, raid.mnc, raid.lac, raid.rac); - sep = ","; - } - - if (parse_ctx->old_raid_enc) { - struct gprs_ra_id raid; - gsm48_parse_ra(&raid, parse_ctx->old_raid_enc); - LOGPC(DGPRS, log_level, "%s old RAID %u-%u-%u-%u", sep, - raid.mcc, raid.mnc, raid.lac, raid.rac); - sep = ","; - } - - if (parse_ctx->bssgp_ptmsi_enc) { - uint32_t ptmsi = GSM_RESERVED_TMSI; - gprs_parse_tmsi(parse_ctx->bssgp_ptmsi_enc, &ptmsi); - LOGPC(DGPRS, log_level, "%s BSSGP PTMSI %08x", sep, ptmsi); - sep = ","; - } - - if (parse_ctx->ptmsi_enc) { - uint32_t ptmsi = GSM_RESERVED_TMSI; - gprs_parse_tmsi(parse_ctx->ptmsi_enc, &ptmsi); - LOGPC(DGPRS, log_level, "%s PTMSI %08x", sep, ptmsi); - sep = ","; - } - - if (parse_ctx->new_ptmsi_enc) { - uint32_t new_ptmsi = GSM_RESERVED_TMSI; - gprs_parse_tmsi(parse_ctx->new_ptmsi_enc, &new_ptmsi); - LOGPC(DGPRS, log_level, "%s new PTMSI %08x", sep, new_ptmsi); - sep = ","; - } - - if (parse_ctx->imsi) { - char mi_buf[200]; - mi_buf[0] = '\0'; - gsm48_mi_to_string(mi_buf, sizeof(mi_buf), - parse_ctx->imsi, parse_ctx->imsi_len); - LOGPC(DGPRS, log_level, "%s IMSI %s", - sep, mi_buf); - sep = ","; - } - if (parse_ctx->invalidate_tlli) { - LOGPC(DGPRS, log_level, "%s invalidate", sep); - sep = ","; - } - if (parse_ctx->await_reattach) { - LOGPC(DGPRS, log_level, "%s re-attach", sep); - sep = ","; - } - - LOGPC(DGPRS, log_level, "\n"); -} - -const char *gprs_gb_message_name(const struct gprs_gb_parse_context *parse_ctx, - const char *default_msg_name) -{ - if (parse_ctx->llc_msg_name) - return parse_ctx->llc_msg_name; - - if (parse_ctx->g48_hdr) - return "GMM"; - - if (parse_ctx->llc) - return "LLC"; - - if (parse_ctx->bud_hdr) - return "BSSGP-UNITDATA"; - - if (parse_ctx->bgp_hdr) - return "BSSGP"; - - return "unknown"; -} diff --git a/openbsc/src/gprs/gprs_gmm.c b/openbsc/src/gprs/gprs_gmm.c deleted file mode 100644 index e6751db7..00000000 --- a/openbsc/src/gprs/gprs_gmm.c +++ /dev/null @@ -1,2939 +0,0 @@ -/* GSM Mobile Radio Interface Layer 3 messages on the A-bis interface - * 3GPP TS 04.08 version 7.21.0 Release 1998 / ETSI TS 100 940 V7.21.0 */ - -/* (C) 2009-2015 by Harald Welte - * (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 "bscconfig.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -#ifdef BUILD_IU -#include -#include -#endif - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -#define PTMSI_ALLOC - -extern struct sgsn_instance *sgsn; - -static const struct tlv_definition gsm48_gmm_att_tlvdef = { - .def = { - [GSM48_IE_GMM_CIPH_CKSN] = { TLV_TYPE_FIXED, 1 }, - [GSM48_IE_GMM_TIMER_READY] = { TLV_TYPE_TV, 1 }, - [GSM48_IE_GMM_ALLOC_PTMSI] = { TLV_TYPE_TLV, 0 }, - [GSM48_IE_GMM_PTMSI_SIG] = { TLV_TYPE_FIXED, 3 }, - [GSM48_IE_GMM_AUTH_RAND] = { TLV_TYPE_FIXED, 16 }, - [GSM48_IE_GMM_AUTH_SRES] = { TLV_TYPE_FIXED, 4 }, - [GSM48_IE_GMM_AUTH_RES_EXT] = { TLV_TYPE_TLV, 0 }, - [GSM48_IE_GMM_AUTH_FAIL_PAR] = { TLV_TYPE_TLV, 0 }, - [GSM48_IE_GMM_IMEISV] = { TLV_TYPE_TLV, 0 }, - [GSM48_IE_GMM_DRX_PARAM] = { TLV_TYPE_FIXED, 2 }, - [GSM48_IE_GMM_MS_NET_CAPA] = { TLV_TYPE_TLV, 0 }, - [GSM48_IE_GMM_PDP_CTX_STATUS] = { TLV_TYPE_TLV, 0 }, - [GSM48_IE_GMM_PS_LCS_CAPA] = { TLV_TYPE_TLV, 0 }, - [GSM48_IE_GMM_GMM_MBMS_CTX_ST] = { TLV_TYPE_TLV, 0 }, - }, -}; - -static const struct tlv_definition gsm48_sm_att_tlvdef = { - .def = { - [GSM48_IE_GSM_APN] = { TLV_TYPE_TLV, 0 }, - [GSM48_IE_GSM_PROTO_CONF_OPT] = { TLV_TYPE_TLV, 0 }, - [GSM48_IE_GSM_PDP_ADDR] = { TLV_TYPE_TLV, 0 }, - [GSM48_IE_GSM_AA_TMR] = { TLV_TYPE_TV, 1 }, - [GSM48_IE_GSM_NAME_FULL] = { TLV_TYPE_TLV, 0 }, - [GSM48_IE_GSM_NAME_SHORT] = { TLV_TYPE_TLV, 0 }, - [GSM48_IE_GSM_TIMEZONE] = { TLV_TYPE_FIXED, 1 }, - [GSM48_IE_GSM_UTC_AND_TZ] = { TLV_TYPE_FIXED, 7 }, - [GSM48_IE_GSM_LSA_ID] = { TLV_TYPE_TLV, 0 }, - }, -}; - -static const struct value_string gprs_pmm_state_names[] = { - { PMM_DETACHED, "PMM DETACH" }, - { PMM_CONNECTED, "PMM CONNECTED" }, - { PMM_IDLE, "PMM IDLE" }, - { MM_IDLE, "MM IDLE" }, - { MM_READY, "MM READY" }, - { MM_STANDBY, "MM STANDBY" }, - { 0, NULL } -}; - -static int gsm48_gmm_authorize(struct sgsn_mm_ctx *ctx); - -static void mmctx_change_gtpu_endpoints_to_sgsn(struct sgsn_mm_ctx *mm_ctx) -{ - struct sgsn_pdp_ctx *pdp; - llist_for_each_entry(pdp, &mm_ctx->pdp_list, list) { - sgsn_pdp_upd_gtp_u(pdp, - &sgsn->cfg.gtp_listenaddr.sin_addr, - sizeof(sgsn->cfg.gtp_listenaddr.sin_addr)); - } -} - -void mmctx_set_pmm_state(struct sgsn_mm_ctx *ctx, enum gprs_pmm_state state) -{ - if (ctx->ran_type != MM_CTX_T_UTRAN_Iu) - return; - - if (ctx->pmm_state == state) - return; - - LOGMMCTXP(LOGL_INFO, ctx, "Changing PMM state from %s to %s\n", - get_value_string(gprs_pmm_state_names, ctx->pmm_state), - get_value_string(gprs_pmm_state_names, state)); - - switch (state) { - case PMM_IDLE: - /* TODO: start RA Upd timer */ - mmctx_change_gtpu_endpoints_to_sgsn(ctx); - break; - case PMM_CONNECTED: - break; - default: - break; - } - - ctx->pmm_state = state; -} - -void mmctx_set_mm_state(struct sgsn_mm_ctx *ctx, enum gprs_pmm_state state) -{ - if (ctx->ran_type != MM_CTX_T_GERAN_Gb) - return; - - if (ctx->pmm_state == state) - return; - - LOGMMCTXP(LOGL_INFO, ctx, "Changing MM state from %s to %s\n", - get_value_string(gprs_pmm_state_names, ctx->pmm_state), - get_value_string(gprs_pmm_state_names, state)); - - ctx->pmm_state = state; -} - -#ifdef BUILD_IU -int sgsn_ranap_rab_ass_resp(struct sgsn_mm_ctx *ctx, RANAP_RAB_SetupOrModifiedItemIEs_t *setup_ies); -int sgsn_ranap_iu_event(struct ue_conn_ctx *ctx, enum iu_event_type type, void *data) -{ - struct sgsn_mm_ctx *mm; - int rc = -1; - - mm = sgsn_mm_ctx_by_ue_ctx(ctx); - if (!mm) { - LOGP(DRANAP, LOGL_NOTICE, "Cannot find mm ctx for IU event %i!\n", type); - return rc; - } - - switch (type) { - case IU_EVENT_RAB_ASSIGN: - rc = sgsn_ranap_rab_ass_resp(mm, (RANAP_RAB_SetupOrModifiedItemIEs_t *)data); - break; - case IU_EVENT_IU_RELEASE: - /* fall thru */ - case IU_EVENT_LINK_INVALIDATED: - /* Clean up ue_conn_ctx here */ - LOGMMCTXP(LOGL_INFO, mm, "IU release for imsi %s\n", mm->imsi); - if (mm->pmm_state == PMM_CONNECTED) - mmctx_set_pmm_state(mm, PMM_IDLE); - rc = 0; - break; - case IU_EVENT_SECURITY_MODE_COMPLETE: - /* Continue authentication here */ - mm->iu.ue_ctx->integrity_active = 1; - rc = gsm48_gmm_authorize(mm); - break; - default: - LOGP(DRANAP, LOGL_NOTICE, "Unknown event received: %i\n", type); - rc = -1; - break; - } - return rc; -} -#endif - - -/* Our implementation, should be kept in SGSN */ - -static void mmctx_timer_cb(void *_mm); - -static void mmctx_timer_start(struct sgsn_mm_ctx *mm, unsigned int T, - unsigned int seconds) -{ - if (osmo_timer_pending(&mm->timer)) - LOGMMCTXP(LOGL_ERROR, mm, "Starting MM timer %u while old " - "timer %u pending\n", T, mm->T); - mm->T = T; - mm->num_T_exp = 0; - - /* FIXME: we should do this only once ? */ - osmo_timer_setup(&mm->timer, mmctx_timer_cb, mm); - osmo_timer_schedule(&mm->timer, seconds, 0); -} - -static void mmctx_timer_stop(struct sgsn_mm_ctx *mm, unsigned int T) -{ - if (mm->T != T) - LOGMMCTXP(LOGL_ERROR, mm, "Stopping MM timer %u but " - "%u is running\n", T, mm->T); - osmo_timer_del(&mm->timer); -} - -time_t gprs_max_time_to_idle(void) -{ - return sgsn->cfg.timers.T3314 + (sgsn->cfg.timers.T3312 + 4 * 60); -} - -/* Send a message through the underlying layer. - * For param encryptable, see 3GPP TS 24.008 § 4.7.1.2 and - * gsm48_hdr_gmm_cipherable(). Pass false for not cipherable messages. */ -static int gsm48_gmm_sendmsg(struct msgb *msg, int command, - struct sgsn_mm_ctx *mm, bool encryptable) -{ - if (mm) { - rate_ctr_inc(&mm->ctrg->ctr[GMM_CTR_PKTS_SIG_OUT]); -#ifdef BUILD_IU - if (mm->ran_type == MM_CTX_T_UTRAN_Iu) - return iu_tx(msg, GPRS_SAPI_GMM); -#endif - } - -#ifdef BUILD_IU - /* In Iu mode, msg->dst contains the ue_conn_ctx pointer, in Gb mode - * dst is empty. */ - /* FIXME: have a more explicit indicator for Iu messages */ - if (msg->dst) - return iu_tx(msg, GPRS_SAPI_GMM); -#endif - - /* caller needs to provide TLLI, BVCI and NSEI */ - return gprs_llc_tx_ui(msg, GPRS_SAPI_GMM, command, mm, encryptable); -} - -/* copy identifiers from old message to new message, this - * is required so lower layers can route it correctly */ -static void gmm_copy_id(struct msgb *msg, const struct msgb *old) -{ - msgb_tlli(msg) = msgb_tlli(old); - msgb_bvci(msg) = msgb_bvci(old); - msgb_nsei(msg) = msgb_nsei(old); - msg->dst = old->dst; -} - -/* Store BVCI/NSEI in MM context */ -static void msgid2mmctx(struct sgsn_mm_ctx *mm, const struct msgb *msg) -{ - mm->gb.bvci = msgb_bvci(msg); - mm->gb.nsei = msgb_nsei(msg); - /* In case a Iu connection is reconnected we need to update the ue ctx */ - mm->iu.ue_ctx = msg->dst; -} - -/* Store BVCI/NSEI in MM context */ -static void mmctx2msgid(struct msgb *msg, const struct sgsn_mm_ctx *mm) -{ - msgb_tlli(msg) = mm->gb.tlli; - msgb_bvci(msg) = mm->gb.bvci; - msgb_nsei(msg) = mm->gb.nsei; - msg->dst = mm->iu.ue_ctx; -} - -static void mm_ctx_cleanup_free(struct sgsn_mm_ctx *ctx, const char *log_text) -{ - LOGMMCTXP(LOGL_INFO, ctx, "Cleaning MM context due to %s\n", log_text); - - /* Mark MM state as deregistered */ - ctx->gmm_state = GMM_DEREGISTERED; - mmctx_set_pmm_state(ctx, PMM_DETACHED); - mmctx_set_pmm_state(ctx, MM_IDLE); - - sgsn_mm_ctx_cleanup_free(ctx); -} - -/* Chapter 9.4.18 */ -static int _tx_status(struct msgb *msg, uint8_t cause, - struct sgsn_mm_ctx *mmctx, int sm) -{ - struct gsm48_hdr *gh; - - /* MMCTX might be NULL! */ - - DEBUGP(DMM, "<- GPRS MM STATUS (cause: %s)\n", - get_value_string(gsm48_gmm_cause_names, cause)); - - gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh) + 1); - if (sm) { - gh->proto_discr = GSM48_PDISC_SM_GPRS; - gh->msg_type = GSM48_MT_GSM_STATUS; - } else { - gh->proto_discr = GSM48_PDISC_MM_GPRS; - gh->msg_type = GSM48_MT_GMM_STATUS; - } - gh->data[0] = cause; - - return gsm48_gmm_sendmsg(msg, 0, mmctx, true); -} - -static int gsm48_tx_gmm_status(struct sgsn_mm_ctx *mmctx, uint8_t cause) -{ - struct msgb *msg = gsm48_msgb_alloc_name("GSM 04.08 GMM STATUS"); - - mmctx2msgid(msg, mmctx); - return _tx_status(msg, cause, mmctx, 0); -} - -static int gsm48_tx_sm_status(struct sgsn_mm_ctx *mmctx, uint8_t cause) -{ - struct msgb *msg = gsm48_msgb_alloc_name("GSM 04.08 SM STATUS"); - - mmctx2msgid(msg, mmctx); - return _tx_status(msg, cause, mmctx, 1); -} - -static int _tx_detach_req(struct msgb *msg, uint8_t detach_type, uint8_t cause, - struct sgsn_mm_ctx *mmctx) -{ - struct gsm48_hdr *gh; - - /* MMCTX might be NULL! */ - - DEBUGP(DMM, "<- GPRS MM DETACH REQ (type: %s, cause: %s)\n", - get_value_string(gprs_det_t_mt_strs, detach_type), - get_value_string(gsm48_gmm_cause_names, cause)); - - gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh) + 1); - - gh->proto_discr = GSM48_PDISC_MM_GPRS; - gh->msg_type = GSM48_MT_GMM_DETACH_REQ; - gh->data[0] = detach_type & 0x07; - - msgb_tv_put(msg, GSM48_IE_GMM_CAUSE, cause); - - return gsm48_gmm_sendmsg(msg, 0, mmctx, true); -} - -static int gsm48_tx_gmm_detach_req(struct sgsn_mm_ctx *mmctx, - uint8_t detach_type, uint8_t cause) -{ - struct msgb *msg = gsm48_msgb_alloc_name("GSM 04.08 DET REQ"); - - mmctx2msgid(msg, mmctx); - return _tx_detach_req(msg, detach_type, cause, mmctx); -} - -static int gsm48_tx_gmm_detach_req_oldmsg(struct msgb *oldmsg, - uint8_t detach_type, uint8_t cause) -{ - struct msgb *msg = gsm48_msgb_alloc_name("GSM 04.08 DET OLD"); - - gmm_copy_id(msg, oldmsg); - return _tx_detach_req(msg, detach_type, cause, NULL); -} - -static struct gsm48_qos default_qos = { - .delay_class = 4, /* best effort */ - .reliab_class = GSM48_QOS_RC_LLC_UN_RLC_ACK_DATA_PROT, - .peak_tput = GSM48_QOS_PEAK_TPUT_32000bps, - .preced_class = GSM48_QOS_PC_NORMAL, - .mean_tput = GSM48_QOS_MEAN_TPUT_BEST_EFFORT, - .traf_class = GSM48_QOS_TC_INTERACTIVE, - .deliv_order = GSM48_QOS_DO_UNORDERED, - .deliv_err_sdu = GSM48_QOS_ERRSDU_YES, - .max_sdu_size = GSM48_QOS_MAXSDU_1520, - .max_bitrate_up = GSM48_QOS_MBRATE_63k, - .max_bitrate_down = GSM48_QOS_MBRATE_63k, - .resid_ber = GSM48_QOS_RBER_5e_2, - .sdu_err_ratio = GSM48_QOS_SERR_1e_2, - .handling_prio = 3, - .xfer_delay = 0x10, /* 200ms */ - .guar_bitrate_up = GSM48_QOS_MBRATE_0k, - .guar_bitrate_down = GSM48_QOS_MBRATE_0k, - .sig_ind = 0, /* not optimised for signalling */ - .max_bitrate_down_ext = 0, /* use octet 9 */ - .guar_bitrate_down_ext = 0, /* use octet 13 */ -}; - -/* Chapter 9.4.2: Attach accept */ -static int gsm48_tx_gmm_att_ack(struct sgsn_mm_ctx *mm) -{ - struct msgb *msg = gsm48_msgb_alloc_name("GSM 04.08 ATT ACK"); - struct gsm48_hdr *gh; - struct gsm48_attach_ack *aa; - uint8_t *mid; -#if 0 - uint8_t *ptsig; -#endif - - LOGMMCTXP(LOGL_INFO, mm, "<- GPRS ATTACH ACCEPT (new P-TMSI=0x%08x)\n", mm->p_tmsi); - rate_ctr_inc(&sgsn->rate_ctrs->ctr[CTR_GPRS_ATTACH_ACKED]); - - mmctx2msgid(msg, mm); - - gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh)); - gh->proto_discr = GSM48_PDISC_MM_GPRS; - gh->msg_type = GSM48_MT_GMM_ATTACH_ACK; - - aa = (struct gsm48_attach_ack *) msgb_put(msg, sizeof(*aa)); - aa->force_stby = 0; /* not indicated */ - aa->att_result = 1; /* GPRS only */ - aa->ra_upd_timer = gprs_secs_to_tmr_floor(sgsn->cfg.timers.T3312); - aa->radio_prio = 4; /* lowest */ - gsm48_construct_ra(aa->ra_id.digits, &mm->ra); - -#if 0 - /* Optional: P-TMSI signature */ - msgb_v_put(msg, GSM48_IE_GMM_PTMSI_SIG); - ptsig = msgb_put(msg, 3); - ptsig[0] = mm->p_tmsi_sig >> 16; - ptsig[1] = mm->p_tmsi_sig >> 8; - ptsig[2] = mm->p_tmsi_sig & 0xff; - -#endif - /* Optional: Negotiated Ready timer value - * (fixed 44s, default value, GSM 04.08, table 11.4a) to safely limit - * the inactivity time READY->STANDBY. - */ - msgb_tv_put(msg, GSM48_IE_GMM_TIMER_READY, - gprs_secs_to_tmr_floor(sgsn->cfg.timers.T3314)); - -#ifdef PTMSI_ALLOC - /* Optional: Allocated P-TMSI */ - mid = msgb_put(msg, GSM48_MID_TMSI_LEN); - gsm48_generate_mid_from_tmsi(mid, mm->p_tmsi); - mid[0] = GSM48_IE_GMM_ALLOC_PTMSI; -#endif - - /* Optional: MS-identity (combined attach) */ - /* Optional: GMM cause (partial attach result for combined attach) */ - - return gsm48_gmm_sendmsg(msg, 0, mm, true); -} - -/* Chapter 9.4.5: Attach reject */ -static int _tx_gmm_att_rej(struct msgb *msg, uint8_t gmm_cause, - const struct sgsn_mm_ctx *mm) -{ - struct gsm48_hdr *gh; - - LOGMMCTXP(LOGL_NOTICE, mm, "<- GPRS ATTACH REJECT: %s\n", - get_value_string(gsm48_gmm_cause_names, gmm_cause)); - rate_ctr_inc(&sgsn->rate_ctrs->ctr[CTR_GPRS_ATTACH_REJECTED]); - - gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh) + 1); - gh->proto_discr = GSM48_PDISC_MM_GPRS; - gh->msg_type = GSM48_MT_GMM_ATTACH_REJ; - gh->data[0] = gmm_cause; - - return gsm48_gmm_sendmsg(msg, 0, NULL, false); -} -static int gsm48_tx_gmm_att_rej_oldmsg(const struct msgb *old_msg, - uint8_t gmm_cause) -{ - struct msgb *msg = gsm48_msgb_alloc_name("GSM 04.08 ATT REJ OLD"); - gmm_copy_id(msg, old_msg); - return _tx_gmm_att_rej(msg, gmm_cause, NULL); -} -static int gsm48_tx_gmm_att_rej(struct sgsn_mm_ctx *mm, - uint8_t gmm_cause) -{ - struct msgb *msg = gsm48_msgb_alloc_name("GSM 04.08 ATT REJ"); - mmctx2msgid(msg, mm); - return _tx_gmm_att_rej(msg, gmm_cause, mm); -} - -/* Chapter 9.4.6.2 Detach accept */ -static int _tx_detach_ack(struct msgb *msg, uint8_t force_stby, - struct sgsn_mm_ctx *mm) -{ - struct gsm48_hdr *gh; - - /* MMCTX might be NULL! */ - - DEBUGP(DMM, "<- GPRS MM DETACH ACC (force-standby: %d)\n", force_stby); - rate_ctr_inc(&sgsn->rate_ctrs->ctr[CTR_GPRS_DETACH_ACKED]); - - gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh) + 1); - gh->proto_discr = GSM48_PDISC_MM_GPRS; - gh->msg_type = GSM48_MT_GMM_DETACH_ACK; - gh->data[0] = force_stby; - - return gsm48_gmm_sendmsg(msg, 0, mm, true); -} - -static int gsm48_tx_gmm_det_ack(struct sgsn_mm_ctx *mm, uint8_t force_stby) -{ - struct msgb *msg = gsm48_msgb_alloc_name("GSM 04.08 DET ACK"); - - mmctx2msgid(msg, mm); - return _tx_detach_ack(msg, force_stby, mm); -} - -static int gsm48_tx_gmm_det_ack_oldmsg(struct msgb *oldmsg, uint8_t force_stby) -{ - struct msgb *msg = gsm48_msgb_alloc_name("GSM 04.08 DET ACK OLD"); - - gmm_copy_id(msg, oldmsg); - return _tx_detach_ack(msg, force_stby, NULL); -} - -/* Transmit Chapter 9.4.12 Identity Request */ -static int gsm48_tx_gmm_id_req(struct sgsn_mm_ctx *mm, uint8_t id_type) -{ - struct msgb *msg = gsm48_msgb_alloc_name("GSM 04.08 ID REQ"); - struct gsm48_hdr *gh; - - LOGMMCTXP(LOGL_DEBUG, mm, "<- GPRS IDENTITY REQUEST: mi_type=%s\n", - gsm48_mi_type_name(id_type)); - - mmctx2msgid(msg, mm); - - gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh) + 1); - gh->proto_discr = GSM48_PDISC_MM_GPRS; - gh->msg_type = GSM48_MT_GMM_ID_REQ; - /* 10.5.5.9 ID type 2 + identity type and 10.5.5.7 'force to standby' IE */ - gh->data[0] = id_type & 0xf; - - return gsm48_gmm_sendmsg(msg, 1, mm, false); -} - -/* determine if the MS/UE supports R99 or later */ -static bool mmctx_is_r99(const struct sgsn_mm_ctx *mm) -{ - if (mm->ms_network_capa.len < 1) - return false; - if (mm->ms_network_capa.buf[0] & 0x01) - return true; - return false; -} - -/* 3GPP TS 24.008 Section 9.4.9: Authentication and Ciphering Request */ -static int gsm48_tx_gmm_auth_ciph_req(struct sgsn_mm_ctx *mm, - const struct osmo_auth_vector *vec, - uint8_t key_seq, bool force_standby) -{ - struct msgb *msg = gsm48_msgb_alloc_name("GSM 04.08 AUTH CIPH REQ"); - struct gsm48_hdr *gh; - struct gsm48_auth_ciph_req *acreq; - uint8_t *m_rand, *m_cksn, rbyte; - - LOGMMCTXP(LOGL_INFO, mm, "<- GPRS AUTH AND CIPHERING REQ (rand = %s", - osmo_hexdump(vec->rand, sizeof(vec->rand))); - if (mmctx_is_r99(mm) && vec - && (vec->auth_types & OSMO_AUTH_TYPE_UMTS)) { - LOGPC(DMM, LOGL_INFO, ", autn = %s)\n", - osmo_hexdump(vec->autn, sizeof(vec->autn))); - } else - LOGPC(DMM, LOGL_INFO, ")\n"); - - mmctx2msgid(msg, mm); - - gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh)); - gh->proto_discr = GSM48_PDISC_MM_GPRS; - gh->msg_type = GSM48_MT_GMM_AUTH_CIPH_REQ; - - acreq = (struct gsm48_auth_ciph_req *) msgb_put(msg, sizeof(*acreq)); - acreq->ciph_alg = mm->ciph_algo & 0xf; - /* § 10.5.5.10: */ - acreq->imeisv_req = 0x1; - /* § 10.5.5.7: */ - acreq->force_stby = force_standby; - /* 3GPP TS 24.008 § 10.5.5.19: */ - if (RAND_bytes(&rbyte, 1) != 1) { - LOGP(DMM, LOGL_NOTICE, "RAND_bytes failed for A&C ref, falling " - "back to rand()\n"); - acreq->ac_ref_nr = rand(); - } else - acreq->ac_ref_nr = rbyte; - mm->ac_ref_nr_used = acreq->ac_ref_nr; - - /* Only if authentication is requested we need to set RAND + CKSN */ - if (vec) { - m_rand = msgb_put(msg, sizeof(vec->rand) + 1); - m_rand[0] = GSM48_IE_GMM_AUTH_RAND; - memcpy(m_rand + 1, vec->rand, sizeof(vec->rand)); - - /* § 10.5.1.2: */ - m_cksn = msgb_put(msg, 1); - m_cksn[0] = (GSM48_IE_GMM_CIPH_CKSN << 4) | (key_seq & 0x07); - - /* A Release99 or higher MS/UE must be able to handle - * the optional AUTN IE. If a classic GSM SIM is - * inserted, it will simply ignore AUTN and just use - * RAND */ - if (mmctx_is_r99(mm) && - (vec->auth_types & OSMO_AUTH_TYPE_UMTS)) { - msgb_tlv_put(msg, GSM48_IE_GMM_AUTN, - sizeof(vec->autn), vec->autn); - } - } - - return gsm48_gmm_sendmsg(msg, 1, mm, false); -} - -/* Section 9.4.11: Authentication and Ciphering Reject */ -static int gsm48_tx_gmm_auth_ciph_rej(struct sgsn_mm_ctx *mm) -{ - struct msgb *msg = gsm48_msgb_alloc_name("GSM 04.08 AUTH CIPH REJ"); - struct gsm48_hdr *gh; - - LOGMMCTXP(LOGL_NOTICE, mm, "<- GPRS AUTH AND CIPH REJECT\n"); - - mmctx2msgid(msg, mm); - - gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh)); - gh->proto_discr = GSM48_PDISC_MM_GPRS; - gh->msg_type = GSM48_MT_GMM_AUTH_CIPH_REJ; - - return gsm48_gmm_sendmsg(msg, 0, mm, false); -} - -/* check if the received authentication response matches */ -static bool check_auth_resp(struct sgsn_mm_ctx *ctx, - bool is_utran, - const struct osmo_auth_vector *vec, - const uint8_t *res, uint8_t res_len) -{ - const uint8_t *expect_res; - uint8_t expect_res_len; - enum osmo_sub_auth_type expect_type; - const char *expect_str; - - if (!vec) - return true; /* really!? */ - - /* On UTRAN (3G) we always expect UMTS AKA. On GERAN (2G) we sent AUTN - * and expect UMTS AKA if there is R99 capability and our vector - * supports UMTS AKA, otherwise we expect GSM AKA. */ - if (is_utran - || (mmctx_is_r99(ctx) && (vec->auth_types & OSMO_AUTH_TYPE_UMTS))) { - expect_type = OSMO_AUTH_TYPE_UMTS; - expect_str = "UMTS RES"; - expect_res = vec->res; - expect_res_len = vec->res_len; - } else { - expect_type = OSMO_AUTH_TYPE_GSM; - expect_str = "GSM SRES"; - expect_res = vec->sres; - expect_res_len = sizeof(vec->sres); - } - - if (!(vec->auth_types & expect_type)) { - LOGMMCTXP(LOGL_ERROR, ctx, "Auth error: auth vector does" - " not provide the expected auth type:" - " expected %s = 0x%x, auth_types are 0x%x\n", - expect_str, expect_type, vec->auth_types); - return false; - } - - if (!res) - goto auth_mismatch; - - if (res_len != expect_res_len) - goto auth_mismatch; - - if (memcmp(res, expect_res, res_len) != 0) - goto auth_mismatch; - - /* Authorized! */ - return true; - -auth_mismatch: - LOGMMCTXP(LOGL_ERROR, ctx, "Auth mismatch: expected %s = %s\n", - expect_str, osmo_hexdump_nospc(expect_res, expect_res_len)); - return false; -} - -/* Section 9.4.10: Authentication and Ciphering Response */ -static int gsm48_rx_gmm_auth_ciph_resp(struct sgsn_mm_ctx *ctx, - struct msgb *msg) -{ - struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_gmmh(msg); - struct gsm48_auth_ciph_resp *acr = (struct gsm48_auth_ciph_resp *)gh->data; - struct tlv_parsed tp; - struct gsm_auth_tuple *at; - const char *res_name = "(no response)"; - uint8_t res[16]; - uint8_t res_len; - int rc; - - LOGMMCTXP(LOGL_INFO, ctx, "-> GPRS AUTH AND CIPH RESPONSE\n"); - - if (ctx->auth_triplet.key_seq == GSM_KEY_SEQ_INVAL) { - LOGMMCTXP(LOGL_NOTICE, ctx, - "Unexpected Auth & Ciph Response (ignored)\n"); - return 0; - } - - if (acr->ac_ref_nr != ctx->ac_ref_nr_used) { - LOGMMCTXP(LOGL_NOTICE, ctx, "Reference mismatch for Auth & Ciph" - " Response: %u received, %u expected\n", - acr->ac_ref_nr, ctx->ac_ref_nr_used); - return 0; - } - - /* Stop T3360 */ - mmctx_timer_stop(ctx, 3360); - - tlv_parse(&tp, &gsm48_gmm_att_tlvdef, acr->data, - (msg->data + msg->len) - acr->data, 0, 0); - - if (!TLVP_PRESENT(&tp, GSM48_IE_GMM_AUTH_SRES) || - !TLVP_PRESENT(&tp, GSM48_IE_GMM_IMEISV) || - TLVP_LEN(&tp,GSM48_IE_GMM_AUTH_SRES) != 4) { - /* TODO: missing mandatory IE, return STATUS or REJ? */ - LOGMMCTXP(LOGL_ERROR, ctx, "Missing mandantory IE\n"); - return -EINVAL; - } - - /* Start with the good old 4-byte SRES */ - memcpy(res, TLVP_VAL(&tp, GSM48_IE_GMM_AUTH_SRES), 4); - res_len = 4; - res_name = "GSM SRES"; - - /* Append extended RES as part of UMTS AKA, if any */ - if (TLVP_PRESENT(&tp, GSM48_IE_GMM_AUTH_RES_EXT)) { - unsigned int l = TLVP_LEN(&tp, GSM48_IE_GMM_AUTH_RES_EXT); - if (l > sizeof(res)-4) - l = sizeof(res)-4; - memcpy(res+4, TLVP_VAL(&tp, GSM48_IE_GMM_AUTH_RES_EXT), l); - res_len += l; - res_name = "UMTS RES"; - } - - at = &ctx->auth_triplet; - - LOGMMCTXP(LOGL_DEBUG, ctx, "checking auth: received %s = %s\n", - res_name, osmo_hexdump(res, res_len)); - rc = check_auth_resp(ctx, false, &at->vec, res, res_len); - if (!rc) { - rc = gsm48_tx_gmm_auth_ciph_rej(ctx); - mm_ctx_cleanup_free(ctx, "GPRS AUTH AND CIPH REJECT"); - return rc; - } - - ctx->is_authenticated = 1; - - if (ctx->ran_type == MM_CTX_T_UTRAN_Iu) - ctx->iu.new_key = 1; - - /* FIXME: enable LLC cipheirng */ - - /* Check if we can let the mobile station enter */ - return gsm48_gmm_authorize(ctx); -} - -/* Section 9.4.10: Authentication and Ciphering Failure */ -static int gsm48_rx_gmm_auth_ciph_fail(struct sgsn_mm_ctx *ctx, - struct msgb *msg) -{ - struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_gmmh(msg); - struct tlv_parsed tp; - const uint8_t gmm_cause = gh->data[0]; - const uint8_t *auts; - int rc; - - LOGMMCTXP(LOGL_INFO, ctx, "-> GPRS AUTH AND CIPH FAILURE (cause = %s)\n", - get_value_string(gsm48_gmm_cause_names, gmm_cause)); - - tlv_parse(&tp, &gsm48_gmm_att_tlvdef, gh->data+1, msg->len - 1, 0, 0); - - /* Only if GMM cause is present and the AUTS is provided, we can - * start re-sync procedure */ - if (gmm_cause == GMM_CAUSE_SYNC_FAIL && - TLVP_PRESENT(&tp, GSM48_IE_GMM_AUTH_FAIL_PAR)) { - if (TLVP_LEN(&tp, GSM48_IE_GMM_AUTH_FAIL_PAR) != 14) { - LOGMMCTXP(LOGL_ERROR, ctx, "AUTS IE has wrong size:" - " expected %d, got %u\n", 14, - TLVP_LEN(&tp, GSM48_IE_GMM_AUTH_FAIL_PAR)); - return -EINVAL; - } - auts = TLVP_VAL(&tp, GSM48_IE_GMM_AUTH_FAIL_PAR); - - LOGMMCTXP(LOGL_INFO, ctx, - "R99 AUTHENTICATION SYNCH (AUTS = %s)\n", - osmo_hexdump_nospc(auts, 14)); - - /* make sure we'll refresh the auth_triplet in - * sgsn_auth_update() */ - ctx->auth_triplet.key_seq = GSM_KEY_SEQ_INVAL; - - /* make sure we'll retry authentication after the resync */ - ctx->auth_state = SGSN_AUTH_UMTS_RESYNC; - - /* Send AUTS to HLR and wait for new Auth Info Result */ - rc = gprs_subscr_request_auth_info(ctx, auts, - ctx->auth_triplet.vec.rand); - if (!rc) - return 0; - /* on error, fall through to send a reject */ - LOGMMCTXP(LOGL_ERROR, ctx, - "Sending AUTS to HLR failed (rc = %d)\n", rc); - } - - LOGMMCTXP(LOGL_NOTICE, ctx, "Authentication failed\n"); - rc = gsm48_tx_gmm_auth_ciph_rej(ctx); - mm_ctx_cleanup_free(ctx, "GPRS AUTH FAILURE"); - return rc; -} - -static void extract_subscr_msisdn(struct sgsn_mm_ctx *ctx) -{ - struct gsm_mncc_number called; - uint8_t msisdn[sizeof(ctx->subscr->sgsn_data->msisdn) + 1]; - - /* Convert MSISDN from encoded to string.. */ - if (!ctx->subscr) - return; - - if (ctx->subscr->sgsn_data->msisdn_len < 1) - return; - - /* prepare the data for the decoder */ - memset(&called, 0, sizeof(called)); - msisdn[0] = ctx->subscr->sgsn_data->msisdn_len; - memcpy(&msisdn[1], ctx->subscr->sgsn_data->msisdn, - ctx->subscr->sgsn_data->msisdn_len); - - /* decode the string now */ - gsm48_decode_called(&called, msisdn); - - /* Prepend a '+' for international numbers */ - if (called.plan == 1 && called.type == 1) { - ctx->msisdn[0] = '+'; - osmo_strlcpy(&ctx->msisdn[1], called.number, - sizeof(ctx->msisdn)); - } else { - osmo_strlcpy(ctx->msisdn, called.number, sizeof(ctx->msisdn)); - } -} - -static void extract_subscr_hlr(struct sgsn_mm_ctx *ctx) -{ - struct gsm_mncc_number called; - uint8_t hlr_number[sizeof(ctx->subscr->sgsn_data->hlr) + 1]; - - if (!ctx->subscr) - return; - - if (ctx->subscr->sgsn_data->hlr_len < 1) - return; - - /* prepare the data for the decoder */ - memset(&called, 0, sizeof(called)); - hlr_number[0] = ctx->subscr->sgsn_data->hlr_len; - memcpy(&hlr_number[1], ctx->subscr->sgsn_data->hlr, - ctx->subscr->sgsn_data->hlr_len); - - /* decode the string now */ - gsm48_decode_called(&called, hlr_number); - - if (called.plan != 1) { - LOGMMCTXP(LOGL_ERROR, ctx, - "Numbering plan(%d) not allowed\n", - called.plan); - return; - } - - if (called.type != 1) { - LOGMMCTXP(LOGL_ERROR, ctx, - "Numbering type(%d) not allowed\n", - called.type); - return; - } - - osmo_strlcpy(ctx->hlr, called.number, sizeof(ctx->hlr)); -} - -#ifdef BUILD_IU -/* Chapter 9.4.21: Service accept */ -static int gsm48_tx_gmm_service_ack(struct sgsn_mm_ctx *mm) -{ - struct msgb *msg = gsm48_msgb_alloc_name("GSM 04.08 SERVICE ACK"); - struct gsm48_hdr *gh; - - LOGMMCTXP(LOGL_INFO, mm, "<- GPRS SERVICE ACCEPT (P-TMSI=0x%08x)\n", mm->p_tmsi); - - mmctx2msgid(msg, mm); - - gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh)); - gh->proto_discr = GSM48_PDISC_MM_GPRS; - gh->msg_type = GSM48_MT_GMM_SERVICE_ACK; - - /* Optional: PDP context status */ - /* Optional: MBMS context status */ - - return gsm48_gmm_sendmsg(msg, 0, mm, false); -} -#endif - -/* Chapter 9.4.22: Service reject */ -static int _tx_gmm_service_rej(struct msgb *msg, uint8_t gmm_cause, - const struct sgsn_mm_ctx *mm) -{ - struct gsm48_hdr *gh; - - LOGMMCTXP(LOGL_NOTICE, mm, "<- GPRS SERVICE REJECT: %s\n", - get_value_string(gsm48_gmm_cause_names, gmm_cause)); - - gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh) + 1); - gh->proto_discr = GSM48_PDISC_MM_GPRS; - gh->msg_type = GSM48_MT_GMM_SERVICE_REJ; - gh->data[0] = gmm_cause; - - return gsm48_gmm_sendmsg(msg, 0, NULL, true); -} -static int gsm48_tx_gmm_service_rej_oldmsg(const struct msgb *old_msg, - uint8_t gmm_cause) -{ - struct msgb *msg = gsm48_msgb_alloc_name("GSM 04.08 SERVICE REJ OLD"); - gmm_copy_id(msg, old_msg); - return _tx_gmm_service_rej(msg, gmm_cause, NULL); -} -#if 0 --- currently unused -- -static int gsm48_tx_gmm_service_rej(struct sgsn_mm_ctx *mm, - uint8_t gmm_cause) -{ - struct msgb *msg = gsm48_msgb_alloc_name("GSM 04.08 SERVICE REJ"); - mmctx2msgid(msg, mm); - return _tx_gmm_service_rej(msg, gmm_cause, mm); -} -#endif - -static int gsm48_tx_gmm_ra_upd_ack(struct sgsn_mm_ctx *mm); - -#ifdef BUILD_IU -void activate_pdp_rabs(struct sgsn_mm_ctx *ctx) -{ - /* Send RAB activation requests for all PDP contexts */ - struct sgsn_pdp_ctx *pdp; - llist_for_each_entry(pdp, &ctx->pdp_list, list) { - iu_rab_act_ps(pdp->nsapi, pdp, 1); - } -} -#endif - -/* Check if we can already authorize a subscriber */ -static int gsm48_gmm_authorize(struct sgsn_mm_ctx *ctx) -{ -#ifdef BUILD_IU - int rc; -#endif -#ifndef PTMSI_ALLOC - struct sgsn_signal_data sig_data; -#endif - - /* Request IMSI and IMEI from the MS if they are unknown */ - if (!strlen(ctx->imei)) { - ctx->t3370_id_type = GSM_MI_TYPE_IMEI; - mmctx_timer_start(ctx, 3370, sgsn->cfg.timers.T3370); - return gsm48_tx_gmm_id_req(ctx, GSM_MI_TYPE_IMEI); - } - if (!strlen(ctx->imsi)) { - ctx->t3370_id_type = GSM_MI_TYPE_IMSI; - mmctx_timer_start(ctx, 3370, sgsn->cfg.timers.T3370); - return gsm48_tx_gmm_id_req(ctx, GSM_MI_TYPE_IMSI); - } - - /* All information required for authentication is available */ - ctx->t3370_id_type = GSM_MI_TYPE_NONE; - - if (ctx->auth_state == SGSN_AUTH_UNKNOWN) { - /* Request authorization, this leads to a call to - * sgsn_auth_update which in turn calls - * gsm0408_gprs_access_granted or gsm0408_gprs_access_denied */ - - sgsn_auth_request(ctx); - /* Note that gsm48_gmm_authorize can be called recursively via - * sgsn_auth_request iff ctx->auth_info changes to AUTH_ACCEPTED - */ - return 0; - } - - if (ctx->auth_state == SGSN_AUTH_AUTHENTICATE && !ctx->is_authenticated) { - struct gsm_auth_tuple *at = &ctx->auth_triplet; - - mmctx_timer_start(ctx, 3360, sgsn->cfg.timers.T3360); - return gsm48_tx_gmm_auth_ciph_req(ctx, &at->vec, at->key_seq, - false); - } - - if (ctx->auth_state == SGSN_AUTH_AUTHENTICATE && ctx->is_authenticated && - ctx->auth_triplet.key_seq != GSM_KEY_SEQ_INVAL) { - /* Check again for authorization */ - sgsn_auth_request(ctx); - return 0; - } - - if (ctx->auth_state != SGSN_AUTH_ACCEPTED) { - LOGMMCTXP(LOGL_NOTICE, ctx, - "authorization is denied, aborting procedure\n"); - return -EACCES; - } - - /* The MS is authorized */ -#ifdef BUILD_IU - if (ctx->ran_type == MM_CTX_T_UTRAN_Iu && !ctx->iu.ue_ctx->integrity_active) { - rc = iu_tx_sec_mode_cmd(ctx->iu.ue_ctx, &ctx->auth_triplet, 0, ctx->iu.new_key); - ctx->iu.new_key = 0; - return rc; - } -#endif - - switch (ctx->pending_req) { - case 0: - LOGMMCTXP(LOGL_INFO, ctx, - "no pending request, authorization completed\n"); - break; - case GSM48_MT_GMM_ATTACH_REQ: - ctx->pending_req = 0; - - extract_subscr_msisdn(ctx); - extract_subscr_hlr(ctx); -#ifdef PTMSI_ALLOC - /* Start T3350 and re-transmit up to 5 times until ATTACH COMPLETE */ - mmctx_timer_start(ctx, 3350, sgsn->cfg.timers.T3350); - ctx->t3350_mode = GMM_T3350_MODE_ATT; -#else - memset(&sig_data, 0, sizeof(sig_data)); - sig_data.mm = mmctx; - osmo_signal_dispatch(SS_SGSN, S_SGSN_ATTACH, &sig_data); - ctx->gmm_state = GMM_REGISTERED_NORMAL; -#endif - - return gsm48_tx_gmm_att_ack(ctx); -#ifdef BUILD_IU - case GSM48_MT_GMM_SERVICE_REQ: - ctx->pending_req = 0; - mmctx_set_pmm_state(ctx, PMM_CONNECTED); - rc = gsm48_tx_gmm_service_ack(ctx); - - if (ctx->iu.service.type != GPRS_SERVICE_T_SIGNALLING) - activate_pdp_rabs(ctx); - - return rc; -#endif - case GSM48_MT_GMM_RA_UPD_REQ: - ctx->pending_req = 0; - /* Send RA UPDATE ACCEPT */ - return gsm48_tx_gmm_ra_upd_ack(ctx); - - default: - LOGMMCTXP(LOGL_ERROR, ctx, - "only Attach Request is supported yet, " - "got request type %u\n", ctx->pending_req); - break; - } - - return 0; -} - -void gsm0408_gprs_authenticate(struct sgsn_mm_ctx *ctx) -{ - ctx->is_authenticated = 0; - - gsm48_gmm_authorize(ctx); -} - -void gsm0408_gprs_access_granted(struct sgsn_mm_ctx *ctx) -{ - switch (ctx->gmm_state) { - case GMM_COMMON_PROC_INIT: - LOGMMCTXP(LOGL_NOTICE, ctx, - "Authorized, continuing procedure, IMSI=%s\n", - ctx->imsi); - /* Continue with the authorization */ - gsm48_gmm_authorize(ctx); - break; - default: - LOGMMCTXP(LOGL_INFO, ctx, - "Authorized, ignored, IMSI=%s\n", - ctx->imsi); - } -} - -void gsm0408_gprs_access_denied(struct sgsn_mm_ctx *ctx, int gmm_cause) -{ - if (gmm_cause == SGSN_ERROR_CAUSE_NONE) - gmm_cause = GMM_CAUSE_GPRS_NOTALLOWED; - - switch (ctx->gmm_state) { - case GMM_COMMON_PROC_INIT: - LOGMMCTXP(LOGL_NOTICE, ctx, - "Not authorized, rejecting ATTACH REQUEST " - "with cause '%s' (%d)\n", - get_value_string(gsm48_gmm_cause_names, gmm_cause), - gmm_cause); - gsm48_tx_gmm_att_rej(ctx, gmm_cause); - mm_ctx_cleanup_free(ctx, "GPRS ATTACH REJECT"); - break; - case GMM_REGISTERED_NORMAL: - case GMM_REGISTERED_SUSPENDED: - LOGMMCTXP(LOGL_NOTICE, ctx, - "Authorization lost, detaching " - "with cause '%s' (%d)\n", - get_value_string(gsm48_gmm_cause_names, gmm_cause), - gmm_cause); - gsm48_tx_gmm_detach_req( - ctx, GPRS_DET_T_MT_REATT_NOTREQ, gmm_cause); - - mm_ctx_cleanup_free(ctx, "auth lost"); - break; - default: - LOGMMCTXP(LOGL_INFO, ctx, - "Authorization lost, cause is '%s' (%d)\n", - get_value_string(gsm48_gmm_cause_names, gmm_cause), - gmm_cause); - mm_ctx_cleanup_free(ctx, "auth lost"); - } -} - -void gsm0408_gprs_access_cancelled(struct sgsn_mm_ctx *ctx, int gmm_cause) -{ - if (gmm_cause != SGSN_ERROR_CAUSE_NONE) { - LOGMMCTXP(LOGL_INFO, ctx, - "Cancelled with cause '%s' (%d), deleting context\n", - get_value_string(gsm48_gmm_cause_names, gmm_cause), - gmm_cause); - gsm0408_gprs_access_denied(ctx, gmm_cause); - return; - } - - LOGMMCTXP(LOGL_INFO, ctx, "Cancelled, deleting context silently\n"); - mm_ctx_cleanup_free(ctx, "access cancelled"); -} - -/* Parse Chapter 9.4.13 Identity Response */ -static int gsm48_rx_gmm_id_resp(struct sgsn_mm_ctx *ctx, struct msgb *msg) -{ - struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_gmmh(msg); - uint8_t mi_type = gh->data[1] & GSM_MI_TYPE_MASK; - char mi_string[GSM48_MI_SIZE]; - - gsm48_mi_to_string(mi_string, sizeof(mi_string), &gh->data[1], gh->data[0]); - if (!ctx) { - DEBUGP(DMM, "from unknown TLLI 0x%08x?!? This should not happen\n", msgb_tlli(msg)); - return -EINVAL; - } - - LOGMMCTXP(LOGL_DEBUG, ctx, "-> GMM IDENTITY RESPONSE: MI(%s)=%s\n", - gsm48_mi_type_name(mi_type), mi_string); - - if (ctx->t3370_id_type == GSM_MI_TYPE_NONE) { - LOGMMCTXP(LOGL_NOTICE, ctx, - "Got unexpected IDENTITY RESPONSE: MI(%s)=%s, " - "ignoring message\n", - gsm48_mi_type_name(mi_type), mi_string); - return -EINVAL; - } - - if (mi_type == ctx->t3370_id_type) - mmctx_timer_stop(ctx, 3370); - - switch (mi_type) { - case GSM_MI_TYPE_IMSI: - /* we already have a mm context with current TLLI, but no - * P-TMSI / IMSI yet. What we now need to do is to fill - * this initial context with data from the HLR */ - if (strlen(ctx->imsi) == 0) { - /* Check if we already have a MM context for this IMSI */ - struct sgsn_mm_ctx *ictx; - ictx = sgsn_mm_ctx_by_imsi(mi_string); - if (ictx) { - /* Handle it like in gsm48_rx_gmm_det_req, - * except that no messages are sent to the BSS */ - - LOGMMCTXP(LOGL_NOTICE, ctx, "Deleting old MM Context for same IMSI " - "p_tmsi_old=0x%08x\n", - ictx->p_tmsi); - - mm_ctx_cleanup_free(ictx, "GPRS IMSI re-use"); - } - } - osmo_strlcpy(ctx->imsi, mi_string, sizeof(ctx->imsi)); - break; - case GSM_MI_TYPE_IMEI: - osmo_strlcpy(ctx->imei, mi_string, sizeof(ctx->imei)); - break; - case GSM_MI_TYPE_IMEISV: - break; - } - - /* Check if we can let the mobile station enter */ - return gsm48_gmm_authorize(ctx); -} - -/* Section 9.4.1 Attach request */ -static int gsm48_rx_gmm_att_req(struct sgsn_mm_ctx *ctx, struct msgb *msg, - struct gprs_llc_llme *llme) -{ - struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_gmmh(msg); - uint8_t *cur = gh->data, *msnc, *mi, *ms_ra_acc_cap; - uint8_t msnc_len, att_type, mi_len, mi_type, ms_ra_acc_cap_len; - uint16_t drx_par; - uint32_t tmsi; - char mi_string[GSM48_MI_SIZE]; - struct gprs_ra_id ra_id; - uint16_t cid = 0; - enum gsm48_gmm_cause reject_cause; - int rc; - - LOGMMCTXP(LOGL_INFO, ctx, "-> GMM ATTACH REQUEST "); - rate_ctr_inc(&sgsn->rate_ctrs->ctr[CTR_GPRS_ATTACH_REQUEST]); - - /* As per TS 04.08 Chapter 4.7.1.4, the attach request arrives either - * with a foreign TLLI (P-TMSI that was allocated to the MS before), - * or with random TLLI. */ - - /* In Iu mode, msg->dst contains the ue_conn_ctx pointer, in Gb mode - * dst is empty. */ - /* FIXME: have a more explicit indicator for Iu messages */ - if (!msg->dst) { - /* Gb mode */ - cid = bssgp_parse_cell_id(&ra_id, msgb_bcid(msg)); - } else - ra_id = ((struct ue_conn_ctx*)msg->dst)->ra_id; - - /* MS network capability 10.5.5.12 */ - msnc_len = *cur++; - msnc = cur; - if (msnc_len > sizeof(ctx->ms_network_capa.buf)) - goto err_inval; - cur += msnc_len; - - /* TODO: In iu mode - handle follow-on request */ - - /* aTTACH Type 10.5.5.2 */ - att_type = *cur++ & 0x07; - - /* DRX parameter 10.5.5.6 */ - drx_par = *cur++ << 8; - drx_par |= *cur++; - - /* Mobile Identity (P-TMSI or IMSI) 10.5.1.4 */ - mi_len = *cur++; - mi = cur; - if (mi_len > 8) - goto err_inval; - mi_type = *mi & GSM_MI_TYPE_MASK; - cur += mi_len; - - gsm48_mi_to_string(mi_string, sizeof(mi_string), mi, mi_len); - - DEBUGPC(DMM, "MI(%s) type=\"%s\" ", mi_string, - get_value_string(gprs_att_t_strs, att_type)); - - /* Old routing area identification 10.5.5.15. Skip it */ - cur += 6; - - /* MS Radio Access Capability 10.5.5.12a */ - ms_ra_acc_cap_len = *cur++; - ms_ra_acc_cap = cur; - if (ms_ra_acc_cap_len > sizeof(ctx->ms_radio_access_capa.buf)) - goto err_inval; - cur += ms_ra_acc_cap_len; - - LOGPC(DMM, LOGL_INFO, "\n"); - - /* Optional: Old P-TMSI Signature, Requested READY timer, TMSI Status */ - - switch (mi_type) { - case GSM_MI_TYPE_IMSI: - /* Try to find MM context based on IMSI */ - if (!ctx) - ctx = sgsn_mm_ctx_by_imsi(mi_string); - if (!ctx) { -#if 0 - return gsm48_tx_gmm_att_rej(msg, GMM_CAUSE_IMSI_UNKNOWN); -#else - if (msg->dst) - ctx = sgsn_mm_ctx_alloc_iu(msg->dst); - else - ctx = sgsn_mm_ctx_alloc_gb(0, &ra_id); - if (!ctx) { - reject_cause = GMM_CAUSE_NET_FAIL; - goto rejected; - } - osmo_strlcpy(ctx->imsi, mi_string, sizeof(ctx->imsi)); -#endif - } - if (ctx->ran_type == MM_CTX_T_GERAN_Gb) { - ctx->gb.tlli = msgb_tlli(msg); - ctx->gb.llme = llme; - } - msgid2mmctx(ctx, msg); - break; - case GSM_MI_TYPE_TMSI: - memcpy(&tmsi, mi+1, 4); - tmsi = ntohl(tmsi); - /* Try to find MM context based on P-TMSI */ - if (!ctx) - ctx = sgsn_mm_ctx_by_ptmsi(tmsi); - if (!ctx) { - /* Allocate a context as most of our code expects one. - * Context will not have an IMSI ultil ID RESP is received */ - if (msg->dst) - ctx = sgsn_mm_ctx_alloc_iu(msg->dst); - else - ctx = sgsn_mm_ctx_alloc_gb(msgb_tlli(msg), &ra_id); - ctx->p_tmsi = tmsi; - } - if (ctx->ran_type == MM_CTX_T_GERAN_Gb) { - ctx->gb.tlli = msgb_tlli(msg); - ctx->gb.llme = llme; - } - msgid2mmctx(ctx, msg); - break; - default: - LOGMMCTXP(LOGL_NOTICE, ctx, "Rejecting ATTACH REQUEST with " - "MI type %s\n", gsm48_mi_type_name(mi_type)); - reject_cause = GMM_CAUSE_MS_ID_NOT_DERIVED; - goto rejected; - } - /* Update MM Context with currient RA and Cell ID */ - ctx->ra = ra_id; - if (ctx->ran_type == MM_CTX_T_GERAN_Gb) - ctx->gb.cell_id = cid; - else if (ctx->ran_type == MM_CTX_T_UTRAN_Iu) { - /* DEVELOPMENT HACK: Our current HLR does not support 3G - * authentication tokens. A new HLR/VLR implementation is being - * developed. Until it is ready and actual milenage - * authentication is properly supported, we are hardcoding a - * fixed Ki and use 2G auth. */ - unsigned char tmp_rand[16]; - /* Ki 000102030405060708090a0b0c0d0e0f */ - struct osmo_sub_auth_data auth = { - .type = OSMO_AUTH_TYPE_GSM, - .algo = OSMO_AUTH_ALG_COMP128v1, - .u.gsm.ki = { - 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, - 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, - 0x0e, 0x0f - }, - }; - /* XXX: Hack to make 3G auth work with special SIM card */ - ctx->auth_state = SGSN_AUTH_AUTHENTICATE; - - RAND_bytes(tmp_rand, 16); - - memset(&ctx->auth_triplet.vec, 0, sizeof(ctx->auth_triplet.vec)); - osmo_auth_gen_vec(&ctx->auth_triplet.vec, &auth, tmp_rand); - - ctx->auth_triplet.key_seq = 0; - } - - /* Update MM Context with other data */ - ctx->drx_parms = drx_par; - ctx->ms_radio_access_capa.len = ms_ra_acc_cap_len; - memcpy(ctx->ms_radio_access_capa.buf, ms_ra_acc_cap, - ctx->ms_radio_access_capa.len); - ctx->ms_network_capa.len = msnc_len; - memcpy(ctx->ms_network_capa.buf, msnc, msnc_len); - if (!gprs_ms_net_cap_gea_supported(ctx->ms_network_capa.buf, msnc_len, - ctx->ciph_algo)) { - reject_cause = GMM_CAUSE_PROTO_ERR_UNSPEC; - LOGMMCTXP(LOGL_NOTICE, ctx, "Rejecting ATTACH REQUEST with MI " - "type %s because MS do not support required %s " - "encryption\n", gsm48_mi_type_name(mi_type), - get_value_string(gprs_cipher_names,ctx->ciph_algo)); - goto rejected; - } -#ifdef PTMSI_ALLOC - /* Allocate a new P-TMSI (+ P-TMSI signature) and update TLLI */ - /* Don't change the P-TMSI if a P-TMSI re-assignment is under way */ - if (ctx->gmm_state != GMM_COMMON_PROC_INIT) { - ctx->p_tmsi_old = ctx->p_tmsi; - ctx->p_tmsi = sgsn_alloc_ptmsi(); - } - ctx->gmm_state = GMM_COMMON_PROC_INIT; -#endif - - if (ctx->ran_type == MM_CTX_T_GERAN_Gb) { - /* Even if there is no P-TMSI allocated, the MS will - * switch from foreign TLLI to local TLLI */ - ctx->gb.tlli_new = gprs_tmsi2tlli(ctx->p_tmsi, TLLI_LOCAL); - - /* Inform LLC layer about new TLLI but keep old active */ - if (ctx->is_authenticated) - gprs_llme_copy_key(ctx, ctx->gb.llme); - - gprs_llgmm_assign(ctx->gb.llme, ctx->gb.tlli, ctx->gb.tlli_new); - } - - ctx->pending_req = GSM48_MT_GMM_ATTACH_REQ; - return gsm48_gmm_authorize(ctx); - -err_inval: - LOGPC(DMM, LOGL_INFO, "\n"); - reject_cause = GMM_CAUSE_SEM_INCORR_MSG; - -rejected: - /* Send ATTACH REJECT */ - LOGMMCTXP(LOGL_NOTICE, ctx, - "Rejecting Attach Request with cause '%s' (%d)\n", - get_value_string(gsm48_gmm_cause_names, reject_cause), reject_cause); - rc = gsm48_tx_gmm_att_rej_oldmsg(msg, reject_cause); - if (ctx) - mm_ctx_cleanup_free(ctx, "GPRS ATTACH REJ"); - else - gprs_llgmm_unassign(llme); - - return rc; - -} - -/* Section 4.7.4.1 / 9.4.5.2 MO Detach request */ -static int gsm48_rx_gmm_det_req(struct sgsn_mm_ctx *ctx, struct msgb *msg) -{ - struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_gmmh(msg); - uint8_t detach_type, power_off; - int rc = 0; - - detach_type = gh->data[0] & 0x7; - power_off = gh->data[0] & 0x8; - - /* FIXME: In 24.008 there is an optional P-TMSI and P-TMSI signature IE */ - rate_ctr_inc(&sgsn->rate_ctrs->ctr[CTR_GPRS_DETACH_REQUEST]); - LOGMMCTXP(LOGL_INFO, ctx, "-> GMM DETACH REQUEST TLLI=0x%08x type=%s %s\n", - msgb_tlli(msg), get_value_string(gprs_det_t_mo_strs, detach_type), - power_off ? "Power-off" : ""); - - /* Only send the Detach Accept (MO) if power off isn't indicated, - * see 04.08, 4.7.4.1.2/3 for details */ - if (!power_off) { - /* force_stby = 0 */ - if (ctx) - rc = gsm48_tx_gmm_det_ack(ctx, 0); - else - rc = gsm48_tx_gmm_det_ack_oldmsg(msg, 0); - } - - if (ctx) { - struct sgsn_signal_data sig_data; - memset(&sig_data, 0, sizeof(sig_data)); - sig_data.mm = ctx; - osmo_signal_dispatch(SS_SGSN, S_SGSN_DETACH, &sig_data); - mm_ctx_cleanup_free(ctx, "GPRS DETACH REQUEST"); - } - - return rc; -} - -/* Chapter 9.4.15: Routing area update accept */ -static int gsm48_tx_gmm_ra_upd_ack(struct sgsn_mm_ctx *mm) -{ - struct msgb *msg = gsm48_msgb_alloc_name("GSM 04.08 UPD ACK"); - struct gsm48_hdr *gh; - struct gsm48_ra_upd_ack *rua; - uint8_t *mid; - - rate_ctr_inc(&sgsn->rate_ctrs->ctr[CTR_GPRS_ROUTING_AREA_ACKED]); - LOGMMCTXP(LOGL_INFO, mm, "<- ROUTING AREA UPDATE ACCEPT\n"); - - mmctx2msgid(msg, mm); - - gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh)); - gh->proto_discr = GSM48_PDISC_MM_GPRS; - gh->msg_type = GSM48_MT_GMM_RA_UPD_ACK; - - rua = (struct gsm48_ra_upd_ack *) msgb_put(msg, sizeof(*rua)); - rua->force_stby = 0; /* not indicated */ - rua->upd_result = 0; /* RA updated */ - rua->ra_upd_timer = gprs_secs_to_tmr_floor(sgsn->cfg.timers.T3312); - - gsm48_construct_ra(rua->ra_id.digits, &mm->ra); - -#if 0 - /* Optional: P-TMSI signature */ - msgb_v_put(msg, GSM48_IE_GMM_PTMSI_SIG); - ptsig = msgb_put(msg, 3); - ptsig[0] = mm->p_tmsi_sig >> 16; - ptsig[1] = mm->p_tmsi_sig >> 8; - ptsig[2] = mm->p_tmsi_sig & 0xff; -#endif - -#ifdef PTMSI_ALLOC - /* Optional: Allocated P-TMSI */ - mid = msgb_put(msg, GSM48_MID_TMSI_LEN); - gsm48_generate_mid_from_tmsi(mid, mm->p_tmsi); - mid[0] = GSM48_IE_GMM_ALLOC_PTMSI; -#endif - - /* Optional: Negotiated READY timer value */ - msgb_tv_put(msg, GSM48_IE_GMM_TIMER_READY, - gprs_secs_to_tmr_floor(sgsn->cfg.timers.T3314)); - - /* Option: MS ID, ... */ - return gsm48_gmm_sendmsg(msg, 0, mm, true); -} - -/* Chapter 9.4.17: Routing area update reject */ -static int gsm48_tx_gmm_ra_upd_rej(struct msgb *old_msg, uint8_t cause) -{ - struct msgb *msg = gsm48_msgb_alloc_name("GSM 04.08 RA UPD REJ"); - struct gsm48_hdr *gh; - - LOGP(DMM, LOGL_NOTICE, "<- ROUTING AREA UPDATE REJECT\n"); - rate_ctr_inc(&sgsn->rate_ctrs->ctr[CTR_GPRS_ROUTING_AREA_REJECT]); - - gmm_copy_id(msg, old_msg); - - gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh) + 2); - gh->proto_discr = GSM48_PDISC_MM_GPRS; - gh->msg_type = GSM48_MT_GMM_RA_UPD_REJ; - gh->data[0] = cause; - gh->data[1] = 0; /* ? */ - - /* Option: P-TMSI signature, allocated P-TMSI, MS ID, ... */ - return gsm48_gmm_sendmsg(msg, 0, NULL, false); -} - -static void process_ms_ctx_status(struct sgsn_mm_ctx *mmctx, - const uint8_t *pdp_status) -{ - struct sgsn_pdp_ctx *pdp, *pdp2; - /* 24.008 4.7.5.1.3: If the PDP context status information element is - * included in ROUTING AREA UPDATE REQUEST message, then the network - * shall deactivate all those PDP contexts locally (without peer to - * peer signalling between the MS and the network), which are not in SM - * state PDP-INACTIVE on network side but are indicated by the MS as - * being in state PDP-INACTIVE. */ - - llist_for_each_entry_safe(pdp, pdp2, &mmctx->pdp_list, list) { - if (pdp->nsapi < 8) { - if (!(pdp_status[0] & (1 << pdp->nsapi))) { - LOGMMCTXP(LOGL_NOTICE, mmctx, "Dropping PDP context for NSAPI=%u " - "due to PDP CTX STATUS IE= 0x%02x%02x\n", - pdp->nsapi, pdp_status[1], pdp_status[0]); - sgsn_delete_pdp_ctx(pdp); - } - } else { - if (!(pdp_status[1] & (1 << (pdp->nsapi - 8)))) { - LOGMMCTXP(LOGL_NOTICE, mmctx, "Dropping PDP context for NSAPI=%u " - "due to PDP CTX STATUS IE= 0x%02x%02x\n", - pdp->nsapi, pdp_status[1], pdp_status[0]); - sgsn_delete_pdp_ctx(pdp); - } - } - } -} - -/* Chapter 9.4.14: Routing area update request */ -static int gsm48_rx_gmm_ra_upd_req(struct sgsn_mm_ctx *mmctx, struct msgb *msg, - struct gprs_llc_llme *llme) -{ -#ifndef PTMSI_ALLOC - struct sgsn_signal_data sig_data; -#endif - struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_gmmh(msg); - uint8_t *cur = gh->data; - uint8_t ms_ra_acc_cap_len; - struct gprs_ra_id old_ra_id; - struct tlv_parsed tp; - uint8_t upd_type; - enum gsm48_gmm_cause reject_cause = GMM_CAUSE_PROTO_ERR_UNSPEC; - int rc; - - /* TODO: In iu mode - handle follow-on request */ - - /* Update Type 10.5.5.18 */ - upd_type = *cur++ & 0x07; - - rate_ctr_inc(&sgsn->rate_ctrs->ctr[CTR_GPRS_ROUTING_AREA_REQUEST]); - LOGP(DMM, LOGL_INFO, "-> GMM RA UPDATE REQUEST type=\"%s\"\n", - get_value_string(gprs_upd_t_strs, upd_type)); - - /* Old routing area identification 10.5.5.15 */ - gsm48_parse_ra(&old_ra_id, cur); - cur += 6; - - /* MS Radio Access Capability 10.5.5.12a */ - ms_ra_acc_cap_len = *cur++; - if (ms_ra_acc_cap_len > 52) { - reject_cause = GMM_CAUSE_PROTO_ERR_UNSPEC; - goto rejected; - } - cur += ms_ra_acc_cap_len; - - /* Optional: Old P-TMSI Signature, Requested READY timer, TMSI Status, - * DRX parameter, MS network capability */ - tlv_parse(&tp, &gsm48_gmm_att_tlvdef, cur, - (msg->data + msg->len) - cur, 0, 0); - - switch (upd_type) { - case GPRS_UPD_T_RA_LA: - case GPRS_UPD_T_RA_LA_IMSI_ATT: - LOGP(DMM, LOGL_NOTICE, "Update type %i unsupported in Mode III, is your SI13 corrupt?\n", upd_type); - reject_cause = GMM_CAUSE_PROTO_ERR_UNSPEC; - goto rejected; - case GPRS_UPD_T_RA: - case GPRS_UPD_T_PERIODIC: - break; - } - - if (!mmctx) { - /* BSSGP doesn't give us an mmctx */ - - /* TODO: Check if there is an MM CTX with old_ra_id and - * the P-TMSI (if given, reguired for UMTS) or as last resort - * if the TLLI matches foreign_tlli (P-TMSI). Note that this - * is an optimization to avoid the RA reject (impl detached) - * below, which will cause a new attach cycle. */ - /* Look-up the MM context based on old RA-ID and TLLI */ - /* In Iu mode, msg->dst contains the ue_conn_ctx pointer, in Gb - * mode dst is empty. */ - /* FIXME: have a more explicit indicator for Iu messages */ - if (!msg->dst) { - mmctx = sgsn_mm_ctx_by_tlli_and_ptmsi(msgb_tlli(msg), &old_ra_id); - } else if (TLVP_PRESENT(&tp, GSM48_IE_GMM_ALLOC_PTMSI)) { -#ifdef BUILD_IU - /* In Iu mode search only for ptmsi */ - char mi_string[GSM48_MI_SIZE]; - uint8_t mi_len = TLVP_LEN(&tp, GSM48_IE_GMM_ALLOC_PTMSI); - uint8_t *mi = TLVP_VAL(&tp, GSM48_IE_GMM_ALLOC_PTMSI); - uint8_t mi_type = *mi & GSM_MI_TYPE_MASK; - uint32_t tmsi; - - gsm48_mi_to_string(mi_string, sizeof(mi_string), mi, mi_len); - - if (mi_type == GSM_MI_TYPE_TMSI) { - memcpy(&tmsi, mi+1, 4); - tmsi = ntohl(tmsi); - mmctx = sgsn_mm_ctx_by_ptmsi(tmsi); - } -#else - goto rejected; -#endif - } - if (mmctx) { - LOGMMCTXP(LOGL_INFO, mmctx, - "Looked up by matching TLLI and P_TMSI. " - "BSSGP TLLI: %08x, P-TMSI: %08x (%08x), " - "TLLI: %08x (%08x), RA: %d-%d-%d-%d\n", - msgb_tlli(msg), - mmctx->p_tmsi, mmctx->p_tmsi_old, - mmctx->gb.tlli, mmctx->gb.tlli_new, - mmctx->ra.mcc, mmctx->ra.mnc, - mmctx->ra.lac, mmctx->ra.rac); - - mmctx->gmm_state = GMM_COMMON_PROC_INIT; - } - } else if (!gprs_ra_id_equals(&mmctx->ra, &old_ra_id) || - mmctx->gmm_state == GMM_DEREGISTERED) - { - /* We cannot use the mmctx */ - LOGMMCTXP(LOGL_INFO, mmctx, - "The MM context cannot be used, RA: %d-%d-%d-%d\n", - mmctx->ra.mcc, mmctx->ra.mnc, - mmctx->ra.lac, mmctx->ra.rac); - mmctx = NULL; - } - - if (!mmctx) { - if (llme) { - /* send a XID reset to re-set all LLC sequence numbers - * in the MS */ - LOGMMCTXP(LOGL_NOTICE, mmctx, "LLC XID RESET\n"); - gprs_llgmm_reset(llme); - } - /* The MS has to perform GPRS attach */ - /* Device is still IMSI attached for CS but initiate GPRS ATTACH, - * see GSM 04.08, 4.7.5.1.4 and G.6 */ - reject_cause = GMM_CAUSE_IMPL_DETACHED; - goto rejected; - } - - /* Store new BVCI/NSEI in MM context (FIXME: delay until we ack?) */ - msgid2mmctx(mmctx, msg); - /* Bump the statistics of received signalling msgs for this MM context */ - rate_ctr_inc(&mmctx->ctrg->ctr[GMM_CTR_PKTS_SIG_IN]); - - /* Update the MM context with the new RA-ID */ - if (mmctx->ran_type == MM_CTX_T_GERAN_Gb) { - bssgp_parse_cell_id(&mmctx->ra, msgb_bcid(msg)); - /* Update the MM context with the new (i.e. foreign) TLLI */ - mmctx->gb.tlli = msgb_tlli(msg); - } - /* FIXME: Update the MM context with the MS radio acc capabilities */ - /* FIXME: Update the MM context with the MS network capabilities */ - - rate_ctr_inc(&mmctx->ctrg->ctr[GMM_CTR_RA_UPDATE]); - -#ifdef PTMSI_ALLOC - /* Don't change the P-TMSI if a P-TMSI re-assignment is under way */ - if (mmctx->gmm_state != GMM_COMMON_PROC_INIT) { - mmctx->p_tmsi_old = mmctx->p_tmsi; - mmctx->p_tmsi = sgsn_alloc_ptmsi(); - } - /* Start T3350 and re-transmit up to 5 times until ATTACH COMPLETE */ - mmctx->t3350_mode = GMM_T3350_MODE_RAU; - mmctx_timer_start(mmctx, 3350, sgsn->cfg.timers.T3350); - - mmctx->gmm_state = GMM_COMMON_PROC_INIT; -#else - /* Make sure we are NORMAL (i.e. not SUSPENDED anymore) */ - mmctx->gmm_state = GMM_REGISTERED_NORMAL; - - memset(&sig_data, 0, sizeof(sig_data)); - sig_data.mm = mmctx; - osmo_signal_dispatch(SS_SGSN, S_SGSN_UPDATE, &sig_data); -#endif - if (mmctx->ran_type == MM_CTX_T_GERAN_Gb) { - /* Even if there is no P-TMSI allocated, the MS will switch from - * foreign TLLI to local TLLI */ - mmctx->gb.tlli_new = gprs_tmsi2tlli(mmctx->p_tmsi, TLLI_LOCAL); - - /* Inform LLC layer about new TLLI but keep old active */ - gprs_llgmm_assign(mmctx->gb.llme, mmctx->gb.tlli, - mmctx->gb.tlli_new); - } - - /* Look at PDP Context Status IE and see if MS's view of - * activated/deactivated NSAPIs agrees with our view */ - if (TLVP_PRESENT(&tp, GSM48_IE_GMM_PDP_CTX_STATUS)) { - const uint8_t *pdp_status = TLVP_VAL(&tp, GSM48_IE_GMM_PDP_CTX_STATUS); - process_ms_ctx_status(mmctx, pdp_status); - } - - /* Send RA UPDATE ACCEPT. In Iu, the RA upd request can be called from - * a new Iu connection, so we might need to re-authenticate the - * connection as well as turn on integrity protection. */ - mmctx->pending_req = GSM48_MT_GMM_RA_UPD_REQ; - return gsm48_gmm_authorize(mmctx); - -rejected: - /* Send RA UPDATE REJECT */ - LOGMMCTXP(LOGL_NOTICE, mmctx, - "Rejecting RA Update Request with cause '%s' (%d)\n", - get_value_string(gsm48_gmm_cause_names, reject_cause), reject_cause); - rc = gsm48_tx_gmm_ra_upd_rej(msg, reject_cause); - if (mmctx) - mm_ctx_cleanup_free(mmctx, "GPRS RA UPDATE REJ"); - else { - if (llme) - gprs_llgmm_unassign(llme); - } - - return rc; -} - -/* 3GPP TS 24.008 Section 9.4.20 Service request. - * In Iu, a UE in PMM-IDLE mode can use GSM48_MT_GMM_SERVICE_REQ to switch back - * to PMM-CONNECTED mode. */ -static int gsm48_rx_gmm_service_req(struct sgsn_mm_ctx *ctx, struct msgb *msg) -{ - struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_gmmh(msg); - uint8_t *cur = gh->data, *mi; - uint8_t ciph_seq_nr, service_type, mi_len, mi_type; - uint32_t tmsi; - struct tlv_parsed tp; - char mi_string[GSM48_MI_SIZE]; - enum gsm48_gmm_cause reject_cause; - int rc; - - LOGMMCTXP(LOGL_INFO, ctx, "-> GMM SERVICE REQUEST "); - - /* This message is only valid in Iu mode */ - if (!msg->dst) { - LOGPC(DMM, LOGL_INFO, "Invalid if not in Iu mode\n"); - return -1; - } - - /* Skip Ciphering key sequence number 10.5.1.2 */ - ciph_seq_nr = *cur & 0x07; - - /* Service type 10.5.5.20 */ - service_type = (*cur++ >> 4) & 0x07; - - /* Mobile Identity (P-TMSI or IMSI) 10.5.1.4 */ - mi_len = *cur++; - mi = cur; - if (mi_len > 8) - goto err_inval; - mi_type = *mi & GSM_MI_TYPE_MASK; - cur += mi_len; - - gsm48_mi_to_string(mi_string, sizeof(mi_string), mi, mi_len); - - DEBUGPC(DMM, "MI(%s) type=\"%s\" ", mi_string, - get_value_string(gprs_service_t_strs, service_type)); - - LOGPC(DMM, LOGL_INFO, "\n"); - - /* Optional: PDP context status, MBMS context status, Uplink data status, Device properties */ - tlv_parse(&tp, &gsm48_gmm_att_tlvdef, cur, (msg->data + msg->len) - cur, 0, 0); - - switch (mi_type) { - case GSM_MI_TYPE_IMSI: - /* Try to find MM context based on IMSI */ - if (!ctx) - ctx = sgsn_mm_ctx_by_imsi(mi_string); - if (!ctx) { - /* FIXME: We need to have a context for service request? */ - reject_cause = GMM_CAUSE_NET_FAIL; - goto rejected; - } - msgid2mmctx(ctx, msg); - break; - case GSM_MI_TYPE_TMSI: - memcpy(&tmsi, mi+1, 4); - tmsi = ntohl(tmsi); - /* Try to find MM context based on P-TMSI */ - if (!ctx) - ctx = sgsn_mm_ctx_by_ptmsi(tmsi); - if (!ctx) { - /* FIXME: We need to have a context for service request? */ - reject_cause = GMM_CAUSE_NET_FAIL; - goto rejected; - } - msgid2mmctx(ctx, msg); - break; - default: - LOGMMCTXP(LOGL_NOTICE, ctx, "Rejecting SERVICE REQUEST with " - "MI type %s\n", gsm48_mi_type_name(mi_type)); - reject_cause = GMM_CAUSE_MS_ID_NOT_DERIVED; - goto rejected; - } - - ctx->gmm_state = GMM_COMMON_PROC_INIT; - - ctx->iu.service.type = service_type; - - /* TODO: Handle those only in case of accept? */ - /* Look at PDP Context Status IE and see if MS's view of - * activated/deactivated NSAPIs agrees with our view */ - if (TLVP_PRESENT(&tp, GSM48_IE_GMM_PDP_CTX_STATUS)) { - const uint8_t *pdp_status = TLVP_VAL(&tp, GSM48_IE_GMM_PDP_CTX_STATUS); - process_ms_ctx_status(ctx, pdp_status); - } - - - ctx->pending_req = GSM48_MT_GMM_SERVICE_REQ; - return gsm48_gmm_authorize(ctx); - -err_inval: - LOGPC(DMM, LOGL_INFO, "\n"); - reject_cause = GMM_CAUSE_SEM_INCORR_MSG; - -rejected: - /* Send SERVICE REJECT */ - LOGMMCTXP(LOGL_NOTICE, ctx, - "Rejecting Service Request with cause '%s' (%d)\n", - get_value_string(gsm48_gmm_cause_names, reject_cause), reject_cause); - rc = gsm48_tx_gmm_service_rej_oldmsg(msg, reject_cause); - - return rc; - -} - - -static int gsm48_rx_gmm_status(struct sgsn_mm_ctx *mmctx, struct msgb *msg) -{ - struct gsm48_hdr *gh = msgb_l3(msg); - - LOGMMCTXP(LOGL_INFO, mmctx, "-> GPRS MM STATUS (cause: %s)\n", - get_value_string(gsm48_gmm_cause_names, gh->data[0])); - - return 0; -} - -/* GPRS Mobility Management */ -static int gsm0408_rcv_gmm(struct sgsn_mm_ctx *mmctx, struct msgb *msg, - struct gprs_llc_llme *llme, bool drop_cipherable) -{ - struct sgsn_signal_data sig_data; - struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_gmmh(msg); - int rc; - - /* MMCTX can be NULL when called */ - if (drop_cipherable && gsm48_hdr_gmm_cipherable(gh)) { - LOGMMCTXP(LOGL_NOTICE, mmctx, "Dropping cleartext GMM %s which " - "is expected to be encrypted for TLLI 0x%08x\n", - get_value_string(gprs_msgt_gmm_names, gh->msg_type), - llme->tlli); - return -EBADMSG; - } - - if (llme && !mmctx && - gh->msg_type != GSM48_MT_GMM_ATTACH_REQ && - gh->msg_type != GSM48_MT_GMM_RA_UPD_REQ) { - LOGP(DMM, LOGL_NOTICE, "Cannot handle GMM for unknown MM CTX\n"); - /* 4.7.10 */ - if (gh->msg_type == GSM48_MT_GMM_STATUS) { - /* TLLI unassignment */ - gprs_llgmm_unassign(llme); - return 0; - } - - /* Don't reply or establish a LLME on DETACH_ACK */ - if (gh->msg_type == GSM48_MT_GMM_DETACH_ACK) - return gprs_llgmm_unassign(llme); - - gprs_llgmm_reset(llme); - - /* Don't force it into re-attachment */ - if (gh->msg_type == GSM48_MT_GMM_DETACH_REQ) { - /* Handle Detach Request */ - rc = gsm48_rx_gmm_det_req(NULL, msg); - - /* TLLI unassignment */ - gprs_llgmm_unassign(llme); - return rc; - } - - /* Force the MS to re-attach */ - rc = gsm0408_gprs_force_reattach_oldmsg(msg, llme); - - /* TLLI unassignment */ - gprs_llgmm_unassign(llme); - return rc; - } - - /* - * For a few messages, mmctx may be NULL. For most, we want to ensure a - * non-NULL mmctx. At the same time, we want to keep the message - * validity check intact, so that all message types appear in the - * switch statement and the default case thus means "unknown message". - * If we split the switch in two parts to check non-NULL halfway, the - * unknown-message check breaks, or we'd need to duplicate the switch - * cases in both parts. Just keep one large switch and add some gotos. - */ - switch (gh->msg_type) { - case GSM48_MT_GMM_RA_UPD_REQ: - rc = gsm48_rx_gmm_ra_upd_req(mmctx, msg, llme); - break; - case GSM48_MT_GMM_ATTACH_REQ: - rc = gsm48_rx_gmm_att_req(mmctx, msg, llme); - break; - case GSM48_MT_GMM_SERVICE_REQ: - rc = gsm48_rx_gmm_service_req(mmctx, msg); - break; - /* For all the following types mmctx can not be NULL */ - case GSM48_MT_GMM_ID_RESP: - if (!mmctx) - goto null_mmctx; - rc = gsm48_rx_gmm_id_resp(mmctx, msg); - break; - case GSM48_MT_GMM_STATUS: - if (!mmctx) - goto null_mmctx; - rc = gsm48_rx_gmm_status(mmctx, msg); - break; - case GSM48_MT_GMM_DETACH_REQ: - if (!mmctx) - goto null_mmctx; - rc = gsm48_rx_gmm_det_req(mmctx, msg); - break; - case GSM48_MT_GMM_DETACH_ACK: - if (!mmctx) - goto null_mmctx; - LOGMMCTXP(LOGL_INFO, mmctx, "-> DETACH ACK\n"); - mm_ctx_cleanup_free(mmctx, "GPRS DETACH ACK"); - rc = 0; - break; - case GSM48_MT_GMM_ATTACH_COMPL: - if (!mmctx) - goto null_mmctx; - /* only in case SGSN offered new P-TMSI */ - LOGMMCTXP(LOGL_INFO, mmctx, "-> ATTACH COMPLETE\n"); - mmctx_timer_stop(mmctx, 3350); - mmctx->t3350_mode = GMM_T3350_MODE_NONE; - mmctx->p_tmsi_old = 0; - mmctx->pending_req = 0; - if (mmctx->ran_type == MM_CTX_T_GERAN_Gb) { - /* Unassign the old TLLI */ - mmctx->gb.tlli = mmctx->gb.tlli_new; - gprs_llme_copy_key(mmctx, mmctx->gb.llme); - gprs_llgmm_assign(mmctx->gb.llme, 0xffffffff, - mmctx->gb.tlli_new); - } - mmctx->gmm_state = GMM_REGISTERED_NORMAL; - mmctx_set_pmm_state(mmctx, PMM_CONNECTED); - mmctx_set_mm_state(mmctx, MM_READY); - rc = 0; - - memset(&sig_data, 0, sizeof(sig_data)); - sig_data.mm = mmctx; - osmo_signal_dispatch(SS_SGSN, S_SGSN_ATTACH, &sig_data); - break; - case GSM48_MT_GMM_RA_UPD_COMPL: - if (!mmctx) - goto null_mmctx; - /* only in case SGSN offered new P-TMSI */ - LOGMMCTXP(LOGL_INFO, mmctx, "-> ROUTING AREA UPDATE COMPLETE\n"); - mmctx_timer_stop(mmctx, 3350); - mmctx->t3350_mode = GMM_T3350_MODE_NONE; - mmctx->p_tmsi_old = 0; - mmctx->pending_req = 0; - if (mmctx->ran_type == MM_CTX_T_GERAN_Gb) { - /* Unassign the old TLLI */ - mmctx->gb.tlli = mmctx->gb.tlli_new; - gprs_llgmm_assign(mmctx->gb.llme, 0xffffffff, - mmctx->gb.tlli_new); - } - mmctx->gmm_state = GMM_REGISTERED_NORMAL; - mmctx_set_pmm_state(mmctx, PMM_CONNECTED); - mmctx_set_mm_state(mmctx, MM_READY); - rc = 0; - - memset(&sig_data, 0, sizeof(sig_data)); - sig_data.mm = mmctx; - osmo_signal_dispatch(SS_SGSN, S_SGSN_UPDATE, &sig_data); - break; - case GSM48_MT_GMM_PTMSI_REALL_COMPL: - if (!mmctx) - goto null_mmctx; - LOGMMCTXP(LOGL_INFO, mmctx, "-> PTMSI REALLLICATION COMPLETE\n"); - mmctx_timer_stop(mmctx, 3350); - mmctx->t3350_mode = GMM_T3350_MODE_NONE; - mmctx->p_tmsi_old = 0; - mmctx->pending_req = 0; - if (mmctx->ran_type == MM_CTX_T_GERAN_Gb) { - /* Unassign the old TLLI */ - mmctx->gb.tlli = mmctx->gb.tlli_new; - //gprs_llgmm_assign(mmctx->gb.llme, 0xffffffff, mmctx->gb.tlli_new, GPRS_ALGO_GEA0, NULL); - } - rc = 0; - break; - case GSM48_MT_GMM_AUTH_CIPH_RESP: - if (!mmctx) - goto null_mmctx; - rc = gsm48_rx_gmm_auth_ciph_resp(mmctx, msg); - break; - case GSM48_MT_GMM_AUTH_CIPH_FAIL: - rc = gsm48_rx_gmm_auth_ciph_fail(mmctx, msg); - break; - default: - LOGMMCTXP(LOGL_NOTICE, mmctx, "Unknown GSM 04.08 GMM msg type 0x%02x\n", - gh->msg_type); - if (mmctx) - rc = gsm48_tx_gmm_status(mmctx, GMM_CAUSE_MSGT_NOTEXIST_NOTIMPL); - else - rc = -EINVAL; - break; - } - - return rc; - -null_mmctx: - LOGP(DMM, LOGL_ERROR, - "Received GSM 04.08 message type 0x%02x," - " but no MM context available\n", - gh->msg_type); - return -EINVAL; -} - -static void mmctx_timer_cb(void *_mm) -{ - struct sgsn_mm_ctx *mm = _mm; - struct gsm_auth_tuple *at; - - mm->num_T_exp++; - - switch (mm->T) { - case 3350: /* waiting for ATTACH COMPLETE */ - if (mm->num_T_exp >= 5) { - LOGMMCTXP(LOGL_NOTICE, mm, "T3350 expired >= 5 times\n"); - mm_ctx_cleanup_free(mm, "T3350"); - /* FIXME: should we return some error? */ - break; - } - /* re-transmit the respective msg and re-start timer */ - switch (mm->t3350_mode) { - case GMM_T3350_MODE_ATT: - gsm48_tx_gmm_att_ack(mm); - break; - case GMM_T3350_MODE_RAU: - gsm48_tx_gmm_ra_upd_ack(mm); - break; - case GMM_T3350_MODE_PTMSI_REALL: - /* FIXME */ - break; - case GMM_T3350_MODE_NONE: - LOGMMCTXP(LOGL_NOTICE, mm, - "T3350 mode wasn't set, ignoring timeout\n"); - break; - } - osmo_timer_schedule(&mm->timer, sgsn->cfg.timers.T3350, 0); - break; - case 3360: /* waiting for AUTH AND CIPH RESP */ - if (mm->num_T_exp >= 5) { - LOGMMCTXP(LOGL_NOTICE, mm, "T3360 expired >= 5 times\n"); - mm_ctx_cleanup_free(mm, "T3360"); - break; - } - /* Re-transmit the respective msg and re-start timer */ - if (mm->auth_triplet.key_seq == GSM_KEY_SEQ_INVAL) { - LOGMMCTXP(LOGL_ERROR, mm, - "timeout: invalid auth triplet reference\n"); - mm_ctx_cleanup_free(mm, "T3360"); - break; - } - at = &mm->auth_triplet; - - gsm48_tx_gmm_auth_ciph_req(mm, &at->vec, at->key_seq, false); - osmo_timer_schedule(&mm->timer, sgsn->cfg.timers.T3360, 0); - break; - case 3370: /* waiting for IDENTITY RESPONSE */ - if (mm->num_T_exp >= 5) { - LOGMMCTXP(LOGL_NOTICE, mm, "T3370 expired >= 5 times\n"); - gsm48_tx_gmm_att_rej(mm, GMM_CAUSE_MS_ID_NOT_DERIVED); - mm_ctx_cleanup_free(mm, "GPRS ATTACH REJECT (T3370)"); - break; - } - /* re-tranmit IDENTITY REQUEST and re-start timer */ - gsm48_tx_gmm_id_req(mm, mm->t3370_id_type); - osmo_timer_schedule(&mm->timer, sgsn->cfg.timers.T3370, 0); - break; - default: - LOGMMCTXP(LOGL_ERROR, mm, "timer expired in unknown mode %u\n", - mm->T); - } -} - -/* GPRS SESSION MANAGEMENT */ - -static void pdpctx_timer_cb(void *_mm); - -static void pdpctx_timer_start(struct sgsn_pdp_ctx *pdp, unsigned int T, - unsigned int seconds) -{ - if (osmo_timer_pending(&pdp->timer)) - LOGPDPCTXP(LOGL_ERROR, pdp, "Starting PDP timer %u while old " - "timer %u pending\n", T, pdp->T); - pdp->T = T; - pdp->num_T_exp = 0; - - /* FIXME: we should do this only once ? */ - osmo_timer_setup(&pdp->timer, pdpctx_timer_cb, pdp); - osmo_timer_schedule(&pdp->timer, seconds, 0); -} - -static void pdpctx_timer_stop(struct sgsn_pdp_ctx *pdp, unsigned int T) -{ - if (pdp->T != T) - LOGPDPCTXP(LOGL_ERROR, pdp, "Stopping PDP timer %u but " - "%u is running\n", T, pdp->T); - osmo_timer_del(&pdp->timer); -} - -#if 0 -static void msgb_put_pdp_addr_ipv4(struct msgb *msg, uint32_t ipaddr) -{ - uint8_t v[6]; - - v[0] = PDP_TYPE_ORG_IETF; - v[1] = PDP_TYPE_N_IETF_IPv4; - *(uint32_t *)(v+2) = htonl(ipaddr); - - msgb_tlv_put(msg, GSM48_IE_GSM_PDP_ADDR, sizeof(v), v); -} - -static void msgb_put_pdp_addr_ppp(struct msgb *msg) -{ - uint8_t v[2]; - - v[0] = PDP_TYPE_ORG_ETSI; - v[1] = PDP_TYPE_N_ETSI_PPP; - - msgb_tlv_put(msg, GSM48_IE_GSM_PDP_ADDR, sizeof(v), v); -} -#endif - -/* Section 9.5.2: Activate PDP Context Accept */ -int gsm48_tx_gsm_act_pdp_acc(struct sgsn_pdp_ctx *pdp) -{ - struct msgb *msg = gsm48_msgb_alloc_name("GSM 04.08 PDP ACC"); - struct gsm48_hdr *gh; - uint8_t transaction_id = pdp->ti ^ 0x8; /* flip */ - - LOGPDPCTXP(LOGL_INFO, pdp, "<- ACTIVATE PDP CONTEXT ACK\n"); - rate_ctr_inc(&sgsn->rate_ctrs->ctr[CTR_PDP_ACTIVATE_ACCEPT]); - - mmctx2msgid(msg, pdp->mm); - - gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh)); - gh->proto_discr = GSM48_PDISC_SM_GPRS | (transaction_id << 4); - gh->msg_type = GSM48_MT_GSM_ACT_PDP_ACK; - - /* Negotiated LLC SAPI */ - msgb_v_put(msg, pdp->sapi); - - /* FIXME: copy QoS parameters from original request */ - //msgb_lv_put(msg, pdp->lib->qos_neg.l, pdp->lib->qos_neg.v); - msgb_lv_put(msg, sizeof(default_qos), (uint8_t *)&default_qos); - - /* Radio priority 10.5.7.2 */ - msgb_v_put(msg, pdp->lib->radio_pri); - - /* PDP address */ - /* Highest 4 bits of first byte need to be set to 1, otherwise - * the IE is identical with the 04.08 PDP Address IE */ - pdp->lib->eua.v[0] &= ~0xf0; - msgb_tlv_put(msg, GSM48_IE_GSM_PDP_ADDR, - pdp->lib->eua.l, pdp->lib->eua.v); - pdp->lib->eua.v[0] |= 0xf0; - - /* Optional: Protocol configuration options (FIXME: why 'req') */ - if (pdp->lib->pco_req.l) - msgb_tlv_put(msg, GSM48_IE_GSM_PROTO_CONF_OPT, - pdp->lib->pco_req.l, pdp->lib->pco_req.v); - - /* Optional: Packet Flow Identifier */ - - return gsm48_gmm_sendmsg(msg, 0, pdp->mm, true); -} - -/* Section 9.5.3: Activate PDP Context reject */ -int gsm48_tx_gsm_act_pdp_rej(struct sgsn_mm_ctx *mm, uint8_t tid, - uint8_t cause, uint8_t pco_len, uint8_t *pco_v) -{ - struct msgb *msg = gsm48_msgb_alloc_name("GSM 04.08 PDP REJ"); - struct gsm48_hdr *gh; - uint8_t transaction_id = tid ^ 0x8; /* flip */ - - LOGMMCTXP(LOGL_NOTICE, mm, "<- ACTIVATE PDP CONTEXT REJ(cause=%u)\n", cause); - rate_ctr_inc(&sgsn->rate_ctrs->ctr[CTR_PDP_ACTIVATE_REJECT]); - - mmctx2msgid(msg, mm); - - gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh)); - gh->proto_discr = GSM48_PDISC_SM_GPRS | (transaction_id << 4); - gh->msg_type = GSM48_MT_GSM_ACT_PDP_REJ; - - msgb_v_put(msg, cause); - if (pco_len && pco_v) - msgb_tlv_put(msg, GSM48_IE_GSM_PROTO_CONF_OPT, pco_len, pco_v); - - return gsm48_gmm_sendmsg(msg, 0, mm, true); -} - -/* Section 9.5.8: Deactivate PDP Context Request */ -static int _gsm48_tx_gsm_deact_pdp_req(struct sgsn_mm_ctx *mm, uint8_t tid, - uint8_t sm_cause) -{ - struct msgb *msg = gsm48_msgb_alloc_name("GSM 04.08 PDP DET REQ"); - struct gsm48_hdr *gh; - uint8_t transaction_id = tid ^ 0x8; /* flip */ - - LOGMMCTXP(LOGL_INFO, mm, "<- DEACTIVATE PDP CONTEXT REQ\n"); - rate_ctr_inc(&sgsn->rate_ctrs->ctr[CTR_PDP_DL_DEACTIVATE_REQUEST]); - - mmctx2msgid(msg, mm); - - gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh)); - gh->proto_discr = GSM48_PDISC_SM_GPRS | (transaction_id << 4); - gh->msg_type = GSM48_MT_GSM_DEACT_PDP_REQ; - - msgb_v_put(msg, sm_cause); - - return gsm48_gmm_sendmsg(msg, 0, mm, true); -} -int gsm48_tx_gsm_deact_pdp_req(struct sgsn_pdp_ctx *pdp, uint8_t sm_cause) -{ - pdpctx_timer_start(pdp, 3395, sgsn->cfg.timers.T3395); - - return _gsm48_tx_gsm_deact_pdp_req(pdp->mm, pdp->ti, sm_cause); -} - -/* Section 9.5.9: Deactivate PDP Context Accept */ -static int _gsm48_tx_gsm_deact_pdp_acc(struct sgsn_mm_ctx *mm, uint8_t tid) -{ - struct msgb *msg = gsm48_msgb_alloc_name("GSM 04.08 PDP DET ACC"); - struct gsm48_hdr *gh; - uint8_t transaction_id = tid ^ 0x8; /* flip */ - - LOGMMCTXP(LOGL_INFO, mm, "<- DEACTIVATE PDP CONTEXT ACK\n"); - rate_ctr_inc(&sgsn->rate_ctrs->ctr[CTR_PDP_DL_DEACTIVATE_ACCEPT]); - - mmctx2msgid(msg, mm); - - gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh)); - gh->proto_discr = GSM48_PDISC_SM_GPRS | (transaction_id << 4); - gh->msg_type = GSM48_MT_GSM_DEACT_PDP_ACK; - - return gsm48_gmm_sendmsg(msg, 0, mm, true); -} -int gsm48_tx_gsm_deact_pdp_acc(struct sgsn_pdp_ctx *pdp) -{ - return _gsm48_tx_gsm_deact_pdp_acc(pdp->mm, pdp->ti); -} - -static int activate_ggsn(struct sgsn_mm_ctx *mmctx, - struct sgsn_ggsn_ctx *ggsn, const uint8_t transaction_id, - const uint8_t req_nsapi, const uint8_t req_llc_sapi, - struct tlv_parsed *tp, int destroy_ggsn) -{ - struct sgsn_pdp_ctx *pdp; - - LOGMMCTXP(LOGL_DEBUG, mmctx, "Using GGSN %u\n", ggsn->id); - ggsn->gsn = sgsn->gsn; - pdp = sgsn_create_pdp_ctx(ggsn, mmctx, req_nsapi, tp); - if (!pdp) - return -1; - - /* Store SAPI and Transaction Identifier */ - pdp->sapi = req_llc_sapi; - pdp->ti = transaction_id; - pdp->destroy_ggsn = destroy_ggsn; - - return 0; -} - -static void ggsn_lookup_cb(void *arg, int status, int timeouts, struct hostent *hostent) -{ - struct sgsn_ggsn_ctx *ggsn; - struct sgsn_ggsn_lookup *lookup = arg; - struct in_addr *addr = NULL; - - /* The context is gone while we made a request */ - if (!lookup->mmctx) { - talloc_free(lookup->orig_msg); - talloc_free(lookup); - return; - } - - if (status != ARES_SUCCESS) { - struct sgsn_mm_ctx *mmctx = lookup->mmctx; - - LOGMMCTXP(LOGL_ERROR, mmctx, "DNS query failed.\n"); - - /* Need to try with three digits now */ - if (lookup->state == SGSN_GGSN_2DIGIT) { - char *hostname; - int rc; - - lookup->state = SGSN_GGSN_3DIGIT; - hostname = osmo_apn_qualify_from_imsi(mmctx->imsi, - lookup->apn_str, 1); - LOGMMCTXP(LOGL_DEBUG, mmctx, - "Going to query %s\n", hostname); - rc = sgsn_ares_query(sgsn, hostname, - ggsn_lookup_cb, lookup); - if (rc != 0) { - LOGMMCTXP(LOGL_ERROR, mmctx, "Couldn't start GGSN\n"); - goto reject_due_failure; - } - return; - } - - LOGMMCTXP(LOGL_ERROR, mmctx, "Couldn't resolve GGSN\n"); - goto reject_due_failure; - } - - if (hostent->h_length != sizeof(struct in_addr)) { - LOGMMCTXP(LOGL_ERROR, lookup->mmctx, - "Wrong addr size(%zu)\n", sizeof(struct in_addr)); - goto reject_due_failure; - } - - /* Get the first addr from the list */ - addr = (struct in_addr *) hostent->h_addr_list[0]; - if (!addr) { - LOGMMCTXP(LOGL_ERROR, lookup->mmctx, "No host address.\n"); - goto reject_due_failure; - } - - ggsn = sgsn_ggsn_ctx_alloc(UINT32_MAX); - if (!ggsn) { - LOGMMCTXP(LOGL_ERROR, lookup->mmctx, "Failed to create ggsn.\n"); - goto reject_due_failure; - } - ggsn->remote_addr = *addr; - LOGMMCTXP(LOGL_NOTICE, lookup->mmctx, - "Selected %s as GGSN.\n", inet_ntoa(*addr)); - - /* forget about the ggsn look-up */ - lookup->mmctx->ggsn_lookup = NULL; - - activate_ggsn(lookup->mmctx, ggsn, lookup->ti, lookup->nsapi, - lookup->sapi, &lookup->tp, 1); - - /* Now free it */ - talloc_free(lookup->orig_msg); - talloc_free(lookup); - return; - -reject_due_failure: - gsm48_tx_gsm_act_pdp_rej(lookup->mmctx, lookup->ti, - GMM_CAUSE_NET_FAIL, 0, NULL); - lookup->mmctx->ggsn_lookup = NULL; - talloc_free(lookup->orig_msg); - talloc_free(lookup); -} - -static int do_act_pdp_req(struct sgsn_mm_ctx *mmctx, struct msgb *msg, bool *delete) -{ - struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_gmmh(msg); - struct gsm48_act_pdp_ctx_req *act_req = (struct gsm48_act_pdp_ctx_req *) gh->data; - uint8_t req_qos_len, req_pdpa_len; - uint8_t *req_qos, *req_pdpa; - struct tlv_parsed tp; - uint8_t transaction_id = gsm48_hdr_trans_id(gh); - struct sgsn_ggsn_ctx *ggsn; - struct sgsn_pdp_ctx *pdp; - enum gsm48_gsm_cause gsm_cause; - char apn_str[GSM_APN_LENGTH] = { 0, }; - char *hostname; - int rc; - struct gprs_llc_lle *lle; - - LOGMMCTXP(LOGL_INFO, mmctx, "-> ACTIVATE PDP CONTEXT REQ: SAPI=%u NSAPI=%u ", - act_req->req_llc_sapi, act_req->req_nsapi); - - /* FIXME: length checks! */ - req_qos_len = act_req->data[0]; - req_qos = act_req->data + 1; /* 10.5.6.5 */ - req_pdpa_len = act_req->data[1 + req_qos_len]; - req_pdpa = act_req->data + 1 + req_qos_len + 1; /* 10.5.6.4 */ - - switch (req_pdpa[0] & 0xf) { - case 0x0: - DEBUGPC(DMM, "ETSI "); - break; - case 0x1: - DEBUGPC(DMM, "IETF "); - break; - case 0xf: - DEBUGPC(DMM, "Empty "); - break; - } - - switch (req_pdpa[1]) { - case 0x21: - DEBUGPC(DMM, "IPv4 "); - if (req_pdpa_len >= 6) { - struct in_addr ia; - ia.s_addr = ntohl(*((uint32_t *) (req_pdpa+2))); - DEBUGPC(DMM, "%s ", inet_ntoa(ia)); - } - break; - case 0x57: - DEBUGPC(DMM, "IPv6 "); - if (req_pdpa_len >= 18) { - /* FIXME: print IPv6 address */ - } - break; - default: - DEBUGPC(DMM, "0x%02x ", req_pdpa[1]); - break; - } - - LOGPC(DMM, LOGL_INFO, "\n"); - - /* Check if NSAPI is out of range (TS 04.65 / 7.2) */ - if (act_req->req_nsapi < 5 || act_req->req_nsapi > 15) { - /* Send reject with GSM_CAUSE_INV_MAND_INFO */ - return gsm48_tx_gsm_act_pdp_rej(mmctx, transaction_id, - GSM_CAUSE_INV_MAND_INFO, - 0, NULL); - } - - /* Optional: Access Point Name, Protocol Config Options */ - if (req_pdpa + req_pdpa_len < msg->data + msg->len) - tlv_parse(&tp, &gsm48_sm_att_tlvdef, req_pdpa + req_pdpa_len, - (msg->data + msg->len) - (req_pdpa + req_pdpa_len), 0, 0); - else - memset(&tp, 0, sizeof(tp)); - - - /* put the non-TLV elements in the TLV parser structure to - * pass them on to the SGSN / GTP code */ - tp.lv[OSMO_IE_GSM_REQ_QOS].len = req_qos_len; - tp.lv[OSMO_IE_GSM_REQ_QOS].val = req_qos; - tp.lv[OSMO_IE_GSM_REQ_PDP_ADDR].len = req_pdpa_len; - tp.lv[OSMO_IE_GSM_REQ_PDP_ADDR].val = req_pdpa; - - /* Check if NSAPI is already in use */ - pdp = sgsn_pdp_ctx_by_nsapi(mmctx, act_req->req_nsapi); - if (pdp) { - /* We already have a PDP context for this TLLI + NSAPI tuple */ - if (pdp->sapi == act_req->req_llc_sapi && - pdp->ti == transaction_id) { - /* This apparently is a re-transmission of a PDP CTX - * ACT REQ (our ACT ACK must have got dropped) */ - rc = gsm48_tx_gsm_act_pdp_acc(pdp); - if (rc < 0) - return rc; - - if (pdp->mm->ran_type == MM_CTX_T_GERAN_Gb) { - /* Also re-transmit the SNDCP XID message */ - lle = &pdp->mm->gb.llme->lle[pdp->sapi]; - rc = sndcp_sn_xid_req(lle,pdp->nsapi); - if (rc < 0) - return rc; - } - - return 0; - } - - /* Send reject with GSM_CAUSE_NSAPI_IN_USE */ - return gsm48_tx_gsm_act_pdp_rej(mmctx, transaction_id, - GSM_CAUSE_NSAPI_IN_USE, - 0, NULL); - } - - if (mmctx->ggsn_lookup) { - if (mmctx->ggsn_lookup->sapi == act_req->req_llc_sapi && - mmctx->ggsn_lookup->ti == transaction_id) { - LOGMMCTXP(LOGL_NOTICE, mmctx, - "Re-transmission while doing look-up. Ignoring.\n"); - return 0; - } - } - - /* Only increment counter for a real activation, after we checked - * for re-transmissions */ - rate_ctr_inc(&mmctx->ctrg->ctr[GMM_CTR_PDP_CTX_ACT]); - - /* Determine GGSN based on APN and subscription options */ - ggsn = sgsn_mm_ctx_find_ggsn_ctx(mmctx, &tp, &gsm_cause, apn_str); - if (ggsn) - return activate_ggsn(mmctx, ggsn, transaction_id, - act_req->req_nsapi, act_req->req_llc_sapi, - &tp, 0); - - if (strlen(apn_str) == 0) - goto no_context; - if (!sgsn->cfg.dynamic_lookup) - goto no_context; - - /* schedule a dynamic look-up */ - mmctx->ggsn_lookup = talloc_zero(tall_bsc_ctx, struct sgsn_ggsn_lookup); - if (!mmctx->ggsn_lookup) - goto no_context; - - mmctx->ggsn_lookup->state = SGSN_GGSN_2DIGIT; - mmctx->ggsn_lookup->mmctx = mmctx; - strcpy(mmctx->ggsn_lookup->apn_str, apn_str); - - mmctx->ggsn_lookup->orig_msg = msg; - mmctx->ggsn_lookup->tp = tp; - - mmctx->ggsn_lookup->ti = transaction_id; - mmctx->ggsn_lookup->nsapi = act_req->req_nsapi; - mmctx->ggsn_lookup->sapi = act_req->req_llc_sapi; - - hostname = osmo_apn_qualify_from_imsi(mmctx->imsi, - mmctx->ggsn_lookup->apn_str, 0); - - LOGMMCTXP(LOGL_DEBUG, mmctx, "Going to query %s\n", hostname); - rc = sgsn_ares_query(sgsn, hostname, - ggsn_lookup_cb, mmctx->ggsn_lookup); - if (rc != 0) { - LOGMMCTXP(LOGL_ERROR, mmctx, "Failed to start ares query.\n"); - goto no_context; - } - *delete = 0; - - return 0; - -no_context: - LOGMMCTXP(LOGL_ERROR, mmctx, "No GGSN context found!\n"); - return gsm48_tx_gsm_act_pdp_rej(mmctx, transaction_id, - gsm_cause, 0, NULL); -} - -/* Section 9.5.1: Activate PDP Context Request */ -static int gsm48_rx_gsm_act_pdp_req(struct sgsn_mm_ctx *mmctx, - struct msgb *_msg) -{ - bool delete = 1; - struct msgb *msg; - int rc; - - rate_ctr_inc(&sgsn->rate_ctrs->ctr[CTR_PDP_ACTIVATE_REQUEST]); - - /* - * This is painful. We might not have a static GGSN - * configuration and then would need to copy the msg - * and re-do most of this routine (or call it again - * and make sure it only goes through the dynamic - * resolving. The question is what to optimize for - * and the dynamic resolution will be the right thing - * in the long run. - */ - msg = gprs_msgb_copy(_msg, __func__); - if (!msg) { - struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_gmmh(_msg); - uint8_t transaction_id = gsm48_hdr_trans_id(gh); - - LOGMMCTXP(LOGL_ERROR, mmctx, "-> ACTIVATE PDP CONTEXT REQ failed copy.\n"); - /* Send reject with GSM_CAUSE_INV_MAND_INFO */ - return gsm48_tx_gsm_act_pdp_rej(mmctx, transaction_id, - GSM_CAUSE_NET_FAIL, - 0, NULL); - } - - rc = do_act_pdp_req(mmctx, msg, &delete); - if (delete) - msgb_free(msg); - return rc; -} - -/* Section 9.5.8: Deactivate PDP Context Request */ -static int gsm48_rx_gsm_deact_pdp_req(struct sgsn_mm_ctx *mm, struct msgb *msg) -{ - struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_gmmh(msg); - uint8_t transaction_id = gsm48_hdr_trans_id(gh); - struct sgsn_pdp_ctx *pdp; - - LOGMMCTXP(LOGL_INFO, mm, "-> DEACTIVATE PDP CONTEXT REQ (cause: %s)\n", - get_value_string(gsm48_gsm_cause_names, gh->data[0])); - rate_ctr_inc(&sgsn->rate_ctrs->ctr[CTR_PDP_UL_DEACTIVATE_REQUEST]); - - pdp = sgsn_pdp_ctx_by_tid(mm, transaction_id); - if (!pdp) { - LOGMMCTXP(LOGL_NOTICE, mm, "Deactivate PDP Context Request for " - "non-existing PDP Context (IMSI=%s, TI=%u)\n", - mm->imsi, transaction_id); - return _gsm48_tx_gsm_deact_pdp_acc(mm, transaction_id); - } - - return sgsn_delete_pdp_ctx(pdp); -} - -/* Section 9.5.9: Deactivate PDP Context Accept */ -static int gsm48_rx_gsm_deact_pdp_ack(struct sgsn_mm_ctx *mm, struct msgb *msg) -{ - struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_gmmh(msg); - uint8_t transaction_id = gsm48_hdr_trans_id(gh); - struct sgsn_pdp_ctx *pdp; - - LOGMMCTXP(LOGL_INFO, mm, "-> DEACTIVATE PDP CONTEXT ACK\n"); - rate_ctr_inc(&sgsn->rate_ctrs->ctr[CTR_PDP_UL_DEACTIVATE_ACCEPT]); - - pdp = sgsn_pdp_ctx_by_tid(mm, transaction_id); - if (!pdp) { - LOGMMCTXP(LOGL_NOTICE, mm, "Deactivate PDP Context Accept for " - "non-existing PDP Context (IMSI=%s, TI=%u)\n", - mm->imsi, transaction_id); - return 0; - } - /* stop timer 3395 */ - pdpctx_timer_stop(pdp, 3395); - return sgsn_delete_pdp_ctx(pdp); -} - -static int gsm48_rx_gsm_status(struct sgsn_mm_ctx *ctx, struct msgb *msg) -{ - struct gsm48_hdr *gh = msgb_l3(msg); - - LOGMMCTXP(LOGL_INFO, ctx, "-> GPRS SM STATUS (cause: %s)\n", - get_value_string(gsm48_gsm_cause_names, gh->data[0])); - - return 0; -} - -static void pdpctx_timer_cb(void *_pdp) -{ - struct sgsn_pdp_ctx *pdp = _pdp; - - pdp->num_T_exp++; - - switch (pdp->T) { - case 3395: /* waiting for PDP CTX DEACT ACK */ - if (pdp->num_T_exp >= 4) { - LOGPDPCTXP(LOGL_NOTICE, pdp, "T3395 expired >= 5 times\n"); - pdp->state = PDP_STATE_INACTIVE; - sgsn_delete_pdp_ctx(pdp); - break; - } - gsm48_tx_gsm_deact_pdp_req(pdp, GSM_CAUSE_NET_FAIL); - break; - default: - LOGPDPCTXP(LOGL_ERROR, pdp, "timer expired in unknown mode %u\n", - pdp->T); - } -} - - -/* GPRS Session Management */ -static int gsm0408_rcv_gsm(struct sgsn_mm_ctx *mmctx, struct msgb *msg, - struct gprs_llc_llme *llme) -{ - struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_gmmh(msg); - int rc; - - /* MMCTX can be NULL when called */ - - if (!mmctx) { - LOGP(DMM, LOGL_NOTICE, "Cannot handle SM for unknown MM CTX\n"); - /* 6.1.3.6 */ - if (gh->msg_type == GSM48_MT_GSM_STATUS) - return 0; - - return gsm0408_gprs_force_reattach_oldmsg(msg, llme); - } - - switch (gh->msg_type) { - case GSM48_MT_GSM_ACT_PDP_REQ: - rc = gsm48_rx_gsm_act_pdp_req(mmctx, msg); - break; - case GSM48_MT_GSM_DEACT_PDP_REQ: - rc = gsm48_rx_gsm_deact_pdp_req(mmctx, msg); - break; - case GSM48_MT_GSM_DEACT_PDP_ACK: - rc = gsm48_rx_gsm_deact_pdp_ack(mmctx, msg); - break; - case GSM48_MT_GSM_STATUS: - rc = gsm48_rx_gsm_status(mmctx, msg); - break; - case GSM48_MT_GSM_REQ_PDP_ACT_REJ: - case GSM48_MT_GSM_ACT_AA_PDP_REQ: - case GSM48_MT_GSM_DEACT_AA_PDP_REQ: - LOGMMCTXP(LOGL_NOTICE, mmctx, "Unimplemented GSM 04.08 GSM msg type 0x%02x: %s\n", - gh->msg_type, osmo_hexdump((uint8_t *)gh, msgb_l3len(msg))); - rc = gsm48_tx_sm_status(mmctx, GSM_CAUSE_MSGT_NOTEXIST_NOTIMPL); - break; - default: - LOGMMCTXP(LOGL_NOTICE, mmctx, "Unknown GSM 04.08 GSM msg type 0x%02x: %s\n", - gh->msg_type, osmo_hexdump((uint8_t *)gh, msgb_l3len(msg))); - rc = gsm48_tx_sm_status(mmctx, GSM_CAUSE_MSGT_NOTEXIST_NOTIMPL); - break; - - } - - return rc; -} - -int gsm0408_gprs_force_reattach_oldmsg(struct msgb *msg, - struct gprs_llc_llme *llme) -{ - int rc; - if (llme) - gprs_llgmm_reset_oldmsg(msg, GPRS_SAPI_GMM, llme); - - rc = gsm48_tx_gmm_detach_req_oldmsg( - msg, GPRS_DET_T_MT_REATT_REQ, GMM_CAUSE_IMPL_DETACHED); - - return rc; -} - -int gsm0408_gprs_force_reattach(struct sgsn_mm_ctx *mmctx) -{ - int rc; - if (mmctx->ran_type == MM_CTX_T_GERAN_Gb) - gprs_llgmm_reset(mmctx->gb.llme); - - rc = gsm48_tx_gmm_detach_req( - mmctx, GPRS_DET_T_MT_REATT_REQ, GMM_CAUSE_IMPL_DETACHED); - - mm_ctx_cleanup_free(mmctx, "forced reattach"); - - return rc; -} - -/* Main entry point for incoming 04.08 GPRS messages from Iu */ -int gsm0408_gprs_rcvmsg_iu(struct msgb *msg, struct gprs_ra_id *ra_id, - uint16_t *sai) -{ - struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_gmmh(msg); - uint8_t pdisc = gsm48_hdr_pdisc(gh); - struct sgsn_mm_ctx *mmctx; - int rc = -EINVAL; - - mmctx = sgsn_mm_ctx_by_ue_ctx(msg->dst); - if (mmctx) { - rate_ctr_inc(&mmctx->ctrg->ctr[GMM_CTR_PKTS_SIG_IN]); - if (ra_id) - memcpy(&mmctx->ra, ra_id, sizeof(mmctx->ra)); - } - - /* MMCTX can be NULL */ - - switch (pdisc) { - case GSM48_PDISC_MM_GPRS: - rc = gsm0408_rcv_gmm(mmctx, msg, NULL, false); -#warning "set drop_cipherable arg for gsm0408_rcv_gmm() from IuPS?" - break; - case GSM48_PDISC_SM_GPRS: - rc = gsm0408_rcv_gsm(mmctx, msg, NULL); - break; - default: - LOGMMCTXP(LOGL_NOTICE, mmctx, - "Unknown GSM 04.08 discriminator 0x%02x: %s\n", - pdisc, osmo_hexdump((uint8_t *)gh, msgb_l3len(msg))); - /* FIXME: return status message */ - break; - } - - /* MMCTX can be invalid */ - - return rc; -} - -/* Main entry point for incoming 04.08 GPRS messages from Gb */ -int gsm0408_gprs_rcvmsg_gb(struct msgb *msg, struct gprs_llc_llme *llme, - bool drop_cipherable) -{ - struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_gmmh(msg); - uint8_t pdisc = gsm48_hdr_pdisc(gh); - struct sgsn_mm_ctx *mmctx; - struct gprs_ra_id ra_id; - int rc = -EINVAL; - - bssgp_parse_cell_id(&ra_id, msgb_bcid(msg)); - mmctx = sgsn_mm_ctx_by_tlli(msgb_tlli(msg), &ra_id); - if (mmctx) { - msgid2mmctx(mmctx, msg); - rate_ctr_inc(&mmctx->ctrg->ctr[GMM_CTR_PKTS_SIG_IN]); - mmctx->gb.llme = llme; - } - - /* MMCTX can be NULL */ - - switch (pdisc) { - case GSM48_PDISC_MM_GPRS: - rc = gsm0408_rcv_gmm(mmctx, msg, llme, drop_cipherable); - break; - case GSM48_PDISC_SM_GPRS: - rc = gsm0408_rcv_gsm(mmctx, msg, llme); - break; - default: - LOGMMCTXP(LOGL_NOTICE, mmctx, - "Unknown GSM 04.08 discriminator 0x%02x: %s\n", - pdisc, osmo_hexdump((uint8_t *)gh, msgb_l3len(msg))); - /* FIXME: return status message */ - break; - } - - /* MMCTX can be invalid */ - - return rc; -} - -int gprs_gmm_rx_suspend(struct gprs_ra_id *raid, uint32_t tlli) -{ - struct sgsn_mm_ctx *mmctx; - - mmctx = sgsn_mm_ctx_by_tlli(tlli, raid); - if (!mmctx) { - LOGP(DMM, LOGL_NOTICE, "SUSPEND request for unknown " - "TLLI=%08x\n", tlli); - return -EINVAL; - } - - if (mmctx->gmm_state != GMM_REGISTERED_NORMAL && - mmctx->gmm_state != GMM_REGISTERED_SUSPENDED) { - LOGMMCTXP(LOGL_NOTICE, mmctx, "SUSPEND request while state " - "!= REGISTERED (TLLI=%08x)\n", tlli); - return -EINVAL; - } - - /* Transition from REGISTERED_NORMAL to REGISTERED_SUSPENDED */ - mmctx->gmm_state = GMM_REGISTERED_SUSPENDED; - return 0; -} - -int gprs_gmm_rx_resume(struct gprs_ra_id *raid, uint32_t tlli, - uint8_t suspend_ref) -{ - struct sgsn_mm_ctx *mmctx; - - /* FIXME: make use of suspend reference? */ - - mmctx = sgsn_mm_ctx_by_tlli(tlli, raid); - if (!mmctx) { - LOGP(DMM, LOGL_NOTICE, "RESUME request for unknown " - "TLLI=%08x\n", tlli); - return -EINVAL; - } - - if (mmctx->gmm_state != GMM_REGISTERED_NORMAL && - mmctx->gmm_state != GMM_REGISTERED_SUSPENDED) { - LOGMMCTXP(LOGL_NOTICE, mmctx, "RESUME request while state " - "!= SUSPENDED (TLLI=%08x)\n", tlli); - /* FIXME: should we not simply ignore it? */ - return -EINVAL; - } - - /* Transition from SUSPENDED to NORMAL */ - mmctx->gmm_state = GMM_REGISTERED_NORMAL; - return 0; -} - -#ifdef BUILD_IU -int iu_rab_act_ps(uint8_t rab_id, struct sgsn_pdp_ctx *pdp, bool use_x213_nsap) -{ - struct msgb *msg; - struct sgsn_mm_ctx *mm = pdp->mm; - struct ue_conn_ctx *uectx; - uint32_t ggsn_ip; - - uectx = mm->iu.ue_ctx; - - /* Get the IP address for ggsn user plane */ - memcpy(&ggsn_ip, pdp->lib->gsnru.v, pdp->lib->gsnru.l); - ggsn_ip = htonl(ggsn_ip); - - LOGP(DRANAP, LOGL_DEBUG, "Assigning RAB: rab_id=%d, ggsn_ip=%x," - " teid_gn=%x, use_x213_nsap=%d\n", - rab_id, ggsn_ip, pdp->lib->teid_gn, use_x213_nsap); - - msg = ranap_new_msg_rab_assign_data(rab_id, ggsn_ip, - pdp->lib->teid_gn, use_x213_nsap); - msg->l2h = msg->data; - return iu_rab_act(uectx, msg); -} -#endif diff --git a/openbsc/src/gprs/gprs_llc.c b/openbsc/src/gprs/gprs_llc.c deleted file mode 100644 index 2be663f9..00000000 --- a/openbsc/src/gprs/gprs_llc.c +++ /dev/null @@ -1,1132 +0,0 @@ -/* GPRS LLC protocol implementation as per 3GPP TS 04.64 */ - -/* (C) 2009-2010 by Harald Welte - * - * All Rights Reserved - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation; either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - * - */ - -#include -#include -#include - -#include - -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -static struct gprs_llc_llme *llme_alloc(uint32_t tlli); -static int gprs_llc_tx_xid(struct gprs_llc_lle *lle, struct msgb *msg, - int command); -static int gprs_llc_tx_u(struct msgb *msg, uint8_t sapi, - int command, enum gprs_llc_u_cmd u_cmd, int pf_bit); - -/* BEGIN XID RELATED */ - -/* Generate XID message */ -static int gprs_llc_generate_xid(uint8_t *bytes, int bytes_len, - struct gprs_llc_xid_field *l3_xid_field, - struct gprs_llc_llme *llme) -{ - /* Note: Called by gprs_ll_xid_req() */ - - LLIST_HEAD(xid_fields); - - struct gprs_llc_xid_field xid_version; - struct gprs_llc_xid_field xid_n201u; - struct gprs_llc_xid_field xid_n201i; - - xid_version.type = GPRS_LLC_XID_T_VERSION; - xid_version.data = (uint8_t *) "\x00"; - xid_version.data_len = 1; - - xid_n201u.type = GPRS_LLC_XID_T_N201_U; - xid_n201u.data = (uint8_t *) "\x05\xf0"; - xid_n201u.data_len = 2; - - xid_n201i.type = GPRS_LLC_XID_T_N201_I; - xid_n201i.data = (uint8_t *) "\x05\xf0"; - xid_n201i.data_len = 2; - - /* Add locally managed XID Fields */ - llist_add(&xid_version.list, &xid_fields); - llist_add(&xid_n201u.list, &xid_fields); - llist_add(&xid_n201i.list, &xid_fields); - - /* Append layer 3 XID field (if present) */ - if (l3_xid_field) { - /* Enforce layer 3 XID type (just to be sure) */ - l3_xid_field->type = GPRS_LLC_XID_T_L3_PAR; - - /* Add Layer 3 XID field to the list */ - llist_add(&l3_xid_field->list, &xid_fields); - } - - /* Store generated XID for later reference */ - talloc_free(llme->xid); - llme->xid = gprs_llc_copy_xid(llme, &xid_fields); - - return gprs_llc_compile_xid(bytes, bytes_len, &xid_fields); -} - -/* Generate XID message that will cause the GMM to reset */ -static int gprs_llc_generate_xid_for_gmm_reset(uint8_t *bytes, - int bytes_len, uint32_t iov_ui, - struct gprs_llc_llme *llme) -{ - /* Called by gprs_llgmm_reset() and - * gprs_llgmm_reset_oldmsg() */ - - LLIST_HEAD(xid_fields); - - struct gprs_llc_xid_field xid_reset; - struct gprs_llc_xid_field xid_iovui; - - /* First XID component must be RESET */ - xid_reset.type = GPRS_LLC_XID_T_RESET; - xid_reset.data = NULL; - xid_reset.data_len = 0; - - /* Add new IOV-UI */ - xid_iovui.type = GPRS_LLC_XID_T_IOV_UI; - xid_iovui.data = (uint8_t *) & iov_ui; - xid_iovui.data_len = 4; - - /* Add locally managed XID Fields */ - llist_add(&xid_iovui.list, &xid_fields); - llist_add(&xid_reset.list, &xid_fields); - - /* Store generated XID for later reference */ - talloc_free(llme->xid); - llme->xid = gprs_llc_copy_xid(llme, &xid_fields); - - return gprs_llc_compile_xid(bytes, bytes_len, &xid_fields); -} - -/* Process an incoming XID confirmation */ -static int gprs_llc_process_xid_conf(uint8_t *bytes, int bytes_len, - struct gprs_llc_lle *lle) -{ - /* Note: This function handles the response of a network originated - * XID-Request. There XID messages reflected by the MS are analyzed - * and processed here. The caller is called by rx_llc_xid(). */ - - struct llist_head *xid_fields; - struct gprs_llc_xid_field *xid_field; - struct gprs_llc_xid_field *xid_field_request; - struct gprs_llc_xid_field *xid_field_request_l3 = NULL; - - /* Pick layer3 XID from the XID request we have sent last */ - if (lle->llme->xid) { - llist_for_each_entry(xid_field_request, lle->llme->xid, list) { - if (xid_field_request->type == GPRS_LLC_XID_T_L3_PAR) - xid_field_request_l3 = xid_field_request; - } - } - - /* Parse and analyze XID-Response */ - xid_fields = gprs_llc_parse_xid(NULL, bytes, bytes_len); - - if (xid_fields) { - - gprs_llc_dump_xid_fields(xid_fields, LOGL_DEBUG); - llist_for_each_entry(xid_field, xid_fields, list) { - - /* Forward SNDCP-XID fields to Layer 3 (SNDCP) */ - if (xid_field->type == GPRS_LLC_XID_T_L3_PAR && - xid_field_request_l3) { - sndcp_sn_xid_conf(xid_field, - xid_field_request_l3, lle); - } - - /* Process LLC-XID fields: */ - else { - - /* FIXME: Do something more useful with the - * echoed XID-Information. Currently we - * just ignore the response completely and - * by doing so we blindly accept any changes - * the MS might have done to the our XID - * inquiry. There is a remainig risk of - * malfunction! */ - LOGP(DLLC, LOGL_NOTICE, - "Ignoring XID-Field: XID: type %s, data_len=%d, data=%s\n", - get_value_string(gprs_llc_xid_type_names, - xid_field->type), - xid_field->data_len, - osmo_hexdump_nospc(xid_field->data, - xid_field->data_len)); - } - } - talloc_free(xid_fields); - } - - /* Flush pending XID fields */ - talloc_free(lle->llme->xid); - lle->llme->xid = NULL; - - return 0; -} - -/* Process an incoming XID indication and generate an appropiate response */ -static int gprs_llc_process_xid_ind(uint8_t *bytes_request, - int bytes_request_len, - uint8_t *bytes_response, - int bytes_response_maxlen, - struct gprs_llc_lle *lle) -{ - /* Note: This function computes the response that is sent back to the - * MS when a mobile originated XID is received. The function is - * called by rx_llc_xid() */ - - int rc = -EINVAL; - - struct llist_head *xid_fields; - struct llist_head *xid_fields_response; - - struct gprs_llc_xid_field *xid_field; - struct gprs_llc_xid_field *xid_field_response; - - /* Parse and analyze XID-Request */ - xid_fields = - gprs_llc_parse_xid(lle->llme, bytes_request, bytes_request_len); - if (xid_fields) { - xid_fields_response = talloc_zero(lle->llme, struct llist_head); - INIT_LLIST_HEAD(xid_fields_response); - gprs_llc_dump_xid_fields(xid_fields, LOGL_DEBUG); - - /* Process LLC-XID fields: */ - llist_for_each_entry(xid_field, xid_fields, list) { - - if (xid_field->type != GPRS_LLC_XID_T_L3_PAR) { - /* FIXME: Check the incoming XID parameters for - * for validity. Currently we just blindly - * accept all XID fields by just echoing them. - * There is a remaining risk of malfunction - * when a MS submits values which defer from - * the default! */ - LOGP(DLLC, LOGL_NOTICE, - "Echoing XID-Field: XID: type %s, data_len=%d, data=%s\n", - get_value_string(gprs_llc_xid_type_names, - xid_field->type), - xid_field->data_len, - osmo_hexdump_nospc(xid_field->data, - xid_field->data_len)); - xid_field_response = - gprs_llc_dup_xid_field - (lle->llme, xid_field); - llist_add(&xid_field_response->list, - xid_fields_response); - } - } - - /* Forward SNDCP-XID fields to Layer 3 (SNDCP) */ - llist_for_each_entry(xid_field, xid_fields, list) { - if (xid_field->type == GPRS_LLC_XID_T_L3_PAR) { - - xid_field_response = - talloc_zero(lle->llme, - struct gprs_llc_xid_field); - rc = sndcp_sn_xid_ind(xid_field, - xid_field_response, lle); - if (rc == 0) - llist_add(&xid_field_response->list, - xid_fields_response); - else - talloc_free(xid_field_response); - } - } - - rc = gprs_llc_compile_xid(bytes_response, - bytes_response_maxlen, - xid_fields_response); - talloc_free(xid_fields_response); - talloc_free(xid_fields); - } - - return rc; -} - -/* Dispatch XID indications and responses comming from the MS */ -static void rx_llc_xid(struct gprs_llc_lle *lle, - struct gprs_llc_hdr_parsed *gph) -{ - uint8_t response[1024]; - int response_len; - - /* FIXME: 8.5.3.3: check if XID is invalid */ - if (gph->is_cmd) { - LOGP(DLLC, LOGL_NOTICE, - "Received XID indication from MS.\n"); - - struct msgb *resp; - uint8_t *xid; - resp = msgb_alloc_headroom(4096, 1024, "LLC_XID"); - - response_len = - gprs_llc_process_xid_ind(gph->data, gph->data_len, - response, sizeof(response), - lle); - if (response_len < 0) { - LOGP(DLLC, LOGL_ERROR, - "invalid XID indication received!\n"); - } else { - xid = msgb_put(resp, response_len); - memcpy(xid, response, response_len); - } - gprs_llc_tx_xid(lle, resp, 0); - } else { - LOGP(DLLC, LOGL_NOTICE, - "Received XID confirmation from MS.\n"); - gprs_llc_process_xid_conf(gph->data, gph->data_len, lle); - /* FIXME: if we had sent a XID reset, send - * LLGMM-RESET.conf to GMM */ - } -} - -/* Set of LL-XID negotiation (See also: TS 101 351, Section 7.2.2.4) */ -int gprs_ll_xid_req(struct gprs_llc_lle *lle, - struct gprs_llc_xid_field *l3_xid_field) -{ - /* Note: This functions is calle from gprs_sndcp.c */ - - uint8_t xid_bytes[1024];; - int xid_bytes_len; - uint8_t *xid; - struct msgb *msg; - const char *ftype; - - /* Generate XID */ - xid_bytes_len = - gprs_llc_generate_xid(xid_bytes, sizeof(xid_bytes), - l3_xid_field, lle->llme); - - /* Only perform XID sending if the XID message contains something */ - if (xid_bytes_len > 0) { - /* Transmit XID bytes */ - msg = msgb_alloc_headroom(4096, 1024, "LLC_XID"); - xid = msgb_put(msg, xid_bytes_len); - memcpy(xid, xid_bytes, xid_bytes_len); - if (l3_xid_field) - ftype = get_value_string(gprs_llc_xid_type_names, - l3_xid_field->type); - else - ftype = "NULL"; - LOGP(DLLC, LOGL_NOTICE, "Sending XID type %s (%d bytes) request" - " to MS...\n", ftype, xid_bytes_len); - gprs_llc_tx_xid(lle, msg, 1); - } else { - LOGP(DLLC, LOGL_ERROR, - "XID-Message generation failed, XID not sent!\n"); - return -EINVAL; - } - - return 0; -} -/* END XID RELATED */ - - - - -/* Entry function from upper level (LLC), asking us to transmit a BSSGP PDU - * to a remote MS (identified by TLLI) at a BTS identified by its BVCI and NSEI */ -static int _bssgp_tx_dl_ud(struct msgb *msg, struct sgsn_mm_ctx *mmctx) -{ - struct bssgp_dl_ud_par dup; - const uint8_t qos_profile_default[3] = { 0x00, 0x00, 0x20 }; - - memset(&dup, 0, sizeof(dup)); - /* before we have received some identity from the MS, we might - * not yet have a MMC context (e.g. XID negotiation of primarly - * LLC connection from GMM sapi). */ - if (mmctx) { - dup.imsi = mmctx->imsi; - dup.drx_parms = mmctx->drx_parms; - dup.ms_ra_cap.len = mmctx->ms_radio_access_capa.len; - dup.ms_ra_cap.v = mmctx->ms_radio_access_capa.buf; - - /* make sure we only send it to the right llme */ - OSMO_ASSERT(msgb_tlli(msg) == mmctx->gb.llme->tlli - || msgb_tlli(msg) == mmctx->gb.llme->old_tlli); - } - memcpy(&dup.qos_profile, qos_profile_default, - sizeof(qos_profile_default)); - - return bssgp_tx_dl_ud(msg, 1000, &dup); -} - - -/* Section 8.9.9 LLC layer parameter default values */ -static const struct gprs_llc_params llc_default_params[NUM_SAPIS] = { - [1] = { - .t200_201 = 5, - .n200 = 3, - .n201_u = 400, - }, - [2] = { - .t200_201 = 5, - .n200 = 3, - .n201_u = 270, - }, - [3] = { - .iov_i_exp = 27, - .t200_201 = 5, - .n200 = 3, - .n201_u = 500, - .n201_i = 1503, - .mD = 1520, - .mU = 1520, - .kD = 16, - .kU = 16, - }, - [5] = { - .iov_i_exp = 27, - .t200_201 = 10, - .n200 = 3, - .n201_u = 500, - .n201_i = 1503, - .mD = 760, - .mU = 760, - .kD = 8, - .kU = 8, - }, - [7] = { - .t200_201 = 20, - .n200 = 3, - .n201_u = 270, - }, - [8] = { - .t200_201 = 20, - .n200 = 3, - .n201_u = 270, - }, - [9] = { - .iov_i_exp = 27, - .t200_201 = 20, - .n200 = 3, - .n201_u = 500, - .n201_i = 1503, - .mD = 380, - .mU = 380, - .kD = 4, - .kU = 4, - }, - [11] = { - .iov_i_exp = 27, - .t200_201 = 40, - .n200 = 3, - .n201_u = 500, - .n201_i = 1503, - .mD = 190, - .mU = 190, - .kD = 2, - .kU = 2, - }, -}; - -LLIST_HEAD(gprs_llc_llmes); -void *llc_tall_ctx; - -/* lookup LLC Entity based on DLCI (TLLI+SAPI tuple) */ -static struct gprs_llc_lle *lle_by_tlli_sapi(const uint32_t tlli, uint8_t sapi) -{ - struct gprs_llc_llme *llme; - - llist_for_each_entry(llme, &gprs_llc_llmes, list) { - if (llme->tlli == tlli || llme->old_tlli == tlli) - return &llme->lle[sapi]; - } - return NULL; -} - -struct gprs_llc_lle *gprs_lle_get_or_create(const uint32_t tlli, uint8_t sapi) -{ - struct gprs_llc_llme *llme; - struct gprs_llc_lle *lle; - - lle = lle_by_tlli_sapi(tlli, sapi); - if (lle) - return lle; - - LOGP(DLLC, LOGL_NOTICE, "LLC: unknown TLLI 0x%08x, " - "creating LLME on the fly\n", tlli); - llme = llme_alloc(tlli); - lle = &llme->lle[sapi]; - return lle; -} - -struct llist_head *gprs_llme_list(void) -{ - return &gprs_llc_llmes; -} - -/* lookup LLC Entity for RX based on DLCI (TLLI+SAPI tuple) */ -static struct gprs_llc_lle *lle_for_rx_by_tlli_sapi(const uint32_t tlli, - uint8_t sapi, enum gprs_llc_cmd cmd) -{ - struct gprs_llc_lle *lle; - - /* We already know about this TLLI */ - lle = lle_by_tlli_sapi(tlli, sapi); - if (lle) - return lle; - - /* Maybe it is a routing area update but we already know this sapi? */ - if (gprs_tlli_type(tlli) == TLLI_FOREIGN) { - lle = lle_by_tlli_sapi(tlli, sapi); - if (lle) { - LOGP(DLLC, LOGL_NOTICE, - "LLC RX: Found a local entry for TLLI 0x%08x\n", - tlli); - return lle; - } - } - - /* 7.2.1.1 LLC belonging to unassigned TLLI+SAPI shall be discarded, - * except UID and XID frames with SAPI=1 */ - if (sapi == GPRS_SAPI_GMM && - (cmd == GPRS_LLC_XID || cmd == GPRS_LLC_UI)) { - struct gprs_llc_llme *llme; - /* FIXME: don't use the TLLI but the 0xFFFF unassigned? */ - llme = llme_alloc(tlli); - LOGP(DLLC, LOGL_NOTICE, "LLC RX: unknown TLLI 0x%08x, " - "creating LLME on the fly\n", tlli); - lle = &llme->lle[sapi]; - return lle; - } - - LOGP(DLLC, LOGL_NOTICE, - "unknown TLLI(0x%08x)/SAPI(%d): Silently dropping\n", - tlli, sapi); - return NULL; -} - -static void lle_init(struct gprs_llc_llme *llme, uint8_t sapi) -{ - struct gprs_llc_lle *lle = &llme->lle[sapi]; - - lle->llme = llme; - lle->sapi = sapi; - lle->state = GPRS_LLES_UNASSIGNED; - - /* Initialize according to parameters */ - memcpy(&lle->params, &llc_default_params[sapi], sizeof(lle->params)); -} - -static struct gprs_llc_llme *llme_alloc(uint32_t tlli) -{ - struct gprs_llc_llme *llme; - uint32_t i; - - llme = talloc_zero(llc_tall_ctx, struct gprs_llc_llme); - if (!llme) - return NULL; - - llme->tlli = tlli; - llme->old_tlli = 0xffffffff; - llme->state = GPRS_LLMS_UNASSIGNED; - llme->age_timestamp = GPRS_LLME_RESET_AGE; - llme->cksn = GSM_KEY_SEQ_INVAL; - - for (i = 0; i < ARRAY_SIZE(llme->lle); i++) - lle_init(llme, i); - - llist_add(&llme->list, &gprs_llc_llmes); - - llme->comp.proto = gprs_sndcp_comp_alloc(llme); - llme->comp.data = gprs_sndcp_comp_alloc(llme); - - return llme; -} - -static void llme_free(struct gprs_llc_llme *llme) -{ - gprs_sndcp_comp_free(llme->comp.proto); - gprs_sndcp_comp_free(llme->comp.data); - talloc_free(llme->xid); - llist_del(&llme->list); - talloc_free(llme); -} - -#if 0 -/* FIXME: Unused code... */ -static void t200_expired(void *data) -{ - struct gprs_llc_lle *lle = data; - - /* 8.5.1.3: Expiry of T200 */ - - if (lle->retrans_ctr >= lle->params.n200) { - /* FIXME: LLGM-STATUS-IND, LL-RELEASE-IND/CNF */ - lle->state = GPRS_LLES_ASSIGNED_ADM; - } - - switch (lle->state) { - case GPRS_LLES_LOCAL_EST: - /* FIXME: retransmit SABM */ - /* FIXME: re-start T200 */ - lle->retrans_ctr++; - break; - case GPRS_LLES_LOCAL_REL: - /* FIXME: retransmit DISC */ - /* FIXME: re-start T200 */ - lle->retrans_ctr++; - break; - default: - LOGP(DLLC, LOGL_ERROR, "LLC unhandled state: %d\n", lle->state); - break; - } - -} - -static void t201_expired(void *data) -{ - struct gprs_llc_lle *lle = data; - - if (lle->retrans_ctr < lle->params.n200) { - /* FIXME: transmit apropriate supervisory frame (8.6.4.1) */ - /* FIXME: set timer T201 */ - lle->retrans_ctr++; - } -} -#endif - -int gprs_llc_tx_u(struct msgb *msg, uint8_t sapi, int command, - enum gprs_llc_u_cmd u_cmd, int pf_bit) -{ - uint8_t *fcs, *llch; - uint8_t addr, ctrl; - uint32_t fcs_calc; - - /* Identifiers from UP: (TLLI, SAPI) + (BVCI, NSEI) */ - - /* Address Field */ - addr = sapi & 0xf; - if (command) - addr |= 0x40; - - /* 6.3 Figure 8 */ - ctrl = 0xe0 | u_cmd; - if (pf_bit) - ctrl |= 0x10; - - /* prepend LLC UI header */ - llch = msgb_push(msg, 2); - llch[0] = addr; - llch[1] = ctrl; - - /* append FCS to end of frame */ - fcs = msgb_put(msg, 3); - fcs_calc = gprs_llc_fcs(llch, fcs - llch); - fcs[0] = fcs_calc & 0xff; - fcs[1] = (fcs_calc >> 8) & 0xff; - fcs[2] = (fcs_calc >> 16) & 0xff; - - /* Identifiers passed down: (BVCI, NSEI) */ - - rate_ctr_inc(&sgsn->rate_ctrs->ctr[CTR_LLC_DL_PACKETS]); - rate_ctr_add(&sgsn->rate_ctrs->ctr[CTR_LLC_DL_BYTES], msg->len); - - /* Send BSSGP-DL-UNITDATA.req */ - return _bssgp_tx_dl_ud(msg, NULL); -} - -/* Send XID response to LLE */ -static int gprs_llc_tx_xid(struct gprs_llc_lle *lle, struct msgb *msg, - int command) -{ - /* copy identifiers from LLE to ensure lower layers can route */ - msgb_tlli(msg) = lle->llme->tlli; - msgb_bvci(msg) = lle->llme->bvci; - msgb_nsei(msg) = lle->llme->nsei; - - return gprs_llc_tx_u(msg, lle->sapi, command, GPRS_LLC_U_XID, 1); -} - -/* encrypt information field + FCS, if needed! */ -static int apply_gea(struct gprs_llc_lle *lle, uint16_t crypt_len, uint16_t nu, - uint32_t oc, uint8_t sapi, uint8_t *fcs, uint8_t *data) -{ - uint8_t cipher_out[GSM0464_CIPH_MAX_BLOCK]; - - if (lle->llme->algo == GPRS_ALGO_GEA0) - return -EINVAL; - - /* Compute the 'Input' Paraemeter */ - uint32_t fcs_calc, iv = gprs_cipher_gen_input_ui(lle->llme->iov_ui, sapi, - nu, oc); - /* Compute gamma that we need to XOR with the data */ - int r = gprs_cipher_run(cipher_out, crypt_len, lle->llme->algo, - lle->llme->kc, iv, - fcs ? GPRS_CIPH_SGSN2MS : GPRS_CIPH_MS2SGSN); - if (r < 0) { - LOGP(DLLC, LOGL_ERROR, "Error producing %s gamma for UI " - "frame: %d\n", get_value_string(gprs_cipher_names, - lle->llme->algo), r); - return -ENOMSG; - } - - if (fcs) { - /* Mark frame as encrypted and update FCS */ - data[2] |= 0x02; - fcs_calc = gprs_llc_fcs(data, fcs - data); - fcs[0] = fcs_calc & 0xff; - fcs[1] = (fcs_calc >> 8) & 0xff; - fcs[2] = (fcs_calc >> 16) & 0xff; - data += 3; - } - - /* XOR the cipher output with the data */ - for (r = 0; r < crypt_len; r++) - *(data + r) ^= cipher_out[r]; - - return 0; -} - -/* Transmit a UI frame over the given SAPI: - 'encryptable' indicates whether particular message can be encrypted according - to 3GPP TS 24.008 § 4.7.1.2 - */ -int gprs_llc_tx_ui(struct msgb *msg, uint8_t sapi, int command, - struct sgsn_mm_ctx *mmctx, bool encryptable) -{ - struct gprs_llc_lle *lle; - uint8_t *fcs, *llch; - uint8_t addr, ctrl[2]; - uint32_t fcs_calc; - uint16_t nu = 0; - uint32_t oc; - - /* Identifiers from UP: (TLLI, SAPI) + (BVCI, NSEI) */ - - /* look-up or create the LL Entity for this (TLLI, SAPI) tuple */ - lle = gprs_lle_get_or_create(msgb_tlli(msg), sapi); - - if (msg->len > lle->params.n201_u) { - LOGP(DLLC, LOGL_ERROR, "Cannot Tx %u bytes (N201-U=%u)\n", - msg->len, lle->params.n201_u); - msgb_free(msg); - return -EFBIG; - } - - gprs_llme_copy_key(mmctx, lle->llme); - - /* Update LLE's (BVCI, NSEI) tuple */ - lle->llme->bvci = msgb_bvci(msg); - lle->llme->nsei = msgb_nsei(msg); - - /* Obtain current values for N(u) and OC */ - nu = lle->vu_send; - oc = lle->oc_ui_send; - /* Increment V(U) */ - lle->vu_send = (lle->vu_send + 1) % 512; - /* Increment Overflow Counter, if needed */ - if ((lle->vu_send + 1) / 512) - lle->oc_ui_send += 512; - - /* Address Field */ - addr = sapi & 0xf; - if (command) - addr |= 0x40; - - /* Control Field */ - ctrl[0] = 0xc0; - ctrl[0] |= nu >> 6; - ctrl[1] = (nu << 2) & 0xfc; - ctrl[1] |= 0x01; /* Protected Mode */ - - /* prepend LLC UI header */ - llch = msgb_push(msg, 3); - llch[0] = addr; - llch[1] = ctrl[0]; - llch[2] = ctrl[1]; - - /* append FCS to end of frame */ - fcs = msgb_put(msg, 3); - fcs_calc = gprs_llc_fcs(llch, fcs - llch); - fcs[0] = fcs_calc & 0xff; - fcs[1] = (fcs_calc >> 8) & 0xff; - fcs[2] = (fcs_calc >> 16) & 0xff; - - if (lle->llme->algo != GPRS_ALGO_GEA0 && encryptable) { - int rc = apply_gea(lle, fcs - llch, nu, oc, sapi, fcs, llch); - if (rc < 0) { - msgb_free(msg); - return rc; - } - } - - rate_ctr_inc(&sgsn->rate_ctrs->ctr[CTR_LLC_DL_PACKETS]); - rate_ctr_add(&sgsn->rate_ctrs->ctr[CTR_LLC_DL_BYTES], msg->len); - - /* Identifiers passed down: (BVCI, NSEI) */ - - /* Send BSSGP-DL-UNITDATA.req */ - return _bssgp_tx_dl_ud(msg, mmctx); -} - -static int gprs_llc_hdr_rx(struct gprs_llc_hdr_parsed *gph, - struct gprs_llc_lle *lle) -{ - switch (gph->cmd) { - case GPRS_LLC_SABM: /* Section 6.4.1.1 */ - lle->v_sent = lle->v_ack = lle->v_recv = 0; - if (lle->state == GPRS_LLES_ASSIGNED_ADM) { - /* start re-establishment (8.7.1) */ - } - lle->state = GPRS_LLES_REMOTE_EST; - /* FIXME: Send UA */ - lle->state = GPRS_LLES_ABM; - /* FIXME: process data */ - break; - case GPRS_LLC_DISC: /* Section 6.4.1.2 */ - /* FIXME: Send UA */ - /* terminate ABM */ - lle->state = GPRS_LLES_ASSIGNED_ADM; - break; - case GPRS_LLC_UA: /* Section 6.4.1.3 */ - if (lle->state == GPRS_LLES_LOCAL_EST) - lle->state = GPRS_LLES_ABM; - break; - case GPRS_LLC_DM: /* Section 6.4.1.4: ABM cannot be performed */ - if (lle->state == GPRS_LLES_LOCAL_EST) - lle->state = GPRS_LLES_ASSIGNED_ADM; - break; - case GPRS_LLC_FRMR: /* Section 6.4.1.5 */ - break; - case GPRS_LLC_XID: /* Section 6.4.1.6 */ - rx_llc_xid(lle, gph); - break; - case GPRS_LLC_UI: - if (gprs_llc_is_retransmit(gph->seq_tx, lle->vu_recv)) { - LOGP(DLLC, LOGL_NOTICE, - "TLLI=%08x dropping UI, N(U=%d) not in window V(URV(UR:%d).\n", - lle->llme ? lle->llme->tlli : -1, - gph->seq_tx, lle->vu_recv); - - /* HACK: non-standard recovery handling. If remote LLE - * is re-transmitting the same sequence number for - * three times, don't discard the frame but pass it on - * and 'learn' the new sequence number */ - if (gph->seq_tx != lle->vu_recv_last) { - lle->vu_recv_last = gph->seq_tx; - lle->vu_recv_duplicates = 0; - } else { - lle->vu_recv_duplicates++; - if (lle->vu_recv_duplicates < 3) - return -EIO; - LOGP(DLLC, LOGL_NOTICE, "TLLI=%08x recovering " - "N(U=%d) after receiving %u duplicates\n", - lle->llme ? lle->llme->tlli : -1, - gph->seq_tx, lle->vu_recv_duplicates); - } - } - /* Increment the sequence number that we expect in the next frame */ - lle->vu_recv = (gph->seq_tx + 1) % 512; - /* Increment Overflow Counter */ - if ((gph->seq_tx + 1) / 512) - lle->oc_ui_recv += 512; - break; - default: - LOGP(DLLC, LOGL_NOTICE, "Unhandled command: %d\n", gph->cmd); - break; - } - - return 0; -} - -/* receive an incoming LLC PDU (BSSGP-UL-UNITDATA-IND, 7.2.4.2) */ -int gprs_llc_rcvmsg(struct msgb *msg, struct tlv_parsed *tv) -{ - struct gprs_llc_hdr *lh = (struct gprs_llc_hdr *) msgb_llch(msg); - struct gprs_llc_hdr_parsed llhp; - struct gprs_llc_lle *lle = NULL; - bool drop_cipherable = false; - int rc = 0; - - /* Identifiers from DOWN: NSEI, BVCI, TLLI */ - - memset(&llhp, 0, sizeof(llhp)); - rc = gprs_llc_hdr_parse(&llhp, (uint8_t *) lh, TLVP_LEN(tv, BSSGP_IE_LLC_PDU)); - if (rc < 0) { - LOGP(DLLC, LOGL_NOTICE, "Error during LLC header parsing\n"); - return rc; - } - - switch (gprs_tlli_type(msgb_tlli(msg))) { - case TLLI_LOCAL: - case TLLI_FOREIGN: - case TLLI_RANDOM: - case TLLI_AUXILIARY: - break; - default: - LOGP(DLLC, LOGL_ERROR, - "Discarding frame with strange TLLI type\n"); - break; - } - - /* find the LLC Entity for this TLLI+SAPI tuple */ - lle = lle_for_rx_by_tlli_sapi(msgb_tlli(msg), llhp.sapi, llhp.cmd); - if (!lle) { - switch (llhp.sapi) { - case GPRS_SAPI_SNDCP3: - case GPRS_SAPI_SNDCP5: - case GPRS_SAPI_SNDCP9: - case GPRS_SAPI_SNDCP11: - /* Ask an upper layer for help. */ - return gsm0408_gprs_force_reattach_oldmsg(msg, NULL); - default: - break; - } - return 0; - } - gprs_llc_hdr_dump(&llhp, lle); - /* reset age computation */ - lle->llme->age_timestamp = GPRS_LLME_RESET_AGE; - - /* decrypt information field + FCS, if needed! */ - if (llhp.is_encrypted) { - if (lle->llme->algo != GPRS_ALGO_GEA0) { - rc = apply_gea(lle, llhp.data_len + 3, llhp.seq_tx, - lle->oc_ui_recv, lle->sapi, NULL, - llhp.data); - if (rc < 0) - return rc; - llhp.fcs = *(llhp.data + llhp.data_len); - llhp.fcs |= *(llhp.data + llhp.data_len + 1) << 8; - llhp.fcs |= *(llhp.data + llhp.data_len + 2) << 16; - } else { - LOGP(DLLC, LOGL_NOTICE, "encrypted frame for LLC that " - "has no KC/Algo! Dropping.\n"); - return 0; - } - } else { - if (lle->llme->algo != GPRS_ALGO_GEA0 && - lle->llme->cksn != GSM_KEY_SEQ_INVAL) - drop_cipherable = true; - } - - /* We have to do the FCS check _after_ decryption */ - llhp.fcs_calc = gprs_llc_fcs((uint8_t *)lh, llhp.crc_length); - if (llhp.fcs != llhp.fcs_calc) { - LOGP(DLLC, LOGL_INFO, "Dropping frame with invalid FCS\n"); - return -EIO; - } - - /* Update LLE's (BVCI, NSEI) tuple */ - lle->llme->bvci = msgb_bvci(msg); - lle->llme->nsei = msgb_nsei(msg); - - /* Receive and Process the actual LLC frame */ - rc = gprs_llc_hdr_rx(&llhp, lle); - if (rc < 0) - return rc; - - rate_ctr_inc(&sgsn->rate_ctrs->ctr[CTR_LLC_UL_PACKETS]); - rate_ctr_add(&sgsn->rate_ctrs->ctr[CTR_LLC_UL_BYTES], msg->len); - - /* llhp.data is only set when we need to send LL_[UNIT]DATA_IND up */ - if (llhp.cmd == GPRS_LLC_UI && llhp.data && llhp.data_len) { - msgb_gmmh(msg) = llhp.data; - switch (llhp.sapi) { - case GPRS_SAPI_GMM: - /* send LL_UNITDATA_IND to GMM */ - rc = gsm0408_gprs_rcvmsg_gb(msg, lle->llme, - drop_cipherable); - break; - case GPRS_SAPI_SNDCP3: - case GPRS_SAPI_SNDCP5: - case GPRS_SAPI_SNDCP9: - case GPRS_SAPI_SNDCP11: - /* send LL_DATA_IND/LL_UNITDATA_IND to SNDCP */ - rc = sndcp_llunitdata_ind(msg, lle, llhp.data, llhp.data_len); - break; - case GPRS_SAPI_SMS: - /* FIXME */ - case GPRS_SAPI_TOM2: - case GPRS_SAPI_TOM8: - /* FIXME: send LL_DATA_IND/LL_UNITDATA_IND to TOM */ - default: - LOGP(DLLC, LOGL_NOTICE, "Unsupported SAPI %u\n", llhp.sapi); - rc = -EINVAL; - break; - } - } - - return rc; -} - -/* Propagate crypto parameters MM -> LLME */ -void gprs_llme_copy_key(struct sgsn_mm_ctx *mm, struct gprs_llc_llme *llme) -{ - if (!mm) - return; - if (mm->ciph_algo != GPRS_ALGO_GEA0) { - llme->algo = mm->ciph_algo; - if (llme->cksn != mm->auth_triplet.key_seq && - mm->auth_triplet.key_seq != GSM_KEY_SEQ_INVAL) { - memcpy(llme->kc, mm->auth_triplet.vec.kc, - gprs_cipher_key_length(mm->ciph_algo)); - llme->cksn = mm->auth_triplet.key_seq; - } - } else - llme->cksn = GSM_KEY_SEQ_INVAL; -} - -/* 04.64 Chapter 7.2.1.1 LLGMM-ASSIGN */ -int gprs_llgmm_assign(struct gprs_llc_llme *llme, - uint32_t old_tlli, uint32_t new_tlli) -{ - unsigned int i; - - if (old_tlli == 0xffffffff && new_tlli != 0xffffffff) { - /* TLLI Assignment 8.3.1 */ - /* New TLLI shall be assigned and used when (re)transmitting LLC frames */ - /* If old TLLI != 0xffffffff was assigned to LLME, then TLLI - * old is unassigned. Only TLLI new shall be accepted when - * received from peer. */ - if (llme->old_tlli != 0xffffffff) { - llme->old_tlli = 0xffffffff; - llme->tlli = new_tlli; - } else { - /* If TLLI old == 0xffffffff was assigned to LLME, then this is - * TLLI assignmemt according to 8.3.1 */ - llme->old_tlli = 0xffffffff; - llme->tlli = new_tlli; - llme->state = GPRS_LLMS_ASSIGNED; - /* 8.5.3.1 For all LLE's */ - for (i = 0; i < ARRAY_SIZE(llme->lle); i++) { - struct gprs_llc_lle *l = &llme->lle[i]; - l->vu_send = l->vu_recv = 0; - l->retrans_ctr = 0; - l->state = GPRS_LLES_ASSIGNED_ADM; - /* FIXME Set parameters according to table 9 */ - } - } - } else if (old_tlli != 0xffffffff && new_tlli != 0xffffffff) { - /* TLLI Change 8.3.2 */ - /* Both TLLI Old and TLLI New are assigned; use New when - * (re)transmitting. Accept both Old and New on Rx */ - llme->old_tlli = old_tlli; - llme->tlli = new_tlli; - llme->state = GPRS_LLMS_ASSIGNED; - } else if (old_tlli != 0xffffffff && new_tlli == 0xffffffff) { - /* TLLI Unassignment 8.3.3) */ - llme->tlli = llme->old_tlli = 0; - llme->state = GPRS_LLMS_UNASSIGNED; - for (i = 0; i < ARRAY_SIZE(llme->lle); i++) { - struct gprs_llc_lle *l = &llme->lle[i]; - l->state = GPRS_LLES_UNASSIGNED; - } - llme_free(llme); - } else - return -EINVAL; - - return 0; -} - -/* TLLI unassignment */ -int gprs_llgmm_unassign(struct gprs_llc_llme *llme) -{ - return gprs_llgmm_assign(llme, llme->tlli, 0xffffffff); -} - -/* Chapter 7.2.1.2 LLGMM-RESET.req */ -int gprs_llgmm_reset(struct gprs_llc_llme *llme) -{ - struct msgb *msg = msgb_alloc_headroom(4096, 1024, "LLC_XID"); - struct gprs_llc_lle *lle = &llme->lle[1]; - uint8_t xid_bytes[1024]; - int xid_bytes_len; - uint8_t *xid; - - LOGP(DLLC, LOGL_NOTICE, "LLGM Reset\n"); - if (RAND_bytes((uint8_t *) &llme->iov_ui, 4) != 1) { - LOGP(DLLC, LOGL_NOTICE, "RAND_bytes failed for LLC XID reset, " - "falling back to rand()\n"); - llme->iov_ui = rand(); - } - - /* Generate XID message */ - xid_bytes_len = gprs_llc_generate_xid_for_gmm_reset(xid_bytes, - sizeof(xid_bytes),llme->iov_ui,llme); - if (xid_bytes_len < 0) - return -EINVAL; - xid = msgb_put(msg, xid_bytes_len); - memcpy(xid, xid_bytes, xid_bytes_len); - - /* Reset some of the LLC parameters. See GSM 04.64, 8.5.3.1 */ - lle->vu_recv = 0; - lle->vu_send = 0; - lle->oc_ui_send = 0; - lle->oc_ui_recv = 0; - - /* FIXME: Start T200, wait for XID response */ - return gprs_llc_tx_xid(lle, msg, 1); -} - -int gprs_llgmm_reset_oldmsg(struct msgb* oldmsg, uint8_t sapi, - struct gprs_llc_llme *llme) -{ - struct msgb *msg = msgb_alloc_headroom(4096, 1024, "LLC_XID"); - uint8_t xid_bytes[1024]; - int xid_bytes_len; - uint8_t *xid; - - LOGP(DLLC, LOGL_NOTICE, "LLGM Reset\n"); - if (RAND_bytes((uint8_t *) &llme->iov_ui, 4) != 1) { - LOGP(DLLC, LOGL_NOTICE, "RAND_bytes failed for LLC XID reset, " - "falling back to rand()\n"); - llme->iov_ui = rand(); - } - - /* Generate XID message */ - xid_bytes_len = gprs_llc_generate_xid_for_gmm_reset(xid_bytes, - sizeof(xid_bytes),llme->iov_ui,llme); - if (xid_bytes_len < 0) - return -EINVAL; - xid = msgb_put(msg, xid_bytes_len); - memcpy(xid, xid_bytes, xid_bytes_len); - - /* FIXME: Start T200, wait for XID response */ - - msgb_tlli(msg) = msgb_tlli(oldmsg); - msgb_bvci(msg) = msgb_bvci(oldmsg); - msgb_nsei(msg) = msgb_nsei(oldmsg); - - return gprs_llc_tx_u(msg, sapi, 1, GPRS_LLC_U_XID, 1); -} - -int gprs_llc_init(const char *cipher_plugin_path) -{ - return gprs_cipher_load(cipher_plugin_path); -} diff --git a/openbsc/src/gprs/gprs_llc_parse.c b/openbsc/src/gprs/gprs_llc_parse.c deleted file mode 100644 index a5a7a712..00000000 --- a/openbsc/src/gprs/gprs_llc_parse.c +++ /dev/null @@ -1,251 +0,0 @@ -/* GPRS LLC protocol implementation as per 3GPP TS 04.64 */ - -/* (C) 2009-2010 by Harald Welte - * - * All Rights Reserved - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation; either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - * - */ - -#include -#include - -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include - -static const struct value_string llc_cmd_strs[] = { - { GPRS_LLC_NULL, "NULL" }, - { GPRS_LLC_RR, "RR" }, - { GPRS_LLC_ACK, "ACK" }, - { GPRS_LLC_RNR, "RNR" }, - { GPRS_LLC_SACK, "SACK" }, - { GPRS_LLC_DM, "DM" }, - { GPRS_LLC_DISC, "DISC" }, - { GPRS_LLC_UA, "UA" }, - { GPRS_LLC_SABM, "SABM" }, - { GPRS_LLC_FRMR, "FRMR" }, - { GPRS_LLC_XID, "XID" }, - { GPRS_LLC_UI, "UI" }, - { 0, NULL } -}; - -#define LLC_ALLOC_SIZE 16384 -#define UI_HDR_LEN 3 -#define N202 4 -#define CRC24_LENGTH 3 - -int gprs_llc_fcs(uint8_t *data, unsigned int len) -{ - uint32_t fcs_calc; - - fcs_calc = crc24_calc(INIT_CRC24, data, len); - fcs_calc = ~fcs_calc; - fcs_calc &= 0xffffff; - - return fcs_calc; -} - -void gprs_llc_hdr_dump(struct gprs_llc_hdr_parsed *gph, struct gprs_llc_lle *lle) -{ - const char *gea; - uint32_t iov_ui = 0; - if (lle) { - gea = get_value_string(gprs_cipher_names, lle->llme->algo); - iov_ui = lle->llme->iov_ui; - } else - gea = "GEA?"; - DEBUGP(DLLC, "LLC SAPI=%u %c %c %c %s IOV-UI=0x%06x FCS=0x%06x ", - gph->sapi, gph->is_cmd ? 'C' : 'R', gph->ack_req ? 'A' : ' ', - gph->is_encrypted ? 'E' : 'U', - gea, iov_ui, gph->fcs); - - if (gph->cmd) - DEBUGPC(DLLC, "CMD=%s ", get_value_string(llc_cmd_strs, gph->cmd)); - - if (gph->data) - DEBUGPC(DLLC, "DATA "); - - DEBUGPC(DLLC, "\n"); -} - -/* parse a GPRS LLC header, also check for invalid frames */ -int gprs_llc_hdr_parse(struct gprs_llc_hdr_parsed *ghp, - uint8_t *llc_hdr, int len) -{ - uint8_t *ctrl = llc_hdr+1; - - if (len <= CRC24_LENGTH) - return -EIO; - - ghp->crc_length = len - CRC24_LENGTH; - - ghp->ack_req = 0; - - /* Section 5.5: FCS */ - ghp->fcs = *(llc_hdr + len - 3); - ghp->fcs |= *(llc_hdr + len - 2) << 8; - ghp->fcs |= *(llc_hdr + len - 1) << 16; - - /* Section 6.2.1: invalid PD field */ - if (llc_hdr[0] & 0x80) - return -EIO; - - /* This only works for the MS->SGSN direction */ - if (llc_hdr[0] & 0x40) - ghp->is_cmd = 0; - else - ghp->is_cmd = 1; - - ghp->sapi = llc_hdr[0] & 0xf; - - /* Section 6.2.3: check for reserved SAPI */ - switch (ghp->sapi) { - case 0: - case 4: - case 6: - case 0xa: - case 0xc: - case 0xd: - case 0xf: - return -EINVAL; - } - - if ((ctrl[0] & 0x80) == 0) { - /* I (Information transfer + Supervisory) format */ - uint8_t k; - - ghp->data = ctrl + 3; - - if (ctrl[0] & 0x40) - ghp->ack_req = 1; - - ghp->seq_tx = (ctrl[0] & 0x1f) << 4; - ghp->seq_tx |= (ctrl[1] >> 4); - - ghp->seq_rx = (ctrl[1] & 0x7) << 6; - ghp->seq_rx |= (ctrl[2] >> 2); - - switch (ctrl[2] & 0x03) { - case 0: - ghp->cmd = GPRS_LLC_RR; - break; - case 1: - ghp->cmd = GPRS_LLC_ACK; - break; - case 2: - ghp->cmd = GPRS_LLC_RNR; - break; - case 3: - ghp->cmd = GPRS_LLC_SACK; - k = ctrl[3] & 0x1f; - ghp->data += 1 + k; - break; - } - ghp->data_len = (llc_hdr + len - 3) - ghp->data; - } else if ((ctrl[0] & 0xc0) == 0x80) { - /* S (Supervisory) format */ - ghp->data = NULL; - ghp->data_len = 0; - - if (ctrl[0] & 0x20) - ghp->ack_req = 1; - ghp->seq_rx = (ctrl[0] & 0x7) << 6; - ghp->seq_rx |= (ctrl[1] >> 2); - - switch (ctrl[1] & 0x03) { - case 0: - ghp->cmd = GPRS_LLC_RR; - break; - case 1: - ghp->cmd = GPRS_LLC_ACK; - break; - case 2: - ghp->cmd = GPRS_LLC_RNR; - break; - case 3: - ghp->cmd = GPRS_LLC_SACK; - break; - } - } else if ((ctrl[0] & 0xe0) == 0xc0) { - /* UI (Unconfirmed Inforamtion) format */ - ghp->cmd = GPRS_LLC_UI; - ghp->data = ctrl + 2; - ghp->data_len = (llc_hdr + len - 3) - ghp->data; - - ghp->seq_tx = (ctrl[0] & 0x7) << 6; - ghp->seq_tx |= (ctrl[1] >> 2); - if (ctrl[1] & 0x02) { - ghp->is_encrypted = 1; - /* FIXME: encryption */ - } - if (ctrl[1] & 0x01) { - /* FCS over hdr + all inf fields */ - } else { - /* FCS over hdr + N202 octets (4) */ - if (ghp->crc_length > UI_HDR_LEN + N202) - ghp->crc_length = UI_HDR_LEN + N202; - } - } else { - /* U (Unnumbered) format: 1 1 1 P/F M4 M3 M2 M1 */ - ghp->data = NULL; - ghp->data_len = 0; - - switch (ctrl[0] & 0xf) { - case GPRS_LLC_U_NULL_CMD: - ghp->cmd = GPRS_LLC_NULL; - break; - case GPRS_LLC_U_DM_RESP: - ghp->cmd = GPRS_LLC_DM; - break; - case GPRS_LLC_U_DISC_CMD: - ghp->cmd = GPRS_LLC_DISC; - break; - case GPRS_LLC_U_UA_RESP: - ghp->cmd = GPRS_LLC_UA; - break; - case GPRS_LLC_U_SABM_CMD: - ghp->cmd = GPRS_LLC_SABM; - break; - case GPRS_LLC_U_FRMR_RESP: - ghp->cmd = GPRS_LLC_FRMR; - break; - case GPRS_LLC_U_XID: - ghp->cmd = GPRS_LLC_XID; - ghp->data = ctrl + 1; - ghp->data_len = (llc_hdr + len - 3) - ghp->data; - break; - default: - return -EIO; - } - } - - /* FIXME: parse sack frame */ - if (ghp->cmd == GPRS_LLC_SACK) { - LOGP(DLLC, LOGL_NOTICE, "Unsupported SACK frame\n"); - return -EIO; - } - - return 0; -} diff --git a/openbsc/src/gprs/gprs_llc_vty.c b/openbsc/src/gprs/gprs_llc_vty.c deleted file mode 100644 index bf34e978..00000000 --- a/openbsc/src/gprs/gprs_llc_vty.c +++ /dev/null @@ -1,116 +0,0 @@ -/* VTY interface for our GPRS LLC implementation */ - -/* (C) 2010 by Harald Welte - * - * All Rights Reserved - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation; either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - * - */ - -#include -#include -#include -#include -#include - -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include - -struct value_string gprs_llc_state_strs[] = { - { GPRS_LLES_UNASSIGNED, "TLLI Unassigned" }, - { GPRS_LLES_ASSIGNED_ADM, "TLLI Assigned" }, - { GPRS_LLES_LOCAL_EST, "Local Establishment" }, - { GPRS_LLES_REMOTE_EST, "Remote Establishment" }, - { GPRS_LLES_ABM, "Asynchronous Balanced Mode" }, - { GPRS_LLES_LOCAL_REL, "Local Release" }, - { GPRS_LLES_TIMER_REC, "Timer Recovery" }, - { 0, NULL } -}; - -static void vty_dump_lle(struct vty *vty, struct gprs_llc_lle *lle) -{ - struct gprs_llc_params *par = &lle->params; - vty_out(vty, " SAPI %2u State %s VUsend=%u, VUrecv=%u", lle->sapi, - get_value_string(gprs_llc_state_strs, lle->state), - lle->vu_send, lle->vu_recv); - vty_out(vty, " Vsent=%u Vack=%u Vrecv=%u, RetransCtr=%u%s", - lle->v_sent, lle->v_ack, lle->v_recv, - lle->retrans_ctr, VTY_NEWLINE); - vty_out(vty, " T200=%u, N200=%u, N201-U=%u, N201-I=%u, mD=%u, " - "mU=%u, kD=%u, kU=%u%s", par->t200_201, par->n200, - par->n201_u, par->n201_i, par->mD, par->mU, par->kD, - par->kU, VTY_NEWLINE); -} - -static uint8_t valid_sapis[] = { 1, 2, 3, 5, 7, 8, 9, 11 }; - -static void vty_dump_llme(struct vty *vty, struct gprs_llc_llme *llme) -{ - unsigned int i; - struct timespec now_tp = {0}; - clock_gettime(CLOCK_MONOTONIC, &now_tp); - - vty_out(vty, "TLLI %08x (Old TLLI %08x) BVCI=%u NSEI=%u %s: " - "IOV-UI=0x%06x CKSN=%d Age=%d: State %s%s", llme->tlli, - llme->old_tlli, llme->bvci, llme->nsei, - get_value_string(gprs_cipher_names, llme->algo), llme->iov_ui, - llme->cksn, llme->age_timestamp == GPRS_LLME_RESET_AGE ? 0 : - (int)(now_tp.tv_sec - (time_t)llme->age_timestamp), - get_value_string(gprs_llc_state_strs, llme->state), VTY_NEWLINE); - - for (i = 0; i < ARRAY_SIZE(valid_sapis); i++) { - struct gprs_llc_lle *lle; - uint8_t sapi = valid_sapis[i]; - - if (sapi >= ARRAY_SIZE(llme->lle)) - continue; - - lle = &llme->lle[sapi]; - vty_dump_lle(vty, lle); - } -} - - -DEFUN(show_llc, show_llc_cmd, - "show llc", - SHOW_STR "Display information about the LLC protocol") -{ - struct gprs_llc_llme *llme; - - vty_out(vty, "State of LLC Entities%s", VTY_NEWLINE); - llist_for_each_entry(llme, &gprs_llc_llmes, list) { - vty_dump_llme(vty, llme); - } - return CMD_SUCCESS; -} - -int gprs_llc_vty_init(void) -{ - install_element_ve(&show_llc_cmd); - - return 0; -} diff --git a/openbsc/src/gprs/gprs_llc_xid.c b/openbsc/src/gprs/gprs_llc_xid.c deleted file mode 100644 index fe631715..00000000 --- a/openbsc/src/gprs/gprs_llc_xid.c +++ /dev/null @@ -1,281 +0,0 @@ -/* GPRS LLC XID field encoding/decoding as per 3GPP TS 44.064 */ - -/* (C) 2016 by sysmocom s.f.m.c. GmbH - * All Rights Reserved - * - * Author: Philipp Maier - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation; either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - */ - -#include -#include -#include -#include - -#include -#include -#include -#include -#include - -#include -#include -#include -#include - -const struct value_string gprs_llc_xid_type_names[] = { - { GPRS_LLC_XID_T_VERSION, "VERSION"}, - { GPRS_LLC_XID_T_IOV_UI, "IOV_UI"}, - { GPRS_LLC_XID_T_IOV_I, "IOV_I"}, - { GPRS_LLC_XID_T_T200, "T200"}, - { GPRS_LLC_XID_T_N200, "N200"}, - { GPRS_LLC_XID_T_N201_U, "N201_"}, - { GPRS_LLC_XID_T_N201_I, "N201_I"}, - { GPRS_LLC_XID_T_mD, "mD"}, - { GPRS_LLC_XID_T_mU, "mU"}, - { GPRS_LLC_XID_T_kD, "kD"}, - { GPRS_LLC_XID_T_kU, "kU"}, - { GPRS_LLC_XID_T_L3_PAR, "L3_PAR"}, - { GPRS_LLC_XID_T_RESET, "RESET"}, - { 0, NULL }, -}; - -/* Parse XID parameter field */ -static int decode_xid_field(struct gprs_llc_xid_field *xid_field, - const uint8_t *src, uint8_t src_len) -{ - uint8_t xl; - uint8_t type; - uint8_t len; - int src_counter = 0; - - /* Exit immediately if it is clear that no - * parseable data is present */ - if (src_len < 1 || !src) - return -EINVAL; - - /* Extract header info */ - xl = (*src >> 7) & 1; - type = (*src >> 2) & 0x1F; - - /* Extract length field */ - len = (*src) & 0x3; - src++; - src_counter++; - if (xl) { - if (src_len < 2) - return -EINVAL; - len = (len << 6) & 0xC0; - len |= ((*src) >> 2) & 0x3F; - src++; - src_counter++; - } - - /* Fill out struct */ - xid_field->type = type; - xid_field->data_len = len; - if (len > 0) { - if (src_len < src_counter + len) - return -EINVAL; - xid_field->data = - talloc_memdup(xid_field,src,xid_field->data_len); - } else - xid_field->data = NULL; - - /* Return consumed length */ - return src_counter + len; -} - -/* Encode XID parameter field */ -static int encode_xid_field(uint8_t *dst, int dst_maxlen, - const struct gprs_llc_xid_field *xid_field) -{ - int xl = 0; - - /* When the length does not fit into 2 bits, - * we need extended length fields */ - if (xid_field->data_len > 3) - xl = 1; - - /* Exit immediately if it is clear that no - * encoding result can be stored */ - if (dst_maxlen < xid_field->data_len + 1 + xl) - return -EINVAL; - - /* There are only 5 bits reserved for the type, exit on exceed */ - if (xid_field->type > 31) - return -EINVAL; - - /* Encode header */ - memset(dst, 0, dst_maxlen); - if (xl) - dst[0] |= 0x80; - dst[0] |= (((xid_field->type) & 0x1F) << 2); - - if (xl) { - dst[0] |= (((xid_field->data_len) >> 6) & 0x03); - dst[1] = ((xid_field->data_len) << 2) & 0xFC; - } else - dst[0] |= ((xid_field->data_len) & 0x03); - - /* Append payload data */ - if (xid_field->data && xid_field->data_len) - memcpy(dst + 1 + xl, xid_field->data, xid_field->data_len); - - /* Return generated length */ - return xid_field->data_len + 1 + xl; -} - -/* Transform a list with XID fields into a XID message (dst) */ -int gprs_llc_compile_xid(uint8_t *dst, int dst_maxlen, - const struct llist_head *xid_fields) -{ - struct gprs_llc_xid_field *xid_field; - int rc; - int byte_counter = 0; - - OSMO_ASSERT(xid_fields); - OSMO_ASSERT(dst); - - llist_for_each_entry_reverse(xid_field, xid_fields, list) { - /* Encode XID-Field */ - rc = encode_xid_field(dst, dst_maxlen, xid_field); - if (rc < 0) - return -EINVAL; - - /* Advance pointer and lower maxlen for the - * next encoding round */ - dst += rc; - byte_counter += rc; - dst_maxlen -= rc; - } - - /* Return generated length */ - return byte_counter; -} - -/* Transform a XID message (dst) into a list of XID fields */ -struct llist_head *gprs_llc_parse_xid(const void *ctx, const uint8_t *src, - int src_len) -{ - struct gprs_llc_xid_field *xid_field; - struct llist_head *xid_fields; - - int rc; - int max_loops = src_len; - - OSMO_ASSERT(src); - - xid_fields = talloc_zero(ctx, struct llist_head); - INIT_LLIST_HEAD(xid_fields); - - while (1) { - /* Bail in case decode_xid_field() constantly returns zero */ - if (max_loops <= 0) { - talloc_free(xid_fields); - return NULL; - } - - /* Decode XID field */ - xid_field = talloc_zero(xid_fields, struct gprs_llc_xid_field); - rc = decode_xid_field(xid_field, src, src_len); - - /* Immediately stop on error */ - if (rc < 0) { - talloc_free(xid_fields); - return NULL; - } - - /* Add parsed XID field to list */ - llist_add(&xid_field->list, xid_fields); - - /* Advance pointer and lower dst_len for the next - * decoding round */ - src += rc; - src_len -= rc; - - /* We are (scuccessfully) done when no further byes are left */ - if (src_len == 0) - return xid_fields; - - max_loops--; - } -} - -/* Create a duplicate of an XID-Field */ -struct gprs_llc_xid_field *gprs_llc_dup_xid_field(const void *ctx, const struct - gprs_llc_xid_field - *xid_field) -{ - struct gprs_llc_xid_field *dup; - - OSMO_ASSERT(xid_field); - - /* Create a copy of the XID field in memory */ - dup = talloc_memdup(ctx, xid_field, sizeof(*xid_field)); - dup->data = talloc_memdup(ctx, xid_field->data, xid_field->data_len); - - /* Unlink duplicate from source list */ - INIT_LLIST_HEAD(&dup->list); - - return dup; -} - -/* Copy an llist with xid fields */ -struct llist_head *gprs_llc_copy_xid(const void *ctx, - const struct llist_head *xid_fields) -{ - struct gprs_llc_xid_field *xid_field; - struct llist_head *xid_fields_copy; - - OSMO_ASSERT(xid_fields); - - xid_fields_copy = talloc_zero(ctx, struct llist_head); - INIT_LLIST_HEAD(xid_fields_copy); - - /* Create duplicates and add them to the target list */ - llist_for_each_entry(xid_field, xid_fields, list) { - llist_add(&gprs_llc_dup_xid_field(ctx, xid_field)->list, - xid_fields_copy); - } - - return xid_fields_copy; -} - -/* Dump a list with XID fields (Debug) */ -void gprs_llc_dump_xid_fields(const struct llist_head *xid_fields, - unsigned int logl) -{ - struct gprs_llc_xid_field *xid_field; - - OSMO_ASSERT(xid_fields); - - llist_for_each_entry(xid_field, xid_fields, list) { - if (xid_field->data_len) { - OSMO_ASSERT(xid_field->data); - LOGP(DLLC, logl, - "XID: type %s, data_len=%d, data=%s\n", - get_value_string(gprs_llc_xid_type_names, - xid_field->type), - xid_field->data_len, - osmo_hexdump_nospc(xid_field->data, - xid_field->data_len)); - } else { - LOGP(DLLC, logl, - "XID: type=%d, data_len=%d, data=NULL\n", - xid_field->type, xid_field->data_len); - } - } -} diff --git a/openbsc/src/gprs/gprs_sgsn.c b/openbsc/src/gprs/gprs_sgsn.c deleted file mode 100644 index 071dd97c..00000000 --- a/openbsc/src/gprs/gprs_sgsn.c +++ /dev/null @@ -1,895 +0,0 @@ -/* GPRS SGSN functionality */ - -/* (C) 2009 by Harald Welte - * - * All Rights Reserved - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation; either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - * - */ - -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include "openbsc/gprs_llc.h" -#include - -#include - -#include - -#include - -#define GPRS_LLME_CHECK_TICK 30 - -extern struct sgsn_instance *sgsn; - -LLIST_HEAD(sgsn_mm_ctxts); -LLIST_HEAD(sgsn_ggsn_ctxts); -LLIST_HEAD(sgsn_apn_ctxts); -LLIST_HEAD(sgsn_pdp_ctxts); - -static const struct rate_ctr_desc mmctx_ctr_description[] = { - { "sign.packets.in", "Signalling Messages ( In)" }, - { "sign.packets.out", "Signalling Messages (Out)" }, - { "udata.packets.in", "User Data Messages ( In)" }, - { "udata.packets.out", "User Data Messages (Out)" }, - { "udata.bytes.in", "User Data Bytes ( In)" }, - { "udata.bytes.out", "User Data Bytes (Out)" }, - { "pdp_ctx_act", "PDP Context Activations " }, - { "suspend", "SUSPEND Count " }, - { "paging.ps", "Paging Packet Switched " }, - { "paging.cs", "Paging Circuit Switched " }, - { "ra_update", "Routing Area Update " }, -}; - -static const struct rate_ctr_group_desc mmctx_ctrg_desc = { - .group_name_prefix = "sgsn.mmctx", - .group_description = "SGSN MM Context Statistics", - .num_ctr = ARRAY_SIZE(mmctx_ctr_description), - .ctr_desc = mmctx_ctr_description, - .class_id = OSMO_STATS_CLASS_SUBSCRIBER, -}; - -static const struct rate_ctr_desc pdpctx_ctr_description[] = { - { "udata.packets.in", "User Data Messages ( In)" }, - { "udata.packets.out", "User Data Messages (Out)" }, - { "udata.bytes.in", "User Data Bytes ( In)" }, - { "udata.bytes.out", "User Data Bytes (Out)" }, -}; - -static const struct rate_ctr_group_desc pdpctx_ctrg_desc = { - .group_name_prefix = "sgsn.pdpctx", - .group_description = "SGSN PDP Context Statistics", - .num_ctr = ARRAY_SIZE(pdpctx_ctr_description), - .ctr_desc = pdpctx_ctr_description, - .class_id = OSMO_STATS_CLASS_SUBSCRIBER, -}; - -static const struct rate_ctr_desc sgsn_ctr_description[] = { - { "llc.dl_bytes", "Count sent LLC bytes before giving it to the bssgp layer" }, - { "llc.ul_bytes", "Count sucessful received LLC bytes (encrypt & fcs correct)" }, - { "llc.dl_packets", "Count sucessful sent LLC packets before giving it to the bssgp layer" }, - { "llc.ul_packets", "Count sucessful received LLC packets (encrypt & fcs correct)" }, - { "gprs.attach_requested", "Received attach requests" }, - { "gprs.attach_accepted", "Sent attach accepts" }, - { "gprs.attach_rejected", "Sent attach rejects" }, - { "gprs.detach_requested", "Received detach requests" }, - { "gprs.detach_acked", "Sent detach acks" }, - { "gprs.routing_area_requested", "Received routing area requests" }, - { "gprs.routing_area_requested", "Sent routing area acks" }, - { "gprs.routing_area_requested", "Sent routing area rejects" }, - { "pdp.activate_requested", "Received activate requests" }, - { "pdp.activate_rejected", "Sent activate rejects" }, - { "pdp.activate_accepted", "Sent activate accepts" }, - { "pdp.request_activated", "unused" }, - { "pdp.request_activate_rejected", "unused" }, - { "pdp.modify_requested", "unused" }, - { "pdp.modify_accepted", "unused" }, - { "pdp.dl_deactivate_requested", "Sent deactivate requests" }, - { "pdp.dl_deactivate_accepted", "Sent deactivate accepted" }, - { "pdp.ul_deactivate_requested", "Received deactivate requests" }, - { "pdp.ul_deactivate_accepted", "Received deactivate accepts" }, -}; - -static const struct rate_ctr_group_desc sgsn_ctrg_desc = { - "sgsn", - "SGSN Overall Statistics", - OSMO_STATS_CLASS_GLOBAL, - ARRAY_SIZE(sgsn_ctr_description), - sgsn_ctr_description, -}; - -void sgsn_rate_ctr_init() { - sgsn->rate_ctrs = rate_ctr_group_alloc(tall_bsc_ctx, &sgsn_ctrg_desc, 0); -} - -/* look-up an SGSN MM context based on Iu UE context (struct ue_conn_ctx)*/ -struct sgsn_mm_ctx *sgsn_mm_ctx_by_ue_ctx(const void *uectx) -{ - struct sgsn_mm_ctx *ctx; - - llist_for_each_entry(ctx, &sgsn_mm_ctxts, list) { - if (ctx->ran_type == MM_CTX_T_UTRAN_Iu - && uectx == ctx->iu.ue_ctx) - return ctx; - } - - return NULL; -} - -/* look-up a SGSN MM context based on TLLI + RAI */ -struct sgsn_mm_ctx *sgsn_mm_ctx_by_tlli(uint32_t tlli, - const struct gprs_ra_id *raid) -{ - struct sgsn_mm_ctx *ctx; - - llist_for_each_entry(ctx, &sgsn_mm_ctxts, list) { - if ((tlli == ctx->gb.tlli || tlli == ctx->gb.tlli_new) && - gprs_ra_id_equals(raid, &ctx->ra)) - return ctx; - } - - return NULL; -} - -struct sgsn_mm_ctx *sgsn_mm_ctx_by_tlli_and_ptmsi(uint32_t tlli, - const struct gprs_ra_id *raid) -{ - struct sgsn_mm_ctx *ctx; - int tlli_type; - - /* TODO: Also check the P_TMSI signature to be safe. That signature - * should be different (at least with a sufficiently high probability) - * after SGSN restarts and for multiple SGSN instances. - */ - - tlli_type = gprs_tlli_type(tlli); - if (tlli_type != TLLI_FOREIGN && tlli_type != TLLI_LOCAL) - return NULL; - - llist_for_each_entry(ctx, &sgsn_mm_ctxts, list) { - if ((gprs_tmsi2tlli(ctx->p_tmsi, tlli_type) == tlli || - gprs_tmsi2tlli(ctx->p_tmsi_old, tlli_type) == tlli) && - gprs_ra_id_equals(raid, &ctx->ra)) - return ctx; - } - - return NULL; -} - -struct sgsn_mm_ctx *sgsn_mm_ctx_by_ptmsi(uint32_t p_tmsi) -{ - struct sgsn_mm_ctx *ctx; - - llist_for_each_entry(ctx, &sgsn_mm_ctxts, list) { - if (p_tmsi == ctx->p_tmsi || - (ctx->p_tmsi_old && ctx->p_tmsi_old == p_tmsi)) - return ctx; - } - return NULL; -} - -struct sgsn_mm_ctx *sgsn_mm_ctx_by_imsi(const char *imsi) -{ - struct sgsn_mm_ctx *ctx; - - llist_for_each_entry(ctx, &sgsn_mm_ctxts, list) { - if (!strcmp(imsi, ctx->imsi)) - return ctx; - } - return NULL; - -} - -/* Allocate a new SGSN MM context for GERAN_Gb */ -struct sgsn_mm_ctx *sgsn_mm_ctx_alloc_gb(uint32_t tlli, - const struct gprs_ra_id *raid) -{ - struct sgsn_mm_ctx *ctx; - - ctx = talloc_zero(tall_bsc_ctx, struct sgsn_mm_ctx); - if (!ctx) - return NULL; - - memcpy(&ctx->ra, raid, sizeof(ctx->ra)); - ctx->ran_type = MM_CTX_T_GERAN_Gb; - ctx->gb.tlli = tlli; - ctx->gmm_state = GMM_DEREGISTERED; - ctx->pmm_state = MM_IDLE; - ctx->auth_triplet.key_seq = GSM_KEY_SEQ_INVAL; - ctx->ciph_algo = sgsn->cfg.cipher; - LOGMMCTXP(LOGL_DEBUG, ctx, "Allocated with %s cipher.\n", - get_value_string(gprs_cipher_names, ctx->ciph_algo)); - ctx->ctrg = rate_ctr_group_alloc(ctx, &mmctx_ctrg_desc, tlli); - INIT_LLIST_HEAD(&ctx->pdp_list); - - llist_add(&ctx->list, &sgsn_mm_ctxts); - - return ctx; -} - -/* Allocate a new SGSN MM context */ -struct sgsn_mm_ctx *sgsn_mm_ctx_alloc_iu(void *uectx) -{ - struct sgsn_mm_ctx *ctx; - - ctx = talloc_zero(tall_bsc_ctx, struct sgsn_mm_ctx); - if (!ctx) - return NULL; - - ctx->ran_type = MM_CTX_T_UTRAN_Iu; - ctx->iu.ue_ctx = uectx; - ctx->iu.new_key = 1; - ctx->gmm_state = GMM_DEREGISTERED; - ctx->pmm_state = PMM_DETACHED; - ctx->auth_triplet.key_seq = GSM_KEY_SEQ_INVAL; - ctx->ctrg = rate_ctr_group_alloc(ctx, &mmctx_ctrg_desc, 0); - - /* Need to get RAID from IU conn */ - ctx->ra = ctx->iu.ue_ctx->ra_id; - - INIT_LLIST_HEAD(&ctx->pdp_list); - - llist_add(&ctx->list, &sgsn_mm_ctxts); - - return ctx; -} - - -/* this is a hard _free_ function, it doesn't clean up the PDP contexts - * in libgtp! */ -static void sgsn_mm_ctx_free(struct sgsn_mm_ctx *mm) -{ - struct sgsn_pdp_ctx *pdp, *pdp2; - - /* Unlink from global list of MM contexts */ - llist_del(&mm->list); - - /* Free all PDP contexts */ - llist_for_each_entry_safe(pdp, pdp2, &mm->pdp_list, list) - sgsn_pdp_ctx_free(pdp); - - rate_ctr_group_free(mm->ctrg); - - talloc_free(mm); -} - -void sgsn_mm_ctx_cleanup_free(struct sgsn_mm_ctx *mm) -{ - struct gprs_llc_llme *llme = NULL; - uint32_t tlli = mm->gb.tlli; - struct sgsn_pdp_ctx *pdp, *pdp2; - struct sgsn_signal_data sig_data; - - if (mm->ran_type == MM_CTX_T_GERAN_Gb) - llme = mm->gb.llme; - else - OSMO_ASSERT(mm->gb.llme == NULL); - - /* Forget about ongoing look-ups */ - if (mm->ggsn_lookup) { - LOGMMCTXP(LOGL_NOTICE, mm, - "Cleaning mmctx with on-going query.\n"); - mm->ggsn_lookup->mmctx = NULL; - mm->ggsn_lookup = NULL; - } - - /* delete all existing PDP contexts for this MS */ - llist_for_each_entry_safe(pdp, pdp2, &mm->pdp_list, list) { - LOGMMCTXP(LOGL_NOTICE, mm, - "Dropping PDP context for NSAPI=%u\n", pdp->nsapi); - sgsn_pdp_ctx_terminate(pdp); - } - - if (osmo_timer_pending(&mm->timer)) { - LOGMMCTXP(LOGL_INFO, mm, "Cancelling MM timer %u\n", mm->T); - osmo_timer_del(&mm->timer); - } - - memset(&sig_data, 0, sizeof(sig_data)); - sig_data.mm = mm; - osmo_signal_dispatch(SS_SGSN, S_SGSN_MM_FREE, &sig_data); - - - /* Detach from subscriber which is possibly freed then */ - if (mm->subscr) { - struct gprs_subscr *subscr = gprs_subscr_get(mm->subscr); - gprs_subscr_cleanup(subscr); - gprs_subscr_put(subscr); - } - - sgsn_mm_ctx_free(mm); - mm = NULL; - - if (llme) { - /* TLLI unassignment, must be called after sgsn_mm_ctx_free */ - gprs_llgmm_assign(llme, tlli, 0xffffffff); - } -} - - -/* look up PDP context by MM context and NSAPI */ -struct sgsn_pdp_ctx *sgsn_pdp_ctx_by_nsapi(const struct sgsn_mm_ctx *mm, - uint8_t nsapi) -{ - struct sgsn_pdp_ctx *pdp; - - llist_for_each_entry(pdp, &mm->pdp_list, list) { - if (pdp->nsapi == nsapi) - return pdp; - } - return NULL; -} - -/* look up PDP context by MM context and transaction ID */ -struct sgsn_pdp_ctx *sgsn_pdp_ctx_by_tid(const struct sgsn_mm_ctx *mm, - uint8_t tid) -{ - struct sgsn_pdp_ctx *pdp; - - llist_for_each_entry(pdp, &mm->pdp_list, list) { - if (pdp->ti == tid) - return pdp; - } - return NULL; -} - -/* you don't want to use this directly, call sgsn_create_pdp_ctx() */ -struct sgsn_pdp_ctx *sgsn_pdp_ctx_alloc(struct sgsn_mm_ctx *mm, - uint8_t nsapi) -{ - struct sgsn_pdp_ctx *pdp; - - pdp = sgsn_pdp_ctx_by_nsapi(mm, nsapi); - if (pdp) - return NULL; - - pdp = talloc_zero(tall_bsc_ctx, struct sgsn_pdp_ctx); - if (!pdp) - return NULL; - - pdp->mm = mm; - pdp->nsapi = nsapi; - pdp->ctrg = rate_ctr_group_alloc(pdp, &pdpctx_ctrg_desc, nsapi); - llist_add(&pdp->list, &mm->pdp_list); - llist_add(&pdp->g_list, &sgsn_pdp_ctxts); - - return pdp; -} - -/* - * This function will not trigger any GSM DEACT PDP ACK messages, so you - * probably want to call sgsn_delete_pdp_ctx() instead if the connection - * isn't detached already. - */ -void sgsn_pdp_ctx_terminate(struct sgsn_pdp_ctx *pdp) -{ - struct sgsn_signal_data sig_data; - - OSMO_ASSERT(pdp->mm != NULL); - - /* There might still be pending callbacks in libgtp. So the parts of - * this object relevant to GTP need to remain intact in this case. */ - - LOGPDPCTXP(LOGL_INFO, pdp, "Forcing release of PDP context\n"); - - if (pdp->mm->ran_type == MM_CTX_T_GERAN_Gb) { - /* Force the deactivation of the SNDCP layer */ - sndcp_sm_deactivate_ind(&pdp->mm->gb.llme->lle[pdp->sapi], pdp->nsapi); - } - - memset(&sig_data, 0, sizeof(sig_data)); - sig_data.pdp = pdp; - osmo_signal_dispatch(SS_SGSN, S_SGSN_PDP_TERMINATE, &sig_data); - - /* Detach from MM context */ - llist_del(&pdp->list); - pdp->mm = NULL; - - sgsn_delete_pdp_ctx(pdp); -} - -/* - * Don't call this function directly unless you know what you are doing. - * In normal conditions use sgsn_delete_pdp_ctx and in unspecified or - * implementation dependent abnormal ones sgsn_pdp_ctx_terminate. - */ -void sgsn_pdp_ctx_free(struct sgsn_pdp_ctx *pdp) -{ - struct sgsn_signal_data sig_data; - - memset(&sig_data, 0, sizeof(sig_data)); - sig_data.pdp = pdp; - osmo_signal_dispatch(SS_SGSN, S_SGSN_PDP_FREE, &sig_data); - - rate_ctr_group_free(pdp->ctrg); - if (pdp->mm) - llist_del(&pdp->list); - llist_del(&pdp->g_list); - - /* _if_ we still have a library handle, at least set it to NULL - * to avoid any dereferences of the now-deleted PDP context from - * sgsn_libgtp:cb_data_ind() */ - if (pdp->lib) { - struct pdp_t *lib = pdp->lib; - LOGPDPCTXP(LOGL_NOTICE, pdp, "freeing PDP context that still " - "has a libgtp handle attached to it, this shouldn't " - "happen!\n"); - osmo_generate_backtrace(); - lib->priv = NULL; - } - - if (pdp->destroy_ggsn) - sgsn_ggsn_ctx_free(pdp->ggsn); - talloc_free(pdp); -} - -/* GGSN contexts */ - -struct sgsn_ggsn_ctx *sgsn_ggsn_ctx_alloc(uint32_t id) -{ - struct sgsn_ggsn_ctx *ggc; - - ggc = talloc_zero(tall_bsc_ctx, struct sgsn_ggsn_ctx); - if (!ggc) - return NULL; - - ggc->id = id; - ggc->gtp_version = 1; - ggc->remote_restart_ctr = -1; - /* if we are called from config file parse, this gsn doesn't exist yet */ - ggc->gsn = sgsn->gsn; - llist_add(&ggc->list, &sgsn_ggsn_ctxts); - - return ggc; -} - -void sgsn_ggsn_ctx_free(struct sgsn_ggsn_ctx *ggc) -{ - llist_del(&ggc->list); - talloc_free(ggc); -} - -struct sgsn_ggsn_ctx *sgsn_ggsn_ctx_by_id(uint32_t id) -{ - struct sgsn_ggsn_ctx *ggc; - - llist_for_each_entry(ggc, &sgsn_ggsn_ctxts, list) { - if (id == ggc->id) - return ggc; - } - return NULL; -} - -struct sgsn_ggsn_ctx *sgsn_ggsn_ctx_by_addr(struct in_addr *addr) -{ - struct sgsn_ggsn_ctx *ggc; - - llist_for_each_entry(ggc, &sgsn_ggsn_ctxts, list) { - if (!memcmp(addr, &ggc->remote_addr, sizeof(*addr))) - return ggc; - } - return NULL; -} - - -struct sgsn_ggsn_ctx *sgsn_ggsn_ctx_find_alloc(uint32_t id) -{ - struct sgsn_ggsn_ctx *ggc; - - ggc = sgsn_ggsn_ctx_by_id(id); - if (!ggc) - ggc = sgsn_ggsn_ctx_alloc(id); - return ggc; -} - -/* APN contexts */ - -static struct apn_ctx *sgsn_apn_ctx_alloc(const char *ap_name, const char *imsi_prefix) -{ - struct apn_ctx *actx; - - actx = talloc_zero(tall_bsc_ctx, struct apn_ctx); - if (!actx) - return NULL; - actx->name = talloc_strdup(actx, ap_name); - actx->imsi_prefix = talloc_strdup(actx, imsi_prefix); - - llist_add_tail(&actx->list, &sgsn_apn_ctxts); - - return actx; -} - -void sgsn_apn_ctx_free(struct apn_ctx *actx) -{ - llist_del(&actx->list); - talloc_free(actx); -} - -struct apn_ctx *sgsn_apn_ctx_match(const char *name, const char *imsi) -{ - struct apn_ctx *actx; - struct apn_ctx *found_actx = NULL; - size_t imsi_prio = 0; - size_t name_prio = 0; - size_t name_req_len = strlen(name); - - llist_for_each_entry(actx, &sgsn_apn_ctxts, list) { - size_t name_ref_len, imsi_ref_len; - const char *name_ref_start, *name_match_start; - - imsi_ref_len = strlen(actx->imsi_prefix); - if (strncmp(actx->imsi_prefix, imsi, imsi_ref_len) != 0) - continue; - - if (imsi_ref_len < imsi_prio) - continue; - - /* IMSI matches */ - - name_ref_start = &actx->name[0]; - if (name_ref_start[0] == '*') { - /* Suffix match */ - name_ref_start += 1; - name_ref_len = strlen(name_ref_start); - if (name_ref_len > name_req_len) - continue; - } else { - name_ref_len = strlen(name_ref_start); - if (name_ref_len != name_req_len) - continue; - } - - name_match_start = name + (name_req_len - name_ref_len); - if (strcasecmp(name_match_start, name_ref_start) != 0) - continue; - - /* IMSI and name match */ - - if (imsi_ref_len == imsi_prio && name_ref_len < name_prio) - /* Lower priority, skip */ - continue; - - imsi_prio = imsi_ref_len; - name_prio = name_ref_len; - found_actx = actx; - } - return found_actx; -} - -struct apn_ctx *sgsn_apn_ctx_by_name(const char *name, const char *imsi_prefix) -{ - struct apn_ctx *actx; - - llist_for_each_entry(actx, &sgsn_apn_ctxts, list) { - if (strcasecmp(name, actx->name) == 0 && - strcasecmp(imsi_prefix, actx->imsi_prefix) == 0) - return actx; - } - return NULL; -} - -struct apn_ctx *sgsn_apn_ctx_find_alloc(const char *name, const char *imsi_prefix) -{ - struct apn_ctx *actx; - - actx = sgsn_apn_ctx_by_name(name, imsi_prefix); - if (!actx) - actx = sgsn_apn_ctx_alloc(name, imsi_prefix); - - return actx; -} - -uint32_t sgsn_alloc_ptmsi(void) -{ - struct sgsn_mm_ctx *mm; - uint32_t ptmsi = 0xdeadbeef; - int max_retries = 100; - -restart: - if (RAND_bytes((uint8_t *) &ptmsi, sizeof(ptmsi)) != 1) - goto failed; - - /* Enforce that the 2 MSB are set without loosing the distance between - * identical values. Since rand() has no duplicate values within a - * period (because the size of the state is the same like the size of - * the random value), this leads to a distance of period/4 when the - * distribution of the 2 MSB is uniform. This approach fails with a - * probability of (3/4)^max_retries, only 1% of the approaches will - * need more than 16 numbers (even distribution assumed). - * - * Alternatively, a freeze list could be used if another PRNG is used - * or when this approach proves to be not sufficient. - */ - if (ptmsi >= 0xC0000000) { - if (!max_retries--) - goto failed; - goto restart; - } - ptmsi |= 0xC0000000; - - if (ptmsi == GSM_RESERVED_TMSI) { - if (!max_retries--) - goto failed; - goto restart; - } - - llist_for_each_entry(mm, &sgsn_mm_ctxts, list) { - if (mm->p_tmsi == ptmsi) { - if (!max_retries--) - goto failed; - goto restart; - } - } - - return ptmsi; - -failed: - LOGP(DGPRS, LOGL_ERROR, "Failed to allocate a P-TMSI\n"); - return GSM_RESERVED_TMSI; -} - -static void drop_one_pdp(struct sgsn_pdp_ctx *pdp) -{ - if (pdp->mm->gmm_state == GMM_REGISTERED_NORMAL) - gsm48_tx_gsm_deact_pdp_req(pdp, GSM_CAUSE_NET_FAIL); - else { - /* FIXME: GPRS paging in case MS is SUSPENDED */ - LOGPDPCTXP(LOGL_NOTICE, pdp, "Hard-dropping PDP ctx due to GGSN " - "recovery\n"); - /* FIXME: how to tell this to libgtp? */ - sgsn_pdp_ctx_free(pdp); - } -} - -/* High-level function to be called in case a GGSN has disappeared or - * otherwise lost state (recovery procedure) */ -int drop_all_pdp_for_ggsn(struct sgsn_ggsn_ctx *ggsn) -{ - struct sgsn_mm_ctx *mm; - int num = 0; - - llist_for_each_entry(mm, &sgsn_mm_ctxts, list) { - struct sgsn_pdp_ctx *pdp; - llist_for_each_entry(pdp, &mm->pdp_list, list) { - if (pdp->ggsn == ggsn) { - drop_one_pdp(pdp); - num++; - } - } - } - - return num; -} - -void sgsn_update_subscriber_data(struct sgsn_mm_ctx *mmctx) -{ - OSMO_ASSERT(mmctx != NULL); - LOGMMCTXP(LOGL_INFO, mmctx, "Subscriber data update\n"); - - sgsn_auth_update(mmctx); -} - -static void insert_qos(struct tlv_parsed *tp, struct sgsn_subscriber_pdp_data *pdp) -{ - tp->lv[OSMO_IE_GSM_SUB_QOS].len = pdp->qos_subscribed_len; - tp->lv[OSMO_IE_GSM_SUB_QOS].val = pdp->qos_subscribed; -} - -/** - * The tlv_parsed tp parameter will be modified to insert a - * OSMO_IE_GSM_SUB_QOS in case the data is available in the - * PDP context handling. - */ -struct sgsn_ggsn_ctx *sgsn_mm_ctx_find_ggsn_ctx(struct sgsn_mm_ctx *mmctx, - struct tlv_parsed *tp, - enum gsm48_gsm_cause *gsm_cause, - char *out_apn_str) -{ - char req_apn_str[GSM_APN_LENGTH] = {0}; - const struct apn_ctx *apn_ctx = NULL; - const char *selected_apn_str = NULL; - struct sgsn_subscriber_pdp_data *pdp; - struct sgsn_ggsn_ctx *ggsn = NULL; - int allow_any_apn = 0; - - out_apn_str[0] = '\0'; - - if (TLVP_PRESENT(tp, GSM48_IE_GSM_APN)) { - if (TLVP_LEN(tp, GSM48_IE_GSM_APN) >= GSM_APN_LENGTH - 1) { - LOGMMCTXP(LOGL_ERROR, mmctx, "APN IE too long\n"); - *gsm_cause = GSM_CAUSE_INV_MAND_INFO; - return NULL; - } - - gprs_apn_to_str(req_apn_str, - TLVP_VAL(tp, GSM48_IE_GSM_APN), - TLVP_LEN(tp, GSM48_IE_GSM_APN)); - - if (strcmp(req_apn_str, "*") == 0) - req_apn_str[0] = 0; - } - - if (mmctx->subscr == NULL) - allow_any_apn = 1; - - if (strlen(req_apn_str) == 0 && !allow_any_apn) { - /* No specific APN requested, check for an APN that is both - * granted and configured */ - - llist_for_each_entry(pdp, &mmctx->subscr->sgsn_data->pdp_list, list) { - if (strcmp(pdp->apn_str, "*") == 0) - { - allow_any_apn = 1; - selected_apn_str = ""; - insert_qos(tp, pdp); - continue; - } - if (!llist_empty(&sgsn_apn_ctxts)) { - apn_ctx = sgsn_apn_ctx_match(req_apn_str, mmctx->imsi); - /* Not configured */ - if (apn_ctx == NULL) - continue; - } - insert_qos(tp, pdp); - selected_apn_str = pdp->apn_str; - break; - } - } else if (!allow_any_apn) { - /* Check whether the given APN is granted */ - llist_for_each_entry(pdp, &mmctx->subscr->sgsn_data->pdp_list, list) { - if (strcmp(pdp->apn_str, "*") == 0) { - insert_qos(tp, pdp); - selected_apn_str = req_apn_str; - allow_any_apn = 1; - continue; - } - if (strcasecmp(pdp->apn_str, req_apn_str) == 0) { - insert_qos(tp, pdp); - selected_apn_str = req_apn_str; - break; - } - } - } else if (strlen(req_apn_str) != 0) { - /* Any APN is allowed */ - selected_apn_str = req_apn_str; - } else { - /* Prefer the GGSN associated with the wildcard APN */ - selected_apn_str = ""; - } - - if (!allow_any_apn && selected_apn_str == NULL) { - /* Access not granted */ - LOGMMCTXP(LOGL_NOTICE, mmctx, - "The requested APN '%s' is not allowed\n", - req_apn_str); - *gsm_cause = GSM_CAUSE_REQ_SERV_OPT_NOTSUB; - return NULL; - } - - /* copy the selected apn_str */ - if (selected_apn_str) - strcpy(out_apn_str, selected_apn_str); - else - out_apn_str[0] = '\0'; - - if (apn_ctx == NULL && selected_apn_str) - apn_ctx = sgsn_apn_ctx_match(selected_apn_str, mmctx->imsi); - - if (apn_ctx != NULL) { - ggsn = apn_ctx->ggsn; - } else if (llist_empty(&sgsn_apn_ctxts)) { - /* No configuration -> use GGSN 0 */ - ggsn = sgsn_ggsn_ctx_by_id(0); - } else if (allow_any_apn && - (selected_apn_str == NULL || strlen(selected_apn_str) == 0)) { - /* No APN given and no default configuration -> Use GGSN 0 */ - ggsn = sgsn_ggsn_ctx_by_id(0); - } else { - /* No matching configuration found */ - LOGMMCTXP(LOGL_NOTICE, mmctx, - "The selected APN '%s' has not been configured\n", - selected_apn_str); - *gsm_cause = GSM_CAUSE_MISSING_APN; - return NULL; - } - - if (!ggsn) { - LOGMMCTXP(LOGL_NOTICE, mmctx, - "No static GGSN configured. Selected APN '%s'\n", - selected_apn_str); - return NULL; - } - - LOGMMCTXP(LOGL_INFO, mmctx, - "Found GGSN %d for APN '%s' (requested '%s')\n", - ggsn->id, selected_apn_str ? selected_apn_str : "---", - req_apn_str); - - return ggsn; -} - -static void sgsn_llme_cleanup_free(struct gprs_llc_llme *llme) -{ - struct sgsn_mm_ctx *mmctx = NULL; - - llist_for_each_entry(mmctx, &sgsn_mm_ctxts, list) { - if (llme == mmctx->gb.llme) { - gsm0408_gprs_access_cancelled(mmctx, SGSN_ERROR_CAUSE_NONE); - return; - } - } - - /* No MM context found */ - LOGP(DGPRS, LOGL_INFO, "Deleting orphaned LLME, TLLI 0x%08x\n", - llme->tlli); - gprs_llgmm_unassign(llme); -} - -static void sgsn_llme_check_cb(void *data_) -{ - struct gprs_llc_llme *llme, *llme_tmp; - struct timespec now_tp; - time_t now, age; - time_t max_age = gprs_max_time_to_idle(); - - int rc; - - rc = clock_gettime(CLOCK_MONOTONIC, &now_tp); - OSMO_ASSERT(rc >= 0); - now = now_tp.tv_sec; - - LOGP(DGPRS, LOGL_DEBUG, - "Checking for inactive LLMEs, time = %u\n", (unsigned)now); - - llist_for_each_entry_safe(llme, llme_tmp, &gprs_llc_llmes, list) { - if (llme->age_timestamp == GPRS_LLME_RESET_AGE) - llme->age_timestamp = now; - - age = now - llme->age_timestamp; - - if (age > max_age || age < 0) { - LOGP(DGPRS, LOGL_INFO, - "Inactivity timeout for TLLI 0x%08x, age %d\n", - llme->tlli, (int)age); - sgsn_llme_cleanup_free(llme); - } - } - - osmo_timer_schedule(&sgsn->llme_timer, GPRS_LLME_CHECK_TICK, 0); -} - -void sgsn_inst_init() -{ - osmo_timer_setup(&sgsn->llme_timer, sgsn_llme_check_cb, NULL); - osmo_timer_schedule(&sgsn->llme_timer, GPRS_LLME_CHECK_TICK, 0); -} - diff --git a/openbsc/src/gprs/gprs_sndcp.c b/openbsc/src/gprs/gprs_sndcp.c deleted file mode 100644 index a18998f9..00000000 --- a/openbsc/src/gprs/gprs_sndcp.c +++ /dev/null @@ -1,1258 +0,0 @@ -/* GPRS SNDCP protocol implementation as per 3GPP TS 04.65 */ - -/* (C) 2010 by Harald Welte - * (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 - -#define DEBUG_IP_PACKETS 0 /* 0=Disabled, 1=Enabled */ - -#if DEBUG_IP_PACKETS == 1 -/* Calculate TCP/IP checksum */ -static uint16_t calc_ip_csum(uint8_t *data, int len) -{ - int i; - uint32_t accumulator = 0; - uint16_t *pointer = (uint16_t *) data; - - for (i = len; i > 1; i -= 2) { - accumulator += *pointer; - pointer++; - } - - if (len % 2) - accumulator += *pointer; - - accumulator = (accumulator & 0xffff) + ((accumulator >> 16) & 0xffff); - accumulator += (accumulator >> 16) & 0xffff; - return (~accumulator); -} - -/* Calculate TCP/IP checksum */ -static uint16_t calc_tcpip_csum(const void *ctx, uint8_t *packet, int len) -{ - uint8_t *buf; - uint16_t csum; - - buf = talloc_zero_size(ctx, len); - memset(buf, 0, len); - memcpy(buf, packet + 12, 8); - buf[9] = packet[9]; - buf[11] = (len - 20) & 0xFF; - buf[10] = (len - 20) >> 8 & 0xFF; - memcpy(buf + 12, packet + 20, len - 20); - csum = calc_ip_csum(buf, len - 20 + 12); - talloc_free(buf); - return csum; -} - -/* Show some ip packet details */ -static void debug_ip_packet(uint8_t *data, int len, int dir, char *info) -{ - uint8_t tcp_flags; - char flags_debugmsg[256]; - int len_short; - static unsigned int packet_count = 0; - static unsigned int tcp_csum_err_count = 0; - static unsigned int ip_csum_err_count = 0; - - packet_count++; - - if (len > 80) - len_short = 80; - else - len_short = len; - - if (dir) - DEBUGP(DSNDCP, "%s: MS => SGSN: %s\n", info, - osmo_hexdump_nospc(data, len_short)); - else - DEBUGP(DSNDCP, "%s: MS <= SGSN: %s\n", info, - osmo_hexdump_nospc(data, len_short)); - - DEBUGP(DSNDCP, "%s: Length.: %d\n", info, len); - DEBUGP(DSNDCP, "%s: NO.: %d\n", info, packet_count); - - if (len < 20) { - DEBUGP(DSNDCP, "%s: Error: Short IP packet!\n", info); - return; - } - - if (calc_ip_csum(data, 20) != 0) { - DEBUGP(DSNDCP, "%s: Bad IP-Header checksum!\n", info); - ip_csum_err_count++; - } else - DEBUGP(DSNDCP, "%s: IP-Header checksum ok.\n", info); - - if (data[9] == 0x06) { - if (len < 40) { - DEBUGP(DSNDCP, "%s: Error: Short TCP packet!\n", info); - return; - } - - DEBUGP(DSNDCP, "%s: Protocol type: TCP\n", info); - tcp_flags = data[33]; - - if (calc_tcpip_csum(NULL, data, len) != 0) { - DEBUGP(DSNDCP, "%s: Bad TCP checksum!\n", info); - tcp_csum_err_count++; - } else - DEBUGP(DSNDCP, "%s: TCP checksum ok.\n", info); - - memset(flags_debugmsg, 0, sizeof(flags_debugmsg)); - if (tcp_flags & 1) - strcat(flags_debugmsg, "FIN "); - if (tcp_flags & 2) - strcat(flags_debugmsg, "SYN "); - if (tcp_flags & 4) - strcat(flags_debugmsg, "RST "); - if (tcp_flags & 8) - strcat(flags_debugmsg, "PSH "); - if (tcp_flags & 16) - strcat(flags_debugmsg, "ACK "); - if (tcp_flags & 32) - strcat(flags_debugmsg, "URG "); - DEBUGP(DSNDCP, "%s: FLAGS: %s\n", info, flags_debugmsg); - } else if (data[9] == 0x11) { - DEBUGP(DSNDCP, "%s: Protocol type: UDP\n", info); - } else { - DEBUGP(DSNDCP, "%s: Protocol type: (%02x)\n", info, data[9]); - } - - DEBUGP(DSNDCP, "%s: IP-Header checksum errors: %d\n", info, - ip_csum_err_count); - DEBUGP(DSNDCP, "%s: TCP-Checksum errors: %d\n", info, - tcp_csum_err_count); -} -#endif - -/* Chapter 7.2: SN-PDU Formats */ -struct sndcp_common_hdr { - /* octet 1 */ - uint8_t nsapi:4; - uint8_t more:1; - uint8_t type:1; - uint8_t first:1; - uint8_t spare:1; -} __attribute__((packed)); - -/* PCOMP / DCOMP only exist in first fragment */ -struct sndcp_comp_hdr { - /* octet 2 */ - uint8_t pcomp:4; - uint8_t dcomp:4; -} __attribute__((packed)); - -struct sndcp_udata_hdr { - /* octet 3 */ - uint8_t npdu_high:4; - uint8_t seg_nr:4; - /* octet 4 */ - uint8_t npdu_low; -} __attribute__((packed)); - - -static void *tall_sndcp_ctx; - -/* A fragment queue entry, containing one framgent of a N-PDU */ -struct defrag_queue_entry { - struct llist_head list; - /* segment number of this fragment */ - uint32_t seg_nr; - /* length of the data area of this fragment */ - uint32_t data_len; - /* pointer to the data of this fragment */ - uint8_t *data; -}; - -LLIST_HEAD(gprs_sndcp_entities); - -/* Check if any compression parameters are set in the sgsn configuration */ -static inline int any_pcomp_or_dcomp_active(struct sgsn_instance *sgsn) { - if (sgsn->cfg.pcomp_rfc1144.active || sgsn->cfg.pcomp_rfc1144.passive || - sgsn->cfg.dcomp_v42bis.active || sgsn->cfg.dcomp_v42bis.passive) - return true; - else - return false; -} - -/* Enqueue a fragment into the defragment queue */ -static int defrag_enqueue(struct gprs_sndcp_entity *sne, uint8_t seg_nr, - uint8_t *data, uint32_t data_len) -{ - struct defrag_queue_entry *dqe; - - dqe = talloc_zero(tall_sndcp_ctx, struct defrag_queue_entry); - if (!dqe) - return -ENOMEM; - dqe->data = talloc_zero_size(dqe, data_len); - if (!dqe->data) { - talloc_free(dqe); - return -ENOMEM; - } - dqe->seg_nr = seg_nr; - dqe->data_len = data_len; - - llist_add(&dqe->list, &sne->defrag.frag_list); - - if (seg_nr > sne->defrag.highest_seg) - sne->defrag.highest_seg = seg_nr; - - sne->defrag.seg_have |= (1 << seg_nr); - sne->defrag.tot_len += data_len; - - memcpy(dqe->data, data, data_len); - - return 0; -} - -/* return if we have all segments of this N-PDU */ -static int defrag_have_all_segments(struct gprs_sndcp_entity *sne) -{ - uint32_t seg_needed = 0; - unsigned int i; - - /* create a bitmask of needed segments */ - for (i = 0; i <= sne->defrag.highest_seg; i++) - seg_needed |= (1 << i); - - if (seg_needed == sne->defrag.seg_have) - return 1; - - return 0; -} - -static struct defrag_queue_entry *defrag_get_seg(struct gprs_sndcp_entity *sne, - uint32_t seg_nr) -{ - struct defrag_queue_entry *dqe; - - llist_for_each_entry(dqe, &sne->defrag.frag_list, list) { - if (dqe->seg_nr == seg_nr) { - llist_del(&dqe->list); - return dqe; - } - } - return NULL; -} - -/* Perform actual defragmentation and create an output packet */ -static int defrag_segments(struct gprs_sndcp_entity *sne) -{ - struct msgb *msg; - unsigned int seg_nr; - uint8_t *npdu; - int npdu_len; - int rc; - uint8_t *expnd = NULL; - - LOGP(DSNDCP, LOGL_DEBUG, "TLLI=0x%08x NSAPI=%u: Defragment output PDU %u " - "num_seg=%u tot_len=%u\n", sne->lle->llme->tlli, sne->nsapi, - sne->defrag.npdu, sne->defrag.highest_seg, sne->defrag.tot_len); - msg = msgb_alloc_headroom(sne->defrag.tot_len+256, 128, "SNDCP Defrag"); - if (!msg) - return -ENOMEM; - - /* FIXME: message headers + identifiers */ - - npdu = msg->data; - - for (seg_nr = 0; seg_nr <= sne->defrag.highest_seg; seg_nr++) { - struct defrag_queue_entry *dqe; - uint8_t *data; - - dqe = defrag_get_seg(sne, seg_nr); - if (!dqe) { - LOGP(DSNDCP, LOGL_ERROR, "Segment %u missing\n", seg_nr); - msgb_free(msg); - return -EIO; - } - /* actually append the segment to the N-PDU */ - data = msgb_put(msg, dqe->data_len); - memcpy(data, dqe->data, dqe->data_len); - - /* release memory for the fragment queue entry */ - talloc_free(dqe); - } - - npdu_len = sne->defrag.tot_len; - - /* FIXME: cancel timer */ - - /* actually send the N-PDU to the SGSN core code, which then - * hands it off to the correct GTP tunnel + GGSN via gtp_data_req() */ - - /* Decompress packet */ -#if DEBUG_IP_PACKETS == 1 - DEBUGP(DSNDCP, " \n"); - DEBUGP(DSNDCP, ":::::::::::::::::::::::::::::::::::::::::::::::::::\n"); - DEBUGP(DSNDCP, "===================================================\n"); -#endif - if (any_pcomp_or_dcomp_active(sgsn)) { - - expnd = talloc_zero_size(msg, npdu_len * MAX_DATADECOMPR_FAC + - MAX_HDRDECOMPR_INCR); - memcpy(expnd, npdu, npdu_len); - - /* Apply data decompression */ - rc = gprs_sndcp_dcomp_expand(expnd, npdu_len, sne->defrag.dcomp, - sne->defrag.data); - if (rc < 0) { - LOGP(DSNDCP, LOGL_ERROR, - "Data decompression failed!\n"); - talloc_free(expnd); - return -EIO; - } - - /* Apply header decompression */ - rc = gprs_sndcp_pcomp_expand(expnd, rc, sne->defrag.pcomp, - sne->defrag.proto); - if (rc < 0) { - LOGP(DSNDCP, LOGL_ERROR, - "TCP/IP Header decompression failed!\n"); - talloc_free(expnd); - return -EIO; - } - - /* Modify npu length, expnd is handed directly handed - * over to gsn_rx_sndcp_ud_ind(), see below */ - npdu_len = rc; - } else - expnd = npdu; -#if DEBUG_IP_PACKETS == 1 - debug_ip_packet(expnd, npdu_len, 1, "defrag_segments()"); - DEBUGP(DSNDCP, "===================================================\n"); - DEBUGP(DSNDCP, ":::::::::::::::::::::::::::::::::::::::::::::::::::\n"); - DEBUGP(DSNDCP, " \n"); -#endif - - /* Hand off packet to gtp */ - rc = sgsn_rx_sndcp_ud_ind(&sne->ra_id, sne->lle->llme->tlli, - sne->nsapi, msg, npdu_len, expnd); - - if (any_pcomp_or_dcomp_active(sgsn)) - talloc_free(expnd); - - return rc; -} - -static int defrag_input(struct gprs_sndcp_entity *sne, struct msgb *msg, - uint8_t *hdr, unsigned int len) -{ - struct sndcp_common_hdr *sch; - struct sndcp_udata_hdr *suh; - uint16_t npdu_num; - uint8_t *data; - int rc; - - sch = (struct sndcp_common_hdr *) hdr; - if (sch->first) { - suh = (struct sndcp_udata_hdr *) (hdr + 1 + sizeof(struct sndcp_common_hdr)); - } else - suh = (struct sndcp_udata_hdr *) (hdr + sizeof(struct sndcp_common_hdr)); - - data = (uint8_t *)suh + sizeof(struct sndcp_udata_hdr); - - npdu_num = (suh->npdu_high << 8) | suh->npdu_low; - - LOGP(DSNDCP, LOGL_DEBUG, "TLLI=0x%08x NSAPI=%u: Input PDU %u Segment %u " - "Length %u %s %s\n", sne->lle->llme->tlli, sne->nsapi, npdu_num, - suh->seg_nr, len, sch->first ? "F " : "", sch->more ? "M" : ""); - - if (sch->first) { - /* first segment of a new packet. Discard all leftover fragments of - * previous packet */ - if (!llist_empty(&sne->defrag.frag_list)) { - struct defrag_queue_entry *dqe, *dqe2; - LOGP(DSNDCP, LOGL_INFO, "TLLI=0x%08x NSAPI=%u: Dropping " - "SN-PDU %u due to insufficient segments (%04x)\n", - sne->lle->llme->tlli, sne->nsapi, sne->defrag.npdu, - sne->defrag.seg_have); - llist_for_each_entry_safe(dqe, dqe2, &sne->defrag.frag_list, list) { - llist_del(&dqe->list); - talloc_free(dqe); - } - } - /* store the currently de-fragmented PDU number */ - sne->defrag.npdu = npdu_num; - - /* Re-set fragmentation state */ - sne->defrag.no_more = sne->defrag.highest_seg = sne->defrag.seg_have = 0; - sne->defrag.tot_len = 0; - /* FIXME: (re)start timer */ - } - - if (sne->defrag.npdu != npdu_num) { - LOGP(DSNDCP, LOGL_INFO, "Segment for different SN-PDU " - "(%u != %u)\n", npdu_num, sne->defrag.npdu); - /* FIXME */ - } - - /* FIXME: check if seg_nr already exists */ - /* make sure to subtract length of SNDCP header from 'len' */ - rc = defrag_enqueue(sne, suh->seg_nr, data, len - (data - hdr)); - if (rc < 0) - return rc; - - if (!sch->more) { - /* this is suppsed to be the last segment of the N-PDU, but it - * might well be not the last to arrive */ - sne->defrag.no_more = 1; - } - - if (sne->defrag.no_more) { - /* we have already received the last segment before, let's check - * if all the previous segments exist */ - if (defrag_have_all_segments(sne)) - return defrag_segments(sne); - } - - return 0; -} - -static struct gprs_sndcp_entity *gprs_sndcp_entity_by_lle(const struct gprs_llc_lle *lle, - uint8_t nsapi) -{ - struct gprs_sndcp_entity *sne; - - llist_for_each_entry(sne, &gprs_sndcp_entities, list) { - if (sne->lle == lle && sne->nsapi == nsapi) - return sne; - } - return NULL; -} - -static struct gprs_sndcp_entity *gprs_sndcp_entity_alloc(struct gprs_llc_lle *lle, - uint8_t nsapi) -{ - struct gprs_sndcp_entity *sne; - - sne = talloc_zero(tall_sndcp_ctx, struct gprs_sndcp_entity); - if (!sne) - return NULL; - - sne->lle = lle; - sne->nsapi = nsapi; - sne->defrag.timer.data = sne; - //sne->fqueue.timer.cb = FIXME; - sne->rx_state = SNDCP_RX_S_FIRST; - INIT_LLIST_HEAD(&sne->defrag.frag_list); - - llist_add(&sne->list, &gprs_sndcp_entities); - - return sne; -} - -/* Entry point for the SNSM-ACTIVATE.indication */ -int sndcp_sm_activate_ind(struct gprs_llc_lle *lle, uint8_t nsapi) -{ - LOGP(DSNDCP, LOGL_INFO, "SNSM-ACTIVATE.ind (lle=%p TLLI=%08x, " - "SAPI=%u, NSAPI=%u)\n", lle, lle->llme->tlli, lle->sapi, nsapi); - - if (gprs_sndcp_entity_by_lle(lle, nsapi)) { - LOGP(DSNDCP, LOGL_ERROR, "Trying to ACTIVATE " - "already-existing entity (TLLI=%08x, NSAPI=%u)\n", - lle->llme->tlli, nsapi); - return -EEXIST; - } - - if (!gprs_sndcp_entity_alloc(lle, nsapi)) { - LOGP(DSNDCP, LOGL_ERROR, "Out of memory during ACTIVATE\n"); - return -ENOMEM; - } - - return 0; -} - -/* Entry point for the SNSM-DEACTIVATE.indication */ -int sndcp_sm_deactivate_ind(struct gprs_llc_lle *lle, uint8_t nsapi) -{ - struct gprs_sndcp_entity *sne; - - LOGP(DSNDCP, LOGL_INFO, "SNSM-DEACTIVATE.ind (lle=%p, TLLI=%08x, " - "SAPI=%u, NSAPI=%u)\n", lle, lle->llme->tlli, lle->sapi, nsapi); - - sne = gprs_sndcp_entity_by_lle(lle, nsapi); - if (!sne) { - LOGP(DSNDCP, LOGL_ERROR, "SNSM-DEACTIVATE.ind for non-" - "existing TLLI=%08x SAPI=%u NSAPI=%u\n", lle->llme->tlli, - lle->sapi, nsapi); - return -ENOENT; - } - llist_del(&sne->list); - /* frag queue entries are hierarchically allocated, so no need to - * free them explicitly here */ - talloc_free(sne); - - return 0; -} - -/* Fragmenter state */ -struct sndcp_frag_state { - uint8_t frag_nr; - struct msgb *msg; /* original message */ - uint8_t *next_byte; /* first byte of next fragment */ - - struct gprs_sndcp_entity *sne; - void *mmcontext; -}; - -/* returns '1' if there are more fragments to send, '0' if none */ -static int sndcp_send_ud_frag(struct sndcp_frag_state *fs, - uint8_t pcomp, uint8_t dcomp) -{ - struct gprs_sndcp_entity *sne = fs->sne; - struct gprs_llc_lle *lle = sne->lle; - struct sndcp_common_hdr *sch; - struct sndcp_comp_hdr *scomph; - struct sndcp_udata_hdr *suh; - struct msgb *fmsg; - unsigned int max_payload_len; - unsigned int len; - uint8_t *data; - int rc, more; - - fmsg = msgb_alloc_headroom(fs->sne->lle->params.n201_u+256, 128, - "SNDCP Frag"); - if (!fmsg) { - msgb_free(fs->msg); - return -ENOMEM; - } - - /* make sure lower layers route the fragment like the original */ - msgb_tlli(fmsg) = msgb_tlli(fs->msg); - msgb_bvci(fmsg) = msgb_bvci(fs->msg); - msgb_nsei(fmsg) = msgb_nsei(fs->msg); - - /* prepend common SNDCP header */ - sch = (struct sndcp_common_hdr *) msgb_put(fmsg, sizeof(*sch)); - sch->nsapi = sne->nsapi; - /* Set FIRST bit if we are the first fragment in a series */ - if (fs->frag_nr == 0) - sch->first = 1; - sch->type = 1; - - /* append the compression header for first fragment */ - if (sch->first) { - scomph = (struct sndcp_comp_hdr *) - msgb_put(fmsg, sizeof(*scomph)); - scomph->pcomp = pcomp; - scomph->dcomp = dcomp; - } - - /* append the user-data header */ - suh = (struct sndcp_udata_hdr *) msgb_put(fmsg, sizeof(*suh)); - suh->npdu_low = sne->tx_npdu_nr & 0xff; - suh->npdu_high = (sne->tx_npdu_nr >> 8) & 0xf; - suh->seg_nr = fs->frag_nr % 0xf; - - /* calculate remaining length to be sent */ - len = (fs->msg->data + fs->msg->len) - fs->next_byte; - /* how much payload can we actually send via LLC? */ - max_payload_len = lle->params.n201_u - (sizeof(*sch) + sizeof(*suh)); - if (sch->first) - max_payload_len -= sizeof(*scomph); - /* check if we're exceeding the max */ - if (len > max_payload_len) - len = max_payload_len; - - /* copy the actual fragment data into our fmsg */ - data = msgb_put(fmsg, len); - memcpy(data, fs->next_byte, len); - - /* Increment fragment number and data pointer to next fragment */ - fs->frag_nr++; - fs->next_byte += len; - - /* determine if we have more fragemnts to send */ - if ((fs->msg->data + fs->msg->len) <= fs->next_byte) - more = 0; - else - more = 1; - - /* set the MORE bit of the SNDCP header accordingly */ - sch->more = more; - - rc = gprs_llc_tx_ui(fmsg, lle->sapi, 0, fs->mmcontext, true); - /* abort in case of error, do not advance frag_nr / next_byte */ - if (rc < 0) { - msgb_free(fs->msg); - return rc; - } - - if (!more) { - /* we've sent all fragments */ - msgb_free(fs->msg); - memset(fs, 0, sizeof(*fs)); - /* increment NPDU number for next frame */ - sne->tx_npdu_nr = (sne->tx_npdu_nr + 1) % 0xfff; - return 0; - } - - /* default: more fragments to send */ - return 1; -} - -/* Request transmission of a SN-PDU over specified LLC Entity + SAPI */ -int sndcp_unitdata_req(struct msgb *msg, struct gprs_llc_lle *lle, uint8_t nsapi, - void *mmcontext) -{ - struct gprs_sndcp_entity *sne; - struct sndcp_common_hdr *sch; - struct sndcp_comp_hdr *scomph; - struct sndcp_udata_hdr *suh; - struct sndcp_frag_state fs; - uint8_t pcomp = 0; - uint8_t dcomp = 0; - int rc; - - /* Identifiers from UP: (TLLI, SAPI) + (BVCI, NSEI) */ - - /* Compress packet */ -#if DEBUG_IP_PACKETS == 1 - DEBUGP(DSNDCP, " \n"); - DEBUGP(DSNDCP, ":::::::::::::::::::::::::::::::::::::::::::::::::::\n"); - DEBUGP(DSNDCP, "===================================================\n"); - debug_ip_packet(msg->data, msg->len, 0, "sndcp_initdata_req()"); -#endif - if (any_pcomp_or_dcomp_active(sgsn)) { - - /* Apply header compression */ - rc = gprs_sndcp_pcomp_compress(msg->data, msg->len, &pcomp, - lle->llme->comp.proto, nsapi); - if (rc < 0) { - LOGP(DSNDCP, LOGL_ERROR, - "TCP/IP Header compression failed!\n"); - return -EIO; - } - - /* Fixup pointer locations and sizes in message buffer to match - * the new, compressed buffer size */ - msgb_get(msg, msg->len); - msgb_put(msg, rc); - - /* Apply data compression */ - rc = gprs_sndcp_dcomp_compress(msg->data, msg->len, &dcomp, - lle->llme->comp.data, nsapi); - if (rc < 0) { - LOGP(DSNDCP, LOGL_ERROR, "Data compression failed!\n"); - return -EIO; - } - - /* Fixup pointer locations and sizes in message buffer to match - * the new, compressed buffer size */ - msgb_get(msg, msg->len); - msgb_put(msg, rc); - } -#if DEBUG_IP_PACKETS == 1 - DEBUGP(DSNDCP, "===================================================\n"); - DEBUGP(DSNDCP, ":::::::::::::::::::::::::::::::::::::::::::::::::::\n"); - DEBUGP(DSNDCP, " \n"); -#endif - - sne = gprs_sndcp_entity_by_lle(lle, nsapi); - if (!sne) { - LOGP(DSNDCP, LOGL_ERROR, "Cannot find SNDCP Entity\n"); - msgb_free(msg); - return -EIO; - } - - /* Check if we need to fragment this N-PDU into multiple SN-PDUs */ - if (msg->len > lle->params.n201_u - - (sizeof(*sch) + sizeof(*suh) + sizeof(*scomph))) { - /* initialize the fragmenter state */ - fs.msg = msg; - fs.frag_nr = 0; - fs.next_byte = msg->data; - fs.sne = sne; - fs.mmcontext = mmcontext; - - /* call function to generate and send fragments until all - * of the N-PDU has been sent */ - while (1) { - int rc = sndcp_send_ud_frag(&fs,pcomp,dcomp); - if (rc == 0) - return 0; - if (rc < 0) - return rc; - } - /* not reached */ - return 0; - } - - /* this is the non-fragmenting case where we only build 1 SN-PDU */ - - /* prepend the user-data header */ - suh = (struct sndcp_udata_hdr *) msgb_push(msg, sizeof(*suh)); - suh->npdu_low = sne->tx_npdu_nr & 0xff; - suh->npdu_high = (sne->tx_npdu_nr >> 8) & 0xf; - suh->seg_nr = 0; - sne->tx_npdu_nr = (sne->tx_npdu_nr + 1) % 0xfff; - - scomph = (struct sndcp_comp_hdr *) msgb_push(msg, sizeof(*scomph)); - scomph->pcomp = pcomp; - scomph->dcomp = dcomp; - - /* prepend common SNDCP header */ - sch = (struct sndcp_common_hdr *) msgb_push(msg, sizeof(*sch)); - sch->first = 1; - sch->type = 1; - sch->nsapi = nsapi; - - return gprs_llc_tx_ui(msg, lle->sapi, 0, mmcontext, true); -} - -/* Section 5.1.2.17 LL-UNITDATA.ind */ -int sndcp_llunitdata_ind(struct msgb *msg, struct gprs_llc_lle *lle, - uint8_t *hdr, uint16_t len) -{ - struct gprs_sndcp_entity *sne; - struct sndcp_common_hdr *sch = (struct sndcp_common_hdr *)hdr; - struct sndcp_comp_hdr *scomph = NULL; - struct sndcp_udata_hdr *suh; - uint8_t *npdu; - uint16_t npdu_num __attribute__((unused)); - int npdu_len; - int rc; - uint8_t *expnd = NULL; - - sch = (struct sndcp_common_hdr *) hdr; - if (sch->first) { - scomph = (struct sndcp_comp_hdr *) (hdr + 1); - suh = (struct sndcp_udata_hdr *) (hdr + 1 + sizeof(struct sndcp_common_hdr)); - } else - suh = (struct sndcp_udata_hdr *) (hdr + sizeof(struct sndcp_common_hdr)); - - if (sch->type == 0) { - LOGP(DSNDCP, LOGL_ERROR, "SN-DATA PDU at unitdata_ind() function\n"); - return -EINVAL; - } - - if (len < sizeof(*sch) + sizeof(*suh)) { - LOGP(DSNDCP, LOGL_ERROR, "SN-UNITDATA PDU too short (%u)\n", len); - return -EIO; - } - - sne = gprs_sndcp_entity_by_lle(lle, sch->nsapi); - if (!sne) { - LOGP(DSNDCP, LOGL_ERROR, "Message for non-existing SNDCP Entity " - "(lle=%p, TLLI=%08x, SAPI=%u, NSAPI=%u)\n", lle, - lle->llme->tlli, lle->sapi, sch->nsapi); - return -EIO; - } - /* FIXME: move this RA_ID up to the LLME or even higher */ - bssgp_parse_cell_id(&sne->ra_id, msgb_bcid(msg)); - - if (scomph) { - sne->defrag.pcomp = scomph->pcomp; - sne->defrag.dcomp = scomph->dcomp; - sne->defrag.proto = lle->llme->comp.proto; - sne->defrag.data = lle->llme->comp.data; - } - - /* any non-first segment is by definition something to defragment - * as is any segment that tells us there are more segments */ - if (!sch->first || sch->more) - return defrag_input(sne, msg, hdr, len); - - npdu_num = (suh->npdu_high << 8) | suh->npdu_low; - npdu = (uint8_t *)suh + sizeof(*suh); - npdu_len = (msg->data + msg->len) - npdu - 3; /* -3 'removes' the FCS */ - - if (npdu_len <= 0) { - LOGP(DSNDCP, LOGL_ERROR, "Short SNDCP N-PDU: %d\n", npdu_len); - return -EIO; - } - /* actually send the N-PDU to the SGSN core code, which then - * hands it off to the correct GTP tunnel + GGSN via gtp_data_req() */ - - /* Decompress packet */ -#if DEBUG_IP_PACKETS == 1 - DEBUGP(DSNDCP, " \n"); - DEBUGP(DSNDCP, ":::::::::::::::::::::::::::::::::::::::::::::::::::\n"); - DEBUGP(DSNDCP, "===================================================\n"); -#endif - if (any_pcomp_or_dcomp_active(sgsn)) { - - expnd = talloc_zero_size(msg, npdu_len * MAX_DATADECOMPR_FAC + - MAX_HDRDECOMPR_INCR); - memcpy(expnd, npdu, npdu_len); - - /* Apply data decompression */ - rc = gprs_sndcp_dcomp_expand(expnd, npdu_len, sne->defrag.dcomp, - sne->defrag.data); - if (rc < 0) { - LOGP(DSNDCP, LOGL_ERROR, - "Data decompression failed!\n"); - talloc_free(expnd); - return -EIO; - } - - /* Apply header decompression */ - rc = gprs_sndcp_pcomp_expand(expnd, rc, sne->defrag.pcomp, - sne->defrag.proto); - if (rc < 0) { - LOGP(DSNDCP, LOGL_ERROR, - "TCP/IP Header decompression failed!\n"); - talloc_free(expnd); - return -EIO; - } - - /* Modify npu length, expnd is handed directly handed - * over to gsn_rx_sndcp_ud_ind(), see below */ - npdu_len = rc; - } else - expnd = npdu; -#if DEBUG_IP_PACKETS == 1 - debug_ip_packet(expnd, npdu_len, 1, "sndcp_llunitdata_ind()"); - DEBUGP(DSNDCP, "===================================================\n"); - DEBUGP(DSNDCP, ":::::::::::::::::::::::::::::::::::::::::::::::::::\n"); - DEBUGP(DSNDCP, " \n"); -#endif - - /* Hand off packet to gtp */ - rc = sgsn_rx_sndcp_ud_ind(&sne->ra_id, lle->llme->tlli, - sne->nsapi, msg, npdu_len, expnd); - - if (any_pcomp_or_dcomp_active(sgsn)) - talloc_free(expnd); - - return rc; -} - -#if 0 -/* Section 5.1.2.1 LL-RESET.ind */ -static int sndcp_ll_reset_ind(struct gprs_sndcp_entity *se) -{ - /* treat all outstanding SNDCP-LLC request type primitives as not sent */ - /* reset all SNDCP XID parameters to default values */ - LOGP(DSNDCP, LOGL_NOTICE, "not implemented.\n"); - return 0; -} - -static int sndcp_ll_status_ind() -{ - /* inform the SM sub-layer by means of SNSM-STATUS.req */ - LOGP(DSNDCP, LOGL_NOTICE, "not implemented.\n"); - return 0; -} - -static struct sndcp_state_list {{ - uint32_t states; - unsigned int type; - int (*rout)(struct gprs_sndcp_entity *se, struct msgb *msg); -} sndcp_state_list[] = { - { ALL_STATES, - LL_RESET_IND, sndcp_ll_reset_ind }, - { ALL_STATES, - LL_ESTABLISH_IND, sndcp_ll_est_ind }, - { SBIT(SNDCP_S_EST_RQD), - LL_ESTABLISH_RESP, sndcp_ll_est_ind }, - { SBIT(SNDCP_S_EST_RQD), - LL_ESTABLISH_CONF, sndcp_ll_est_conf }, - { SBIT(SNDCP_S_ -}; - -static int sndcp_rx_llc_prim() -{ - case LL_ESTABLISH_REQ: - case LL_RELEASE_REQ: - case LL_XID_REQ: - case LL_DATA_REQ: - LL_UNITDATA_REQ, /* TLLI, SN-PDU, Ref, QoS, Radio Prio, Ciph */ - - switch (prim) { - case LL_RESET_IND: - case LL_ESTABLISH_IND: - case LL_ESTABLISH_RESP: - case LL_ESTABLISH_CONF: - case LL_RELEASE_IND: - case LL_RELEASE_CONF: - case LL_XID_IND: - case LL_XID_RESP: - case LL_XID_CONF: - case LL_DATA_IND: - case LL_DATA_CONF: - case LL_UNITDATA_IND: - case LL_STATUS_IND: - } -} -#endif - -/* Generate SNDCP-XID message */ -static int gprs_llc_gen_sndcp_xid(uint8_t *bytes, int bytes_len, uint8_t nsapi) -{ - int entity = 0; - LLIST_HEAD(comp_fields); - struct gprs_sndcp_pcomp_rfc1144_params rfc1144_params; - struct gprs_sndcp_comp_field rfc1144_comp_field; - struct gprs_sndcp_dcomp_v42bis_params v42bis_params; - struct gprs_sndcp_comp_field v42bis_comp_field; - - memset(&rfc1144_comp_field, 0, sizeof(struct gprs_sndcp_comp_field)); - memset(&v42bis_comp_field, 0, sizeof(struct gprs_sndcp_comp_field)); - - /* Setup rfc1144 */ - if (sgsn->cfg.pcomp_rfc1144.active) { - rfc1144_params.nsapi[0] = nsapi; - rfc1144_params.nsapi_len = 1; - rfc1144_params.s01 = sgsn->cfg.pcomp_rfc1144.s01; - rfc1144_comp_field.p = 1; - rfc1144_comp_field.entity = entity; - rfc1144_comp_field.algo = RFC_1144; - rfc1144_comp_field.comp[RFC1144_PCOMP1] = 1; - rfc1144_comp_field.comp[RFC1144_PCOMP2] = 2; - rfc1144_comp_field.comp_len = RFC1144_PCOMP_NUM; - rfc1144_comp_field.rfc1144_params = &rfc1144_params; - entity++; - llist_add(&rfc1144_comp_field.list, &comp_fields); - } - - /* Setup V.42bis */ - if (sgsn->cfg.dcomp_v42bis.active) { - v42bis_params.nsapi[0] = nsapi; - v42bis_params.nsapi_len = 1; - v42bis_params.p0 = sgsn->cfg.dcomp_v42bis.p0; - v42bis_params.p1 = sgsn->cfg.dcomp_v42bis.p1; - v42bis_params.p2 = sgsn->cfg.dcomp_v42bis.p2; - v42bis_comp_field.p = 1; - v42bis_comp_field.entity = entity; - v42bis_comp_field.algo = V42BIS; - v42bis_comp_field.comp[V42BIS_DCOMP1] = 1; - v42bis_comp_field.comp_len = V42BIS_DCOMP_NUM; - v42bis_comp_field.v42bis_params = &v42bis_params; - entity++; - llist_add(&v42bis_comp_field.list, &comp_fields); - } - - /* Do not attempt to compile anything if there is no data in the list */ - if (llist_empty(&comp_fields)) - return 0; - - /* Compile bytestream */ - return gprs_sndcp_compile_xid(bytes, bytes_len, &comp_fields, - DEFAULT_SNDCP_VERSION); -} - -/* Set of SNDCP-XID bnegotiation (See also: TS 144 065, - * Section 6.8 XID parameter negotiation) */ -int sndcp_sn_xid_req(struct gprs_llc_lle *lle, uint8_t nsapi) -{ - /* Note: The specification requires the SNDCP-User to set of an - * SNDCP xid request. See also 3GPP TS 44.065, 6.8 XID parameter - * negotiation, Figure 11: SNDCP XID negotiation procedure. In - * our case the SNDCP-User is sgsn_libgtp.c, which calls - * sndcp_sn_xid_req directly. */ - - uint8_t l3params[1024]; - int xid_len; - struct gprs_llc_xid_field xid_field_request; - - /* Wipe off all compression entities and their states to - * get rid of possible leftovers from a previous session */ - gprs_sndcp_comp_free(lle->llme->comp.proto); - gprs_sndcp_comp_free(lle->llme->comp.data); - lle->llme->comp.proto = gprs_sndcp_comp_alloc(lle->llme); - lle->llme->comp.data = gprs_sndcp_comp_alloc(lle->llme); - talloc_free(lle->llme->xid); - lle->llme->xid = NULL; - - /* Generate compression parameter bytestream */ - xid_len = gprs_llc_gen_sndcp_xid(l3params, sizeof(l3params), nsapi); - - /* Send XID with the SNDCP-XID bytetsream included */ - if (xid_len > 0) { - xid_field_request.type = GPRS_LLC_XID_T_L3_PAR; - xid_field_request.data = l3params; - xid_field_request.data_len = xid_len; - return gprs_ll_xid_req(lle, &xid_field_request); - } - - /* When bytestream can not be generated, proceed without SNDCP-XID */ - return gprs_ll_xid_req(lle, NULL); - -} - -/* Handle header compression entites */ -static int handle_pcomp_entities(struct gprs_sndcp_comp_field *comp_field, - struct gprs_llc_lle *lle) -{ - /* Note: This functions also transforms the comp_field into its - * echo form (strips comp values, resets propose bit etc...) - * the processed comp_fields can then be sent back as XID- - * Response without further modification. */ - - /* Delete propose bit */ - comp_field->p = 0; - - /* Process proposed parameters */ - switch (comp_field->algo) { - case RFC_1144: - if (sgsn->cfg.pcomp_rfc1144.passive - && comp_field->rfc1144_params->nsapi_len > 0) { - DEBUGP(DSNDCP, - "Accepting RFC1144 header compression...\n"); - gprs_sndcp_comp_add(lle->llme, lle->llme->comp.proto, - comp_field); - } else { - DEBUGP(DSNDCP, - "Rejecting RFC1144 header compression...\n"); - gprs_sndcp_comp_delete(lle->llme->comp.proto, - comp_field->entity); - comp_field->rfc1144_params->nsapi_len = 0; - } - break; - case RFC_2507: - /* RFC 2507 is not yet supported, - * so we set applicable nsapis to zero */ - DEBUGP(DSNDCP, "Rejecting RFC2507 header compression...\n"); - comp_field->rfc2507_params->nsapi_len = 0; - gprs_sndcp_comp_delete(lle->llme->comp.proto, - comp_field->entity); - break; - case ROHC: - /* ROHC is not yet supported, - * so we set applicable nsapis to zero */ - DEBUGP(DSNDCP, "Rejecting ROHC header compression...\n"); - comp_field->rohc_params->nsapi_len = 0; - gprs_sndcp_comp_delete(lle->llme->comp.proto, - comp_field->entity); - break; - } - - return 0; -} - -/* Hanle data compression entites */ -static int handle_dcomp_entities(struct gprs_sndcp_comp_field *comp_field, - struct gprs_llc_lle *lle) -{ - /* See note in handle_pcomp_entities() */ - - /* Delete propose bit */ - comp_field->p = 0; - - /* Process proposed parameters */ - switch (comp_field->algo) { - case V42BIS: - if (sgsn->cfg.dcomp_v42bis.passive && - comp_field->v42bis_params->nsapi_len > 0) { - DEBUGP(DSNDCP, - "Accepting V.42bis data compression...\n"); - gprs_sndcp_comp_add(lle->llme, lle->llme->comp.data, - comp_field); - } else { - LOGP(DSNDCP, LOGL_DEBUG, - "Rejecting V.42bis data compression...\n"); - gprs_sndcp_comp_delete(lle->llme->comp.data, - comp_field->entity); - comp_field->v42bis_params->nsapi_len = 0; - } - break; - case V44: - /* V44 is not yet supported, - * so we set applicable nsapis to zero */ - DEBUGP(DSNDCP, "Rejecting V.44 data compression...\n"); - comp_field->v44_params->nsapi_len = 0; - gprs_sndcp_comp_delete(lle->llme->comp.data, - comp_field->entity); - break; - } - - return 0; - -} - -/* Process SNDCP-XID indication - * (See also: TS 144 065, Section 6.8 XID parameter negotiation) */ -int sndcp_sn_xid_ind(struct gprs_llc_xid_field *xid_field_indication, - struct gprs_llc_xid_field *xid_field_response, - struct gprs_llc_lle *lle) -{ - /* Note: This function computes the SNDCP-XID response that is sent - * back to the ms when a ms originated XID is received. The - * Input XID fields are directly processed and the result is directly - * handed back. */ - - int rc; - int compclass; - int version; - - struct llist_head *comp_fields; - struct gprs_sndcp_comp_field *comp_field; - - OSMO_ASSERT(xid_field_indication); - OSMO_ASSERT(xid_field_response); - OSMO_ASSERT(lle); - - /* Parse SNDCP-CID XID-Field */ - comp_fields = gprs_sndcp_parse_xid(&version, lle->llme, - xid_field_indication->data, - xid_field_indication->data_len, - NULL); - if (!comp_fields) - return -EINVAL; - - /* Handle compression entites */ - DEBUGP(DSNDCP, "SNDCP-XID-IND (ms):\n"); - gprs_sndcp_dump_comp_fields(comp_fields, LOGL_DEBUG); - - llist_for_each_entry(comp_field, comp_fields, list) { - compclass = gprs_sndcp_get_compression_class(comp_field); - if (compclass == SNDCP_XID_PROTOCOL_COMPRESSION) - rc = handle_pcomp_entities(comp_field, lle); - else if (compclass == SNDCP_XID_DATA_COMPRESSION) - rc = handle_dcomp_entities(comp_field, lle); - else { - gprs_sndcp_comp_delete(lle->llme->comp.proto, - comp_field->entity); - gprs_sndcp_comp_delete(lle->llme->comp.data, - comp_field->entity); - rc = 0; - } - - if (rc < 0) { - talloc_free(comp_fields); - return -EINVAL; - } - } - - DEBUGP(DSNDCP, "SNDCP-XID-RES (sgsn):\n"); - gprs_sndcp_dump_comp_fields(comp_fields, LOGL_DEBUG); - - /* Reserve some memory to store the modified SNDCP-XID bytes */ - xid_field_response->data = - talloc_zero_size(lle->llme, xid_field_indication->data_len); - - /* Set Type flag for response */ - xid_field_response->type = GPRS_LLC_XID_T_L3_PAR; - - /* Compile modified SNDCP-XID bytes */ - rc = gprs_sndcp_compile_xid(xid_field_response->data, - xid_field_indication->data_len, - comp_fields, 0); - - if (rc > 0) - xid_field_response->data_len = rc; - else { - talloc_free(xid_field_response->data); - xid_field_response->data = NULL; - xid_field_response->data_len = 0; - return -EINVAL; - } - - talloc_free(comp_fields); - - return 0; -} - -/* Process SNDCP-XID indication - * (See also: TS 144 065, Section 6.8 XID parameter negotiation) */ -int sndcp_sn_xid_conf(struct gprs_llc_xid_field *xid_field_conf, - struct gprs_llc_xid_field *xid_field_request, - struct gprs_llc_lle *lle) -{ - /* Note: This function handles an incomming SNDCP-XID confirmiation. - * Since the confirmation fields may lack important parameters we - * will reconstruct these missing fields using the original request - * we have sent. After that we will create (or delete) the - * compression entites */ - - struct llist_head *comp_fields_req; - struct llist_head *comp_fields_conf; - struct gprs_sndcp_comp_field *comp_field; - int rc; - int compclass; - - /* We need both, the confirmation that is sent back by the ms, - * and the original request we have sent. If one of this is missing - * we can not process the confirmation, the caller must check if - * request and confirmation fields are available. */ - OSMO_ASSERT(xid_field_conf); - OSMO_ASSERT(xid_field_request); - - /* Parse SNDCP-CID XID-Field */ - comp_fields_req = gprs_sndcp_parse_xid(NULL, lle->llme, - xid_field_request->data, - xid_field_request->data_len, - NULL); - if (!comp_fields_req) - return -EINVAL; - - DEBUGP(DSNDCP, "SNDCP-XID-REQ (sgsn):\n"); - gprs_sndcp_dump_comp_fields(comp_fields_req, LOGL_DEBUG); - - /* Parse SNDCP-CID XID-Field */ - comp_fields_conf = gprs_sndcp_parse_xid(NULL, lle->llme, - xid_field_conf->data, - xid_field_conf->data_len, - comp_fields_req); - if (!comp_fields_conf) - return -EINVAL; - - DEBUGP(DSNDCP, "SNDCP-XID-CONF (ms):\n"); - gprs_sndcp_dump_comp_fields(comp_fields_conf, LOGL_DEBUG); - - /* Handle compression entites */ - llist_for_each_entry(comp_field, comp_fields_conf, list) { - compclass = gprs_sndcp_get_compression_class(comp_field); - if (compclass == SNDCP_XID_PROTOCOL_COMPRESSION) - rc = handle_pcomp_entities(comp_field, lle); - else if (compclass == SNDCP_XID_DATA_COMPRESSION) - rc = handle_dcomp_entities(comp_field, lle); - else { - gprs_sndcp_comp_delete(lle->llme->comp.proto, - comp_field->entity); - gprs_sndcp_comp_delete(lle->llme->comp.data, - comp_field->entity); - rc = 0; - } - - if (rc < 0) { - talloc_free(comp_fields_req); - talloc_free(comp_fields_conf); - return -EINVAL; - } - } - - talloc_free(comp_fields_req); - talloc_free(comp_fields_conf); - - return 0; -} diff --git a/openbsc/src/gprs/gprs_sndcp_comp.c b/openbsc/src/gprs/gprs_sndcp_comp.c deleted file mode 100644 index a12c39aa..00000000 --- a/openbsc/src/gprs/gprs_sndcp_comp.c +++ /dev/null @@ -1,323 +0,0 @@ -/* GPRS SNDCP header compression entity management tools */ - -/* (C) 2016 by sysmocom s.f.m.c. GmbH - * All Rights Reserved - * - * Author: Philipp Maier - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation; either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - */ - -#include -#include -#include -#include -#include -#include - -#include -#include -#include - -#include -#include -#include -#include -#include - -/* Create a new compression entity from a XID-Field */ -static struct gprs_sndcp_comp *gprs_sndcp_comp_create(const void *ctx, - const struct - gprs_sndcp_comp_field - *comp_field) -{ - struct gprs_sndcp_comp *comp_entity; - comp_entity = talloc_zero(ctx, struct gprs_sndcp_comp); - - /* Copy relevant information from the SNDCP-XID field */ - comp_entity->entity = comp_field->entity; - comp_entity->comp_len = comp_field->comp_len; - memcpy(comp_entity->comp, comp_field->comp, sizeof(comp_entity->comp)); - - if (comp_field->rfc1144_params) { - comp_entity->nsapi_len = comp_field->rfc1144_params->nsapi_len; - memcpy(comp_entity->nsapi, - comp_field->rfc1144_params->nsapi, - sizeof(comp_entity->nsapi)); - } else if (comp_field->rfc2507_params) { - comp_entity->nsapi_len = comp_field->rfc2507_params->nsapi_len; - memcpy(comp_entity->nsapi, - comp_field->rfc2507_params->nsapi, - sizeof(comp_entity->nsapi)); - } else if (comp_field->rohc_params) { - comp_entity->nsapi_len = comp_field->rohc_params->nsapi_len; - memcpy(comp_entity->nsapi, comp_field->rohc_params->nsapi, - sizeof(comp_entity->nsapi)); - } else if (comp_field->v42bis_params) { - comp_entity->nsapi_len = comp_field->v42bis_params->nsapi_len; - memcpy(comp_entity->nsapi, - comp_field->v42bis_params->nsapi, - sizeof(comp_entity->nsapi)); - } else if (comp_field->v44_params) { - comp_entity->nsapi_len = comp_field->v44_params->nsapi_len; - memcpy(comp_entity->nsapi, - comp_field->v44_params->nsapi, - sizeof(comp_entity->nsapi)); - } else { - /* The caller is expected to check carefully if the all - * data fields required for compression entity creation - * are present. Otherwise we blow an assertion here */ - OSMO_ASSERT(false); - } - comp_entity->algo = comp_field->algo; - - /* Check if an NSAPI is selected, if not, it does not make sense - * to create the compression entity, since the caller should - * have checked the presence of the NSAPI, we blow an assertion - * in case of missing NSAPIs */ - OSMO_ASSERT(comp_entity->nsapi_len > 0); - - /* Determine of which class our compression entity will be - * (Protocol or Data compresson ?) */ - comp_entity->compclass = gprs_sndcp_get_compression_class(comp_field); - - OSMO_ASSERT(comp_entity->compclass != -1); - - /* Create an algorithm specific compression context */ - if (comp_entity->compclass == SNDCP_XID_PROTOCOL_COMPRESSION) { - if (gprs_sndcp_pcomp_init(ctx, comp_entity, comp_field) != 0) { - talloc_free(comp_entity); - comp_entity = NULL; - } - } else { - if (gprs_sndcp_dcomp_init(ctx, comp_entity, comp_field) != 0) { - talloc_free(comp_entity); - comp_entity = NULL; - } - } - - /* Bail on failure */ - if (comp_entity == NULL) { - LOGP(DSNDCP, LOGL_ERROR, - "Compression entity creation failed!\n"); - return NULL; - } - - /* Display info message */ - if (comp_entity->compclass == SNDCP_XID_PROTOCOL_COMPRESSION) { - LOGP(DSNDCP, LOGL_INFO, - "New header compression entity (%d) created.\n", - comp_entity->entity); - } else { - LOGP(DSNDCP, LOGL_INFO, - "New data compression entity (%d) created.\n", - comp_entity->entity); - } - - return comp_entity; -} - -/* Allocate a compression enitiy list */ -struct llist_head *gprs_sndcp_comp_alloc(const void *ctx) -{ - struct llist_head *lh; - - lh = talloc_zero(ctx, struct llist_head); - INIT_LLIST_HEAD(lh); - - return lh; -} - -/* Free a compression entitiy list */ -void gprs_sndcp_comp_free(struct llist_head *comp_entities) -{ - struct gprs_sndcp_comp *comp_entity; - - /* We expect the caller to take care of allocating a - * compression entity list properly. Attempting to - * free a non existing list clearly points out - * a malfunction. */ - OSMO_ASSERT(comp_entities); - - llist_for_each_entry(comp_entity, comp_entities, list) { - /* Free compression entity */ - if (comp_entity->compclass == SNDCP_XID_PROTOCOL_COMPRESSION) { - LOGP(DSNDCP, LOGL_INFO, - "Deleting header compression entity %d ...\n", - comp_entity->entity); - gprs_sndcp_pcomp_term(comp_entity); - } else { - LOGP(DSNDCP, LOGL_INFO, - "Deleting data compression entity %d ...\n", - comp_entity->entity); - gprs_sndcp_dcomp_term(comp_entity); - } - } - - talloc_free(comp_entities); -} - -/* Delete a compression entity */ -void gprs_sndcp_comp_delete(struct llist_head *comp_entities, - unsigned int entity) -{ - struct gprs_sndcp_comp *comp_entity; - struct gprs_sndcp_comp *comp_entity_to_delete = NULL; - - OSMO_ASSERT(comp_entities); - - llist_for_each_entry(comp_entity, comp_entities, list) { - if (comp_entity->entity == entity) { - comp_entity_to_delete = comp_entity; - break; - } - } - - if (!comp_entity_to_delete) - return; - - if (comp_entity_to_delete->compclass == SNDCP_XID_PROTOCOL_COMPRESSION) { - LOGP(DSNDCP, LOGL_INFO, - "Deleting header compression entity %d ...\n", - comp_entity_to_delete->entity); - gprs_sndcp_pcomp_term(comp_entity_to_delete); - } else { - LOGP(DSNDCP, LOGL_INFO, - "Deleting data compression entity %d ...\n", - comp_entity_to_delete->entity); - } - - /* Delete compression entity */ - llist_del(&comp_entity_to_delete->list); - talloc_free(comp_entity_to_delete); -} - -/* Create and Add a new compression entity - * (returns a pointer to the compression entity that has just been created) */ -struct gprs_sndcp_comp *gprs_sndcp_comp_add(const void *ctx, - struct llist_head *comp_entities, - const struct gprs_sndcp_comp_field - *comp_field) -{ - struct gprs_sndcp_comp *comp_entity; - - OSMO_ASSERT(comp_entities); - OSMO_ASSERT(comp_field); - - /* Just to be sure, if the entity is already in - * the list it will be deleted now */ - gprs_sndcp_comp_delete(comp_entities, comp_field->entity); - - /* Create and add a new entity to the list */ - comp_entity = gprs_sndcp_comp_create(ctx, comp_field); - - if (!comp_entity) - return NULL; - - llist_add(&comp_entity->list, comp_entities); - return comp_entity; -} - -/* Find which compression entity handles the specified pcomp/dcomp */ -struct gprs_sndcp_comp *gprs_sndcp_comp_by_comp(const struct llist_head - *comp_entities, uint8_t comp) -{ - struct gprs_sndcp_comp *comp_entity; - int i; - - OSMO_ASSERT(comp_entities); - - llist_for_each_entry(comp_entity, comp_entities, list) { - for (i = 0; i < comp_entity->comp_len; i++) { - if (comp_entity->comp[i] == comp) - return comp_entity; - } - } - - LOGP(DSNDCP, LOGL_ERROR, - "Could not find a matching compression entity for given pcomp/dcomp value %d.\n", - comp); - return NULL; -} - -/* Find which compression entity handles the specified nsapi */ -struct gprs_sndcp_comp *gprs_sndcp_comp_by_nsapi(const struct llist_head - *comp_entities, uint8_t nsapi) -{ - struct gprs_sndcp_comp *comp_entity; - int i; - - OSMO_ASSERT(comp_entities); - - llist_for_each_entry(comp_entity, comp_entities, list) { - for (i = 0; i < comp_entity->nsapi_len; i++) { - if (comp_entity->nsapi[i] == nsapi) - return comp_entity; - } - } - - return NULL; -} - -/* Find a comp_index for a given pcomp/dcomp value */ -uint8_t gprs_sndcp_comp_get_idx(const struct gprs_sndcp_comp *comp_entity, - uint8_t comp) -{ - /* Note: This function returns a normalized version of the comp value, - * which matches up with the position of the comp field. Since comp=0 - * is reserved for "no compression", the index value starts counting - * at one. The return value is the PCOMPn/DCOMPn value one can find - * in the Specification (see e.g. 3GPP TS 44.065, 6.5.3.2, Table 7) */ - - int i; - OSMO_ASSERT(comp_entity); - - /* A pcomp/dcomp value of zero is reserved for "no comproession", - * So we just bail and return zero in this case */ - if (comp == 0) - return 0; - - /* Look in the pcomp/dcomp list for the index */ - for (i = 0; i < comp_entity->comp_len; i++) { - if (comp_entity->comp[i] == comp) - return i + 1; - } - - LOGP(DSNDCP, LOGL_ERROR, - "Could not find a matching comp_index for given pcomp/dcomp value %d\n", - comp); - return 0; -} - -/* Find a pcomp/dcomp value for a given comp_index */ -uint8_t gprs_sndcp_comp_get_comp(const struct gprs_sndcp_comp *comp_entity, - uint8_t comp_index) -{ - OSMO_ASSERT(comp_entity); - - /* A comp_index of zero translates to zero right away. */ - if (comp_index == 0) - return 0; - - if (comp_index > comp_entity->comp_len) { - LOGP(DSNDCP, LOGL_ERROR, - "Could not find a matching pcomp/dcomp value for given comp_index value %d.\n", - comp_index); - return 0; - } - - /* Look in the pcomp/dcomp list for the comp_index, see - * note in gprs_sndcp_comp_get_idx() */ - return comp_entity->comp[comp_index - 1]; -} diff --git a/openbsc/src/gprs/gprs_sndcp_dcomp.c b/openbsc/src/gprs/gprs_sndcp_dcomp.c deleted file mode 100644 index b0f95b48..00000000 --- a/openbsc/src/gprs/gprs_sndcp_dcomp.c +++ /dev/null @@ -1,358 +0,0 @@ -/* GPRS SNDCP data compression handler */ - -/* (C) 2016 by Sysmocom s.f.m.c. GmbH - * All Rights Reserved - * - * Author: Philipp Maier - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation; either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - * - */ - -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include - -/* A struct to capture the output data of compressor and decompressor */ -struct v42bis_output_buffer { - uint8_t *buf; - uint8_t *buf_pointer; - int len; -}; - -/* Handler to capture the output data from the compressor */ -void tx_v42bis_frame_handler(void *user_data, const uint8_t *pkt, int len) -{ - struct v42bis_output_buffer *output_buffer = - (struct v42bis_output_buffer *)user_data; - memcpy(output_buffer->buf_pointer, pkt, len); - output_buffer->buf_pointer += len; - output_buffer->len += len; - return; -} - -/* Handler to capture the output data from the decompressor */ -void rx_v42bis_data_handler(void *user_data, const uint8_t *buf, int len) -{ - struct v42bis_output_buffer *output_buffer = - (struct v42bis_output_buffer *)user_data; - memcpy(output_buffer->buf_pointer, buf, len); - output_buffer->buf_pointer += len; - output_buffer->len += len; - return; -} - -/* Initalize data compression */ -int gprs_sndcp_dcomp_init(const void *ctx, struct gprs_sndcp_comp *comp_entity, - const struct gprs_sndcp_comp_field *comp_field) -{ - /* Note: This function is automatically called from - * gprs_sndcp_comp.c when a new data compression - * entity is created by gprs_sndcp.c */ - - OSMO_ASSERT(comp_entity); - OSMO_ASSERT(comp_field); - - if (comp_entity->compclass == SNDCP_XID_DATA_COMPRESSION - && comp_entity->algo == V42BIS) { - OSMO_ASSERT(comp_field->v42bis_params); - comp_entity->state = - v42bis_init(ctx, NULL, comp_field->v42bis_params->p0, - comp_field->v42bis_params->p1, - comp_field->v42bis_params->p2, - &tx_v42bis_frame_handler, NULL, - V42BIS_MAX_OUTPUT_LENGTH, - &rx_v42bis_data_handler, NULL, - V42BIS_MAX_OUTPUT_LENGTH); - LOGP(DSNDCP, LOGL_INFO, - "V.42bis data compression initalized.\n"); - return 0; - } - - /* Just in case someone tries to initalize an unknown or unsupported - * data compresson. Since everything is checked during the SNDCP - * negotiation process, this should never happen! */ - OSMO_ASSERT(false); -} - -/* Terminate data compression */ -void gprs_sndcp_dcomp_term(struct gprs_sndcp_comp *comp_entity) -{ - /* Note: This function is automatically called from - * gprs_sndcp_comp.c when a data compression - * entity is deleted by gprs_sndcp.c */ - - OSMO_ASSERT(comp_entity); - - if (comp_entity->compclass == SNDCP_XID_DATA_COMPRESSION - && comp_entity->algo == V42BIS) { - if (comp_entity->state) { - v42bis_free((v42bis_state_t *) comp_entity->state); - comp_entity->state = NULL; - } - LOGP(DSNDCP, LOGL_INFO, - "V.42bis data compression terminated.\n"); - return; - } - - /* Just in case someone tries to terminate an unknown or unsupported - * data compresson. Since everything is checked during the SNDCP - * negotiation process, this should never happen! */ - OSMO_ASSERT(false); -} - -/* Perform a full reset of the V.42bis compression state */ -static void v42bis_reset(v42bis_state_t *comp) -{ - /* This function performs a complete reset of the V.42bis compression - * state by reinitalizing the state withe the previously negotiated - * parameters. */ - - int p0, p1, p2; - p0 = comp->decompress.v42bis_parm_p0 | comp->compress.v42bis_parm_p0; - p1 = comp->decompress.v42bis_parm_n2; - p2 = comp->decompress.v42bis_parm_n7; - - DEBUGP(DSNDCP, "Resetting compression state: %p, p0=%d, p1=%d, p2=%d\n", - comp, p0, p1, p2); - - v42bis_init(NULL, comp, p0, p1, p2, &tx_v42bis_frame_handler, NULL, - V42BIS_MAX_OUTPUT_LENGTH, &rx_v42bis_data_handler, NULL, - V42BIS_MAX_OUTPUT_LENGTH); -} - -/* Compress a packet using V.42bis data compression */ -static int v42bis_compress_unitdata(uint8_t *pcomp_index, uint8_t *data, - unsigned int len, v42bis_state_t *comp) -{ - /* Note: This implementation may only be used to compress SN_UNITDATA - * packets, since it resets the compression state for each NPDU. */ - - uint8_t *data_o; - int rc; - int skip = 0; - struct v42bis_output_buffer compressed_data; - - /* Don't bother with short packets */ - if (len < MIN_COMPR_PAYLOAD) - skip = 1; - - /* Skip if compression is not enabled for TX direction */ - if (!comp->compress.v42bis_parm_p0) - skip = 1; - - /* Skip compression */ - if (skip) { - *pcomp_index = 0; - return len; - } - - /* Reset V.42bis compression state */ - v42bis_reset(comp); - - /* Run compressor */ - data_o = talloc_zero_size(comp, len * MAX_DATADECOMPR_FAC); - compressed_data.buf = data_o; - compressed_data.buf_pointer = data_o; - compressed_data.len = 0; - comp->compress.user_data = (&compressed_data); - rc = v42bis_compress(comp, data, len); - if (rc < 0) { - LOGP(DSNDCP, LOGL_ERROR, - "Data compression failed, skipping...\n"); - skip = 1; - } - rc = v42bis_compress_flush(comp); - if (rc < 0) { - LOGP(DSNDCP, LOGL_ERROR, - "Data compression failed, skipping...\n"); - skip = 1; - } - - /* The compressor might yield negative compression gain, in - * this case, we just decide to send the packat as normal, - * uncompressed payload => skip compresssion */ - if (compressed_data.len >= len) { - LOGP(DSNDCP, LOGL_ERROR, - "Data compression ineffective, skipping...\n"); - skip = 1; - } - - /* Skip compression */ - if (skip) { - *pcomp_index = 0; - talloc_free(data_o); - return len; - } - - *pcomp_index = 1; - memcpy(data, data_o, compressed_data.len); - talloc_free(data_o); - - return compressed_data.len; -} - -/* Expand a packet using V.42bis data compression */ -static int v42bis_expand_unitdata(uint8_t *data, unsigned int len, - uint8_t pcomp_index, v42bis_state_t *comp) -{ - /* Note: This implementation may only be used to compress SN_UNITDATA - * packets, since it resets the compression state for each NPDU. */ - - int rc; - struct v42bis_output_buffer uncompressed_data; - uint8_t *data_i; - - /* Skip when the packet is marked as uncompressed */ - if (pcomp_index == 0) { - return len; - } - - /* Reset V.42bis compression state */ - v42bis_reset(comp); - - /* Decompress packet */ - data_i = talloc_zero_size(comp, len); - memcpy(data_i, data, len); - uncompressed_data.buf = data; - uncompressed_data.buf_pointer = data; - uncompressed_data.len = 0; - comp->decompress.user_data = (&uncompressed_data); - rc = v42bis_decompress(comp, data_i, len); - talloc_free(data_i); - if (rc < 0) - return -EINVAL; - rc = v42bis_decompress_flush(comp); - if (rc < 0) - return -EINVAL; - - return uncompressed_data.len; -} - -/* Expand packet */ -int gprs_sndcp_dcomp_expand(uint8_t *data, unsigned int len, uint8_t pcomp, - const struct llist_head *comp_entities) -{ - int rc; - uint8_t pcomp_index = 0; - struct gprs_sndcp_comp *comp_entity; - - OSMO_ASSERT(data); - OSMO_ASSERT(comp_entities); - - LOGP(DSNDCP, LOGL_DEBUG, - "Data compression entity list: comp_entities=%p\n", comp_entities); - - LOGP(DSNDCP, LOGL_DEBUG, "Data compression mode: dcomp=%d\n", pcomp); - - /* Skip on pcomp=0 */ - if (pcomp == 0) { - return len; - } - - /* Find out which compression entity handles the data */ - comp_entity = gprs_sndcp_comp_by_comp(comp_entities, pcomp); - - /* Skip compression if no suitable compression entity can be found */ - if (!comp_entity) { - return len; - } - - /* Note: Only data compression entities may appear in - * data compression context */ - OSMO_ASSERT(comp_entity->compclass == SNDCP_XID_DATA_COMPRESSION); - - /* Note: Currently V42BIS is the only compression method we - * support, so the only allowed algorithm is V42BIS */ - OSMO_ASSERT(comp_entity->algo == V42BIS); - - /* Find pcomp_index */ - pcomp_index = gprs_sndcp_comp_get_idx(comp_entity, pcomp); - - /* Run decompression algo */ - rc = v42bis_expand_unitdata(data, len, pcomp_index, comp_entity->state); - - LOGP(DSNDCP, LOGL_DEBUG, - "Data expansion done, old length=%d, new length=%d, entity=%p\n", - len, rc, comp_entity); - - return rc; -} - -/* Compress packet */ -int gprs_sndcp_dcomp_compress(uint8_t *data, unsigned int len, uint8_t *pcomp, - const struct llist_head *comp_entities, - uint8_t nsapi) -{ - int rc; - uint8_t pcomp_index = 0; - struct gprs_sndcp_comp *comp_entity; - - OSMO_ASSERT(data); - OSMO_ASSERT(pcomp); - OSMO_ASSERT(comp_entities); - - LOGP(DSNDCP, LOGL_DEBUG, - "Data compression entity list: comp_entities=%p\n", comp_entities); - - /* Find out which compression entity handles the data */ - comp_entity = gprs_sndcp_comp_by_nsapi(comp_entities, nsapi); - - /* Skip compression if no suitable compression entity can be found */ - if (!comp_entity) { - *pcomp = 0; - return len; - } - - /* Note: Only data compression entities may appear in - * data compression context */ - OSMO_ASSERT(comp_entity->compclass == SNDCP_XID_DATA_COMPRESSION); - - /* Note: Currently V42BIS is the only compression method we - * support, so the only allowed algorithm is V42BIS */ - OSMO_ASSERT(comp_entity->algo == V42BIS); - - /* Run compression algo */ - rc = v42bis_compress_unitdata(&pcomp_index, data, len, - comp_entity->state); - - /* Find pcomp value */ - *pcomp = gprs_sndcp_comp_get_comp(comp_entity, pcomp_index); - - LOGP(DSNDCP, LOGL_DEBUG, "Data compression mode: dcomp=%d\n", *pcomp); - - LOGP(DSNDCP, LOGL_DEBUG, - "Data compression done, old length=%d, new length=%d, entity=%p\n", - len, rc, comp_entity); - - return rc; -} diff --git a/openbsc/src/gprs/gprs_sndcp_pcomp.c b/openbsc/src/gprs/gprs_sndcp_pcomp.c deleted file mode 100644 index a2236c3b..00000000 --- a/openbsc/src/gprs/gprs_sndcp_pcomp.c +++ /dev/null @@ -1,282 +0,0 @@ -/* GPRS SNDCP header compression handler */ - -/* (C) 2016 by Sysmocom s.f.m.c. GmbH - * All Rights Reserved - * - * Author: Philipp Maier - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation; either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - * - */ - -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include - -/* Initalize header compression */ -int gprs_sndcp_pcomp_init(const void *ctx, struct gprs_sndcp_comp *comp_entity, - const struct gprs_sndcp_comp_field *comp_field) -{ - /* Note: This function is automatically called from - * gprs_sndcp_comp.c when a new header compression - * entity is created by gprs_sndcp.c */ - - OSMO_ASSERT(comp_entity); - OSMO_ASSERT(comp_field); - - if (comp_entity->compclass == SNDCP_XID_PROTOCOL_COMPRESSION - && comp_entity->algo == RFC_1144) { - OSMO_ASSERT(comp_field->rfc1144_params); - comp_entity->state = - slhc_init(ctx, comp_field->rfc1144_params->s01 + 1, - comp_field->rfc1144_params->s01 + 1); - LOGP(DSNDCP, LOGL_INFO, - "RFC1144 header compression initalized.\n"); - return 0; - } - - /* Just in case someone tries to initalize an unknown or unsupported - * header compresson. Since everything is checked during the SNDCP - * negotiation process, this should never happen! */ - OSMO_ASSERT(false); -} - -/* Terminate header compression */ -void gprs_sndcp_pcomp_term(struct gprs_sndcp_comp *comp_entity) -{ - /* Note: This function is automatically called from - * gprs_sndcp_comp.c when a header compression - * entity is deleted by gprs_sndcp.c */ - - OSMO_ASSERT(comp_entity); - - if (comp_entity->compclass == SNDCP_XID_PROTOCOL_COMPRESSION - && comp_entity->algo == RFC_1144) { - if (comp_entity->state) { - slhc_free((struct slcompress *)comp_entity->state); - comp_entity->state = NULL; - } - LOGP(DSNDCP, LOGL_INFO, - "RFC1144 header compression terminated.\n"); - return; - } - - /* Just in case someone tries to terminate an unknown or unsupported - * data compresson. Since everything is checked during the SNDCP - * negotiation process, this should never happen! */ - OSMO_ASSERT(false); -} - -/* Compress a packet using Van Jacobson RFC1144 header compression */ -static int rfc1144_compress(uint8_t *pcomp_index, uint8_t *data, - unsigned int len, struct slcompress *comp) -{ - uint8_t *comp_ptr; - int compr_len; - uint8_t *data_o; - - /* Create a working copy of the incoming data */ - data_o = talloc_zero_size(comp, len); - memcpy(data_o, data, len); - - /* Run compressor */ - compr_len = slhc_compress(comp, data, len, data_o, &comp_ptr, 0); - - /* Generate pcomp_index */ - if (data_o[0] & SL_TYPE_COMPRESSED_TCP) { - *pcomp_index = 2; - data_o[0] &= ~SL_TYPE_COMPRESSED_TCP; - memcpy(data, data_o, compr_len); - } else if ((data_o[0] & SL_TYPE_UNCOMPRESSED_TCP) == - SL_TYPE_UNCOMPRESSED_TCP) { - *pcomp_index = 1; - data_o[0] &= 0x4F; - memcpy(data, data_o, compr_len); - } else - *pcomp_index = 0; - - talloc_free(data_o); - return compr_len; -} - -/* Expand a packet using Van Jacobson RFC1144 header compression */ -static int rfc1144_expand(uint8_t *data, unsigned int len, uint8_t pcomp_index, - struct slcompress *comp) -{ - int data_decompressed_len; - int type; - - /* Note: this function should never be called with pcomp_index=0, - * since this condition is already filtered - * out by gprs_sndcp_pcomp_expand() */ - - /* Determine the data type by the PCOMP index */ - switch (pcomp_index) { - case 0: - type = SL_TYPE_IP; - break; - case 1: - type = SL_TYPE_UNCOMPRESSED_TCP; - break; - case 2: - type = SL_TYPE_COMPRESSED_TCP; - break; - default: - LOGP(DSNDCP, LOGL_ERROR, - "rfc1144_expand() Invalid pcomp_index value (%d) detected, assuming no compression!\n", - pcomp_index); - type = SL_TYPE_IP; - break; - } - - /* Restore the original version nibble on - * marked uncompressed packets */ - if (type == SL_TYPE_UNCOMPRESSED_TCP) { - /* Just in case the phone tags uncompressed tcp-data - * (normally this is handled by pcomp so there is - * no need for tagging the data) */ - data[0] &= 0x4F; - data_decompressed_len = slhc_remember(comp, data, len); - return data_decompressed_len; - } - - /* Uncompress compressed packets */ - else if (type == SL_TYPE_COMPRESSED_TCP) { - data_decompressed_len = slhc_uncompress(comp, data, len); - return data_decompressed_len; - } - - /* Regular or unknown packets will not be touched */ - return len; -} - -/* Expand packet header */ -int gprs_sndcp_pcomp_expand(uint8_t *data, unsigned int len, uint8_t pcomp, - const struct llist_head *comp_entities) -{ - int rc; - uint8_t pcomp_index = 0; - struct gprs_sndcp_comp *comp_entity; - - OSMO_ASSERT(data); - OSMO_ASSERT(comp_entities); - - LOGP(DSNDCP, LOGL_DEBUG, - "Header compression entity list: comp_entities=%p\n", - comp_entities); - - LOGP(DSNDCP, LOGL_DEBUG, "Header compression mode: pcomp=%d\n", pcomp); - - /* Skip on pcomp=0 */ - if (pcomp == 0) { - return len; - } - - /* Find out which compression entity handles the data */ - comp_entity = gprs_sndcp_comp_by_comp(comp_entities, pcomp); - - /* Skip compression if no suitable compression entity can be found */ - if (!comp_entity) { - return len; - } - - /* Note: Only protocol compression entities may appear in - * protocol compression context */ - OSMO_ASSERT(comp_entity->compclass == SNDCP_XID_PROTOCOL_COMPRESSION); - - /* Note: Currently RFC1144 is the only compression method we - * support, so the only allowed algorithm is RFC1144 */ - OSMO_ASSERT(comp_entity->algo == RFC_1144); - - /* Find pcomp_index */ - pcomp_index = gprs_sndcp_comp_get_idx(comp_entity, pcomp); - - /* Run decompression algo */ - rc = rfc1144_expand(data, len, pcomp_index, comp_entity->state); - slhc_i_status(comp_entity->state); - slhc_o_status(comp_entity->state); - - LOGP(DSNDCP, LOGL_DEBUG, - "Header expansion done, old length=%d, new length=%d, entity=%p\n", - len, rc, comp_entity); - - return rc; -} - -/* Compress packet header */ -int gprs_sndcp_pcomp_compress(uint8_t *data, unsigned int len, uint8_t *pcomp, - const struct llist_head *comp_entities, - uint8_t nsapi) -{ - int rc; - uint8_t pcomp_index = 0; - struct gprs_sndcp_comp *comp_entity; - - OSMO_ASSERT(data); - OSMO_ASSERT(pcomp); - OSMO_ASSERT(comp_entities); - - LOGP(DSNDCP, LOGL_DEBUG, - "Header compression entity list: comp_entities=%p\n", - comp_entities); - - /* Find out which compression entity handles the data */ - comp_entity = gprs_sndcp_comp_by_nsapi(comp_entities, nsapi); - - /* Skip compression if no suitable compression entity can be found */ - if (!comp_entity) { - *pcomp = 0; - return len; - } - - /* Note: Only protocol compression entities may appear in - * protocol compression context */ - OSMO_ASSERT(comp_entity->compclass == SNDCP_XID_PROTOCOL_COMPRESSION); - - /* Note: Currently RFC1144 is the only compression method we - * support, so the only allowed algorithm is RFC1144 */ - OSMO_ASSERT(comp_entity->algo == RFC_1144); - - /* Run compression algo */ - rc = rfc1144_compress(&pcomp_index, data, len, comp_entity->state); - slhc_i_status(comp_entity->state); - slhc_o_status(comp_entity->state); - - /* Find pcomp value */ - *pcomp = gprs_sndcp_comp_get_comp(comp_entity, pcomp_index); - - LOGP(DSNDCP, LOGL_DEBUG, "Header compression mode: pcomp=%d\n", *pcomp); - - LOGP(DSNDCP, LOGL_DEBUG, - "Header compression done, old length=%d, new length=%d, entity=%p\n", - len, rc, comp_entity); - return rc; -} diff --git a/openbsc/src/gprs/gprs_sndcp_vty.c b/openbsc/src/gprs/gprs_sndcp_vty.c deleted file mode 100644 index 430881fc..00000000 --- a/openbsc/src/gprs/gprs_sndcp_vty.c +++ /dev/null @@ -1,71 +0,0 @@ -/* VTY interface for our GPRS SNDCP implementation */ - -/* (C) 2010 by Harald Welte - * - * All Rights Reserved - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation; either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public 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 - -static void vty_dump_sne(struct vty *vty, struct gprs_sndcp_entity *sne) -{ - vty_out(vty, " TLLI %08x SAPI=%u NSAPI=%u:%s", - sne->lle->llme->tlli, sne->lle->sapi, sne->nsapi, VTY_NEWLINE); - vty_out(vty, " Defrag: npdu=%u highest_seg=%u seg_have=0x%08x tot_len=%u%s", - sne->defrag.npdu, sne->defrag.highest_seg, sne->defrag.seg_have, - sne->defrag.tot_len, VTY_NEWLINE); -} - - -DEFUN(show_sndcp, show_sndcp_cmd, - "show sndcp", - SHOW_STR "Display information about the SNDCP protocol") -{ - struct gprs_sndcp_entity *sne; - - vty_out(vty, "State of SNDCP Entities%s", VTY_NEWLINE); - llist_for_each_entry(sne, &gprs_sndcp_entities, list) - vty_dump_sne(vty, sne); - - return CMD_SUCCESS; -} - -int gprs_sndcp_vty_init(void) -{ - install_element_ve(&show_sndcp_cmd); - - return 0; -} diff --git a/openbsc/src/gprs/gprs_sndcp_xid.c b/openbsc/src/gprs/gprs_sndcp_xid.c deleted file mode 100644 index dfea5feb..00000000 --- a/openbsc/src/gprs/gprs_sndcp_xid.c +++ /dev/null @@ -1,1822 +0,0 @@ -/* GPRS SNDCP XID field encoding/decoding as per 3GPP TS 44.065 */ - -/* (C) 2016 by sysmocom s.f.m.c. GmbH - * All Rights Reserved - * - * Author: Philipp Maier - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation; either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - */ - -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include - -#include -#include -#include -#include - -/* When the propose bit in an SNDCP-XID compression field is set to zero, - * the algorithm identifier is stripped. The algoritm parameters are specific - * for each algorithms. The following struct is used to pass the information - * about the referenced algorithm to the parser. */ -struct entity_algo_table { - unsigned int entity; /* see also: 6.5.1.1.3 and 6.6.1.1.3 */ - unsigned int algo; /* see also: 6.5.1.1.4 and 6.6.1.1.4 */ - unsigned int compclass; /* Can be either SNDCP_XID_DATA_COMPRESSION or - SNDCP_XID_PROTOCOL_COMPRESSION */ -}; - -/* FUNCTIONS RELATED TO SNDCP-XID ENCODING */ - -/* Encode applicable sapis (works the same in all three compression schemes) */ -static int encode_pcomp_applicable_sapis(uint8_t *dst, - const uint8_t *nsapis, - uint8_t nsapis_len) -{ - /* NOTE: Buffer *dst needs offer at 2 bytes - * of space to store the generation results */ - - uint16_t blob; - uint8_t nsapi; - int i; - - /* Bail if number of possible nsapis exceeds valid range - * (Only 11 nsapis possible for PDP-Contexts) */ - OSMO_ASSERT(nsapis_len <= 11); - - /* Encode applicable SAPIs */ - blob = 0; - for (i = 0; i < nsapis_len; i++) { - nsapi = nsapis[i]; - /* Only NSAPI 5 to 15 are applicable for user traffic (PDP- - * contexts). Only for these NSAPIs SNDCP-XID parameters - * can apply. See also 3GPP TS 44.065, 5.1 Service primitives */ - OSMO_ASSERT(nsapi >= 5 && nsapi <= 15); - blob |= (1 << nsapi); - } - - /* Store result */ - *dst = (blob >> 8) & 0xFF; - dst++; - *dst = blob & 0xFF; - - return 2; -} - -/* Encode rfc1144 parameter field - * (see also: 3GPP TS 44.065, 6.5.2.1, Table 5) */ -static int encode_pcomp_rfc1144_params(uint8_t *dst, unsigned int dst_maxlen, - const struct - gprs_sndcp_pcomp_rfc1144_params *params) -{ - /* NOTE: Buffer *dst should offer at least 3 bytes - * of space to store the generation results */ - - int dst_counter = 0; - int rc; - - OSMO_ASSERT(dst_maxlen >= 3); - - /* Zero out buffer */ - memset(dst, 0, dst_maxlen); - - /* Encode applicable SAPIs */ - rc = encode_pcomp_applicable_sapis(dst, params->nsapi, - params->nsapi_len); - dst += rc; - dst_counter += rc; - - /* Encode s01 (see also: 3GPP TS 44.065, 6.5.2.1, Table 5) */ - OSMO_ASSERT(params->s01 >= 0); - OSMO_ASSERT(params->s01 <= 255); - *dst = params->s01; - dst++; - dst_counter++; - - /* Return generated length */ - return dst_counter; -} - -/* - * Encode rfc2507 parameter field - * (see also: 3GPP TS 44.065, 6.5.3.1, Table 6) - */ -static int encode_pcomp_rfc2507_params(uint8_t *dst, unsigned int dst_maxlen, - const struct - gprs_sndcp_pcomp_rfc2507_params *params) -{ - /* NOTE: Buffer *dst should offer at least 3 bytes - * of space to store the generation results */ - - int dst_counter = 0; - int rc; - - OSMO_ASSERT(dst_maxlen >= 9); - - /* Zero out buffer */ - memset(dst, 0, dst_maxlen); - - /* Encode applicable SAPIs */ - rc = encode_pcomp_applicable_sapis(dst, params->nsapi, - params->nsapi_len); - dst += rc; - dst_counter += rc; - - /* Encode F_MAX_PERIOD (see also: 3GPP TS 44.065, 6.5.3.1, Table 6) */ - OSMO_ASSERT(params->f_max_period >= 1); - OSMO_ASSERT(params->f_max_period <= 65535); - *dst = (params->f_max_period >> 8) & 0xFF; - dst++; - dst_counter++; - *dst = (params->f_max_period) & 0xFF; - dst++; - dst_counter++; - - /* Encode F_MAX_TIME (see also: 3GPP TS 44.065, 6.5.3.1, Table 6) */ - OSMO_ASSERT(params->f_max_time >= 1); - OSMO_ASSERT(params->f_max_time <= 255); - *dst = params->f_max_time; - dst++; - dst_counter++; - - /* Encode MAX_HEADER (see also: 3GPP TS 44.065, 6.5.3.1, Table 6) */ - OSMO_ASSERT(params->max_header >= 60); - OSMO_ASSERT(params->max_header <= 255); - *dst = params->max_header; - dst++; - dst_counter++; - - /* Encode TCP_SPACE (see also: 3GPP TS 44.065, 6.5.3.1, Table 6) */ - OSMO_ASSERT(params->tcp_space >= 3); - OSMO_ASSERT(params->tcp_space <= 255); - *dst = params->tcp_space; - dst++; - dst_counter++; - - /* Encode NON_TCP_SPACE (see also: 3GPP TS 44.065, 6.5.3.1, Table 6) */ - OSMO_ASSERT(params->non_tcp_space >= 3); - OSMO_ASSERT(params->non_tcp_space <= 65535); - *dst = (params->non_tcp_space >> 8) & 0xFF; - dst++; - dst_counter++; - *dst = (params->non_tcp_space) & 0xFF; - dst++; - dst_counter++; - - /* Return generated length */ - return dst_counter; -} - -/* Encode ROHC parameter field - * (see also: 3GPP TS 44.065, 6.5.4.1, Table 10) */ -static int encode_pcomp_rohc_params(uint8_t *dst, unsigned int dst_maxlen, - const struct gprs_sndcp_pcomp_rohc_params - *params) -{ - /* NOTE: Buffer *dst should offer at least 36 - * (2 * 16 Profiles + 2 * 3 Parameter) bytes - * of memory space to store generation results */ - - int i; - int dst_counter = 0; - int rc; - - OSMO_ASSERT(dst_maxlen >= 38); - - /* Bail if number of ROHC profiles exceeds limit - * (ROHC supports only a maximum of 16 different profiles) */ - OSMO_ASSERT(params->profile_len <= 16); - - /* Zero out buffer */ - memset(dst, 0, dst_maxlen); - - /* Encode applicable SAPIs */ - rc = encode_pcomp_applicable_sapis(dst, params->nsapi, - params->nsapi_len); - dst += rc; - dst_counter += rc; - - /* Encode MAX_CID (see also: 3GPP TS 44.065, 6.5.4.1, Table 10) */ - OSMO_ASSERT(params->max_cid >= 0); - OSMO_ASSERT(params->max_cid <= 16383); - *dst = (params->max_cid >> 8) & 0xFF; - dst++; - *dst = params->max_cid & 0xFF; - dst++; - dst_counter += 2; - - /* Encode MAX_HEADER (see also: 3GPP TS 44.065, 6.5.4.1, Table 10) */ - OSMO_ASSERT(params->max_header >= 60); - OSMO_ASSERT(params->max_header <= 255); - *dst = (params->max_header >> 8) & 0xFF; - dst++; - *dst = params->max_header & 0xFF; - dst++; - dst_counter += 2; - - /* Encode ROHC Profiles (see also: 3GPP TS 44.065, 6.5.4.1, Table 10) */ - for (i = 0; i < params->profile_len; i++) { - *dst = (params->profile[i] >> 8) & 0xFF; - dst++; - *dst = params->profile[i] & 0xFF; - dst++; - dst_counter += 2; - } - - /* Return generated length */ - return dst_counter; -} - -/* Encode V.42bis parameter field - * (see also: 3GPP TS 44.065, 6.6.2.1, Table 7a) */ -static int encode_dcomp_v42bis_params(uint8_t *dst, unsigned int dst_maxlen, - const struct - gprs_sndcp_dcomp_v42bis_params *params) -{ - /* NOTE: Buffer *dst should offer at least 6 bytes - * of space to store the generation results */ - - int dst_counter = 0; - int rc; - - OSMO_ASSERT(dst_maxlen >= 6); - - /* Zero out buffer */ - memset(dst, 0, dst_maxlen); - - /* Encode applicable SAPIs */ - rc = encode_pcomp_applicable_sapis(dst, params->nsapi, - params->nsapi_len); - dst += rc; - dst_counter += rc; - - /* Encode P0 (see also: 3GPP TS 44.065, 6.6.2.1, Table 7a) */ - OSMO_ASSERT(params->p0 >= 0); - OSMO_ASSERT(params->p0 <= 3); - *dst = params->p0 & 0x03; - dst++; - dst_counter++; - - /* Encode P1 (see also: 3GPP TS 44.065, 6.6.2.1, Table 7a) */ - OSMO_ASSERT(params->p1 >= 512); - OSMO_ASSERT(params->p1 <= 65535); - *dst = (params->p1 >> 8) & 0xFF; - dst++; - *dst = params->p1 & 0xFF; - dst++; - dst_counter += 2; - - /* Encode P2 (see also: 3GPP TS 44.065, 6.6.2.1, Table 7a) */ - OSMO_ASSERT(params->p2 >= 6); - OSMO_ASSERT(params->p2 <= 250); - *dst = params->p2; - dst++; - dst_counter++; - - /* Return generated length */ - return dst_counter; -} - -/* Encode V44 parameter field - * (see also: 3GPP TS 44.065, 6.6.3.1, Table 7c) */ -static int encode_dcomp_v44_params(uint8_t *dst, unsigned int dst_maxlen, - const struct gprs_sndcp_dcomp_v44_params - *params) -{ - /* NOTE: Buffer *dst should offer at least 12 bytes - * of space to store the generation results */ - - int dst_counter = 0; - int rc; - - OSMO_ASSERT(dst_maxlen >= 12); - - /* Zero out buffer */ - memset(dst, 0, dst_maxlen); - - /* Encode applicable SAPIs */ - rc = encode_pcomp_applicable_sapis(dst, params->nsapi, - params->nsapi_len); - dst += rc; - dst_counter += rc; - - /* Encode C0 (see also: 3GPP TS 44.065, 6.6.3.1, Table 7c) */ - OSMO_ASSERT(params->c0 == 0x80 || params->c0 == 0xC0); - *dst = params->c0 & 0xC0; - dst++; - dst_counter++; - - /* Encode P0 (see also: 3GPP TS 44.065, 6.6.3.1, Table 7c) */ - OSMO_ASSERT(params->p0 >= 0); - OSMO_ASSERT(params->p0 <= 3); - *dst = params->p0 & 0x03; - dst++; - dst_counter++; - - /* Encode P1T (see also: 3GPP TS 44.065, 6.6.3.1, Table 7c) */ - OSMO_ASSERT(params->p1t >= 256); - OSMO_ASSERT(params->p1t <= 65535); - *dst = (params->p1t >> 8) & 0xFF; - dst++; - *dst = params->p1t & 0xFF; - dst++; - dst_counter += 2; - - /* Encode P1R (see also: 3GPP TS 44.065, 6.6.3.1, Table 7c) */ - OSMO_ASSERT(params->p1r >= 256); - OSMO_ASSERT(params->p1r <= 65535); - *dst = (params->p1r >> 8) & 0xFF; - dst++; - *dst = params->p1r & 0xFF; - dst++; - dst_counter += 2; - - /* Encode P3T (see also: 3GPP TS 44.065, 6.6.3.1, Table 7c) */ - OSMO_ASSERT(params->p3t >= 0); - OSMO_ASSERT(params->p3t <= 65535); - OSMO_ASSERT(params->p3t >= 2 * params->p1t); - *dst = (params->p3t >> 8) & 0xFF; - dst++; - *dst = params->p3t & 0xFF; - dst++; - dst_counter += 2; - - /* Encode P3R (see also: 3GPP TS 44.065, 6.6.3.1, Table 7c) */ - OSMO_ASSERT(params->p3r >= 0); - OSMO_ASSERT(params->p3r <= 65535); - OSMO_ASSERT(params->p3r >= 2 * params->p1r); - *dst = (params->p3r >> 8) & 0xFF; - dst++; - *dst = params->p3r & 0xFF; - dst++; - dst_counter += 2; - - /* Return generated length */ - return dst_counter; -} - -/* Encode data or protocol control information compression field - * (see also: 3GPP TS 44.065, 6.6.1.1, Figure 9 and - * 3GPP TS 44.065, 6.5.1.1, Figure 7) */ -static int encode_comp_field(uint8_t *dst, unsigned int dst_maxlen, - const struct gprs_sndcp_comp_field *comp_field) -{ - int dst_counter = 0; - int len; - int expected_length; - int i; - - uint8_t payload_bytes[256]; - int payload_bytes_len = -1; - - /* If possible, try do encode payload bytes first */ - if (comp_field->rfc1144_params) { - payload_bytes_len = - encode_pcomp_rfc1144_params(payload_bytes, - sizeof(payload_bytes), - comp_field->rfc1144_params); - } else if (comp_field->rfc2507_params) { - payload_bytes_len = - encode_pcomp_rfc2507_params(payload_bytes, - sizeof(payload_bytes), - comp_field->rfc2507_params); - } else if (comp_field->rohc_params) { - payload_bytes_len = - encode_pcomp_rohc_params(payload_bytes, - sizeof(payload_bytes), - comp_field->rohc_params); - } else if (comp_field->v42bis_params) { - payload_bytes_len = - encode_dcomp_v42bis_params(payload_bytes, - sizeof(payload_bytes), - comp_field->v42bis_params); - } else if (comp_field->v44_params) { - payload_bytes_len = - encode_dcomp_v44_params(payload_bytes, - sizeof(payload_bytes), - comp_field->v44_params); - } else - OSMO_ASSERT(false); - - /* Bail immediately if payload byte generation failed */ - OSMO_ASSERT(payload_bytes_len >= 0); - - /* Bail if comp_len is out of bounds */ - OSMO_ASSERT(comp_field->comp_len <= sizeof(comp_field->comp)); - - /* Calculate length field of the data block */ - if (comp_field->p) { - len = - payload_bytes_len + - ceil((double)(comp_field->comp_len) / 2.0); - expected_length = len + 3; - } else { - len = payload_bytes_len; - expected_length = len + 2; - } - - /* Bail immediately if no sufficient memory space is supplied */ - OSMO_ASSERT(dst_maxlen >= expected_length); - - /* Check if the entity number is within bounds */ - OSMO_ASSERT(comp_field->entity <= 0x1f); - - /* Check if the algorithm number is within bounds */ - OSMO_ASSERT(comp_field->algo >= 0 || comp_field->algo <= 0x1f); - - /* Zero out buffer */ - memset(dst, 0, dst_maxlen); - - /* Encode Propose bit */ - if (comp_field->p) - *dst |= (1 << 7); - - /* Encode entity number */ - *dst |= comp_field->entity & 0x1F; - dst++; - dst_counter++; - - /* Encode algorithm number */ - if (comp_field->p) { - *dst |= comp_field->algo & 0x1F; - dst++; - dst_counter++; - } - - /* Encode length field */ - *dst |= len & 0xFF; - dst++; - dst_counter++; - - /* Encode PCOMP/DCOMP values */ - if (comp_field->p) { - for (i = 0; i < comp_field->comp_len; i++) { - /* Check if submitted PCOMP/DCOMP - values are within bounds */ - if (comp_field->comp[i] > 0x0F) - return -EINVAL; - - if (i & 1) { - *dst |= comp_field->comp[i] & 0x0F; - dst++; - dst_counter++; - } else - *dst |= (comp_field->comp[i] << 4) & 0xF0; - } - - if (i & 1) { - dst++; - dst_counter++; - } - } - - /* Append payload bytes */ - memcpy(dst, payload_bytes, payload_bytes_len); - dst_counter += payload_bytes_len; - - /* Return generated length */ - return dst_counter; -} - -/* Find out to which compression class the specified comp-field belongs - * (header compression or data compression?) */ -int gprs_sndcp_get_compression_class(const struct gprs_sndcp_comp_field - *comp_field) -{ - OSMO_ASSERT(comp_field); - - if (comp_field->rfc1144_params) - return SNDCP_XID_PROTOCOL_COMPRESSION; - else if (comp_field->rfc2507_params) - return SNDCP_XID_PROTOCOL_COMPRESSION; - else if (comp_field->rohc_params) - return SNDCP_XID_PROTOCOL_COMPRESSION; - else if (comp_field->v42bis_params) - return SNDCP_XID_DATA_COMPRESSION; - else if (comp_field->v44_params) - return SNDCP_XID_DATA_COMPRESSION; - else - return -EINVAL; -} - -/* Convert all compression fields to bytstreams */ -static int gprs_sndcp_pack_fields(const struct llist_head *comp_fields, - uint8_t *dst, - unsigned int dst_maxlen, int class) -{ - struct gprs_sndcp_comp_field *comp_field; - int byte_counter = 0; - int rc; - - llist_for_each_entry_reverse(comp_field, comp_fields, list) { - if (class == gprs_sndcp_get_compression_class(comp_field)) { - rc = encode_comp_field(dst + byte_counter, - dst_maxlen - byte_counter, - comp_field); - - /* When input data is correct, there is - * no reason for the encoder to fail! */ - OSMO_ASSERT(rc >= 0); - - byte_counter += rc; - } - } - - /* Return generated length */ - return byte_counter; -} - -/* Transform a list with compression fields into an SNDCP-XID message (dst) */ -int gprs_sndcp_compile_xid(uint8_t *dst, unsigned int dst_maxlen, - const struct llist_head *comp_fields, int version) -{ - int rc; - int byte_counter = 0; - uint8_t comp_bytes[512]; - uint8_t xid_version_number[1]; - - OSMO_ASSERT(comp_fields); - OSMO_ASSERT(dst); - OSMO_ASSERT(dst_maxlen >= 2 + sizeof(xid_version_number)); - - /* Prepend header with version number */ - if (version >= 0) { - xid_version_number[0] = (uint8_t) (version & 0xff); - dst = - tlv_put(dst, SNDCP_XID_VERSION_NUMBER, - sizeof(xid_version_number), xid_version_number); - byte_counter += (sizeof(xid_version_number) + 2); - } - - /* Stop if there is no compression fields supplied */ - if (llist_empty(comp_fields)) - return byte_counter; - - /* Add data compression fields */ - rc = gprs_sndcp_pack_fields(comp_fields, comp_bytes, - sizeof(comp_bytes), - SNDCP_XID_DATA_COMPRESSION); - OSMO_ASSERT(rc >= 0); - - if (rc > 0) { - dst = tlv_put(dst, SNDCP_XID_DATA_COMPRESSION, rc, comp_bytes); - byte_counter += rc + 2; - } - - /* Add header compression fields */ - rc = gprs_sndcp_pack_fields(comp_fields, comp_bytes, - sizeof(comp_bytes), - SNDCP_XID_PROTOCOL_COMPRESSION); - OSMO_ASSERT(rc >= 0); - - if (rc > 0) { - dst = tlv_put(dst, SNDCP_XID_PROTOCOL_COMPRESSION, rc, - comp_bytes); - byte_counter += rc + 2; - } - - /* Return generated length */ - return byte_counter; -} - -/* FUNCTIONS RELATED TO SNDCP-XID DECODING */ - -/* Decode applicable sapis (works the same in all three compression schemes) */ -static int decode_pcomp_applicable_sapis(uint8_t *nsapis, - uint8_t *nsapis_len, - const uint8_t *src, - unsigned int src_len) -{ - uint16_t blob; - int i; - int nsapi_len = 0; - - /* Exit immediately if no result can be stored */ - if (!nsapis) - return -EINVAL; - - /* Exit immediately if not enough input data is available */ - if (src_len < 2) - return -EINVAL; - - /* Read bitmask */ - blob = *src; - blob = (blob << 8) & 0xFF00; - src++; - blob |= (*src) & 0xFF; - blob = (blob >> 5); - - /* Decode applicable SAPIs */ - for (i = 0; i < 15; i++) { - if ((blob >> i) & 1) { - nsapis[nsapi_len] = i + 5; - nsapi_len++; - } - } - - /* Return consumed length */ - *nsapis_len = nsapi_len; - return 2; -} - -/* Decode 16 bit field */ -static int decode_pcomp_16_bit_field(int *value_int, uint16_t * value_uint16, - const uint8_t *src, - unsigned int src_len, - int value_min, int value_max) -{ - uint16_t blob; - - /* Reset values to zero (just to be sure) */ - if (value_int) - *value_int = -1; - if (value_uint16) - *value_uint16 = 0; - - /* Exit if not enough src are available */ - if (src_len < 2) - return -EINVAL; - - /* Decode bit value */ - blob = *src; - blob = (blob << 8) & 0xFF00; - src++; - blob |= *src; - - /* Check if parsed value is within bounds */ - if (blob < value_min) - return -EINVAL; - if (blob > value_max) - return -EINVAL; - - /* Hand back results to the caller */ - if (value_int) - *value_int = blob; - if (value_uint16) - *value_uint16 = blob; - - /* Return consumed length */ - return 2; -} - -/* Decode 8 bit field */ -static int decode_pcomp_8_bit_field(int *value_int, uint8_t *value_uint8, - const uint8_t *src, - unsigned int src_len, - int value_min, int value_max) -{ - uint8_t blob; - - /* Reset values to invalid (just to be sure) */ - if (value_int) - *value_int = -1; - if (value_uint8) - *value_uint8 = 0; - - /* Exit if not enough src are available */ - if (src_len < 1) - return -EINVAL; - - /* Decode bit value */ - blob = *src; - - /* Check if parsed value is within bounds */ - if (blob < value_min) - return -EINVAL; - if (blob > value_max) - return -EINVAL; - - /* Hand back results to the caller */ - if (value_int) - *value_int = blob; - if (value_uint8) - *value_uint8 = blob; - - /* Return consumed length */ - return 1; -} - -/* Decode rfc1144 parameter field see also: 3GPP TS 44.065, 6.5.2.1, Table 5) */ -static int decode_pcomp_rfc1144_params(struct gprs_sndcp_pcomp_rfc1144_params - *params, const uint8_t *src, - unsigned int src_len) -{ - int rc; - int byte_counter = 0; - - /* Mark all optional parameters invalid by default */ - params->s01 = -1; - - /* Decode applicable SAPIs */ - rc = decode_pcomp_applicable_sapis(params->nsapi, ¶ms->nsapi_len, - src, src_len); - if (rc > 0) { - byte_counter += rc; - src += rc; - } else - return byte_counter; - - /* Decode parameter S0 -1 - * (see also: 3GPP TS 44.065, 6.5.2.1, Table 5) */ - rc = decode_pcomp_8_bit_field(¶ms->s01, NULL, src, - src_len - byte_counter, 0, 255); - if (rc <= 0) - return byte_counter; - byte_counter += rc; - src += rc; - - /* Return consumed length */ - return byte_counter; -} - -/* Decode rfc2507 parameter field - * (see also: 3GPP TS 44.065, 6.5.3.1, Table 6) */ -static int decode_pcomp_rfc2507_params(struct gprs_sndcp_pcomp_rfc2507_params - *params, const uint8_t *src, - unsigned int src_len) -{ - int rc; - int byte_counter = 0; - - /* Mark all optional parameters invalid by default */ - params->f_max_period = -1; - params->f_max_time = -1; - params->max_header = -1; - params->tcp_space = -1; - params->non_tcp_space = -1; - - /* Decode applicable SAPIs */ - rc = decode_pcomp_applicable_sapis(params->nsapi, ¶ms->nsapi_len, - src, src_len); - if (rc > 0) { - byte_counter += rc; - src += rc; - } else - return byte_counter; - - /* Decode F_MAX_PERIOD (see also: 3GPP TS 44.065, 6.5.3.1, Table 6) */ - rc = decode_pcomp_16_bit_field(¶ms->f_max_period, NULL, src, - src_len - byte_counter, 1, 65535); - if (rc <= 0) - return byte_counter; - byte_counter += rc; - src += rc; - - /* Decode F_MAX_TIME (see also: 3GPP TS 44.065, 6.5.3.1, Table 6) */ - rc = decode_pcomp_8_bit_field(¶ms->f_max_time, NULL, src, - src_len - byte_counter, 1, 255); - if (rc <= 0) - return byte_counter; - byte_counter += rc; - src += rc; - - /* Decode MAX_HEADER (see also: 3GPP TS 44.065, 6.5.3.1, Table 6) */ - rc = decode_pcomp_8_bit_field(¶ms->max_header, NULL, src, - src_len - byte_counter, 60, 255); - if (rc <= 0) - return byte_counter; - byte_counter += rc; - src += rc; - - /* Decode TCP_SPACE (see also: 3GPP TS 44.065, 6.5.3.1, Table 6) */ - rc = decode_pcomp_8_bit_field(¶ms->tcp_space, NULL, src, - src_len - byte_counter, 3, 255); - if (rc <= 0) - return byte_counter; - byte_counter += rc; - src += rc; - - /* Decode NON_TCP_SPACE (see also: 3GPP TS 44.065, 6.5.3.1, Table 6) */ - rc = decode_pcomp_16_bit_field(¶ms->non_tcp_space, NULL, src, - src_len - byte_counter, 3, 65535); - if (rc <= 0) - return byte_counter; - byte_counter += rc; - src += rc; - - /* Return consumed length */ - return byte_counter; -} - -/* Decode ROHC parameter field (see also: 3GPP TS 44.065, 6.5.4.1, Table 10) */ -static int decode_pcomp_rohc_params(struct gprs_sndcp_pcomp_rohc_params *params, - const uint8_t *src, unsigned int src_len) -{ - int rc; - int byte_counter = 0; - int i; - - /* Mark all optional parameters invalid by default */ - params->max_cid = -1; - params->max_header = -1; - - /* Decode applicable SAPIs */ - rc = decode_pcomp_applicable_sapis(params->nsapi, ¶ms->nsapi_len, - src, src_len); - if (rc <= 0) - return byte_counter; - byte_counter += rc; - src += rc; - - /* Decode MAX_CID (see also: 3GPP TS 44.065, 6.5.4.1, Table 10) */ - rc = decode_pcomp_16_bit_field(¶ms->max_cid, NULL, src, - src_len - byte_counter, 0, 16383); - if (rc <= 0) - return byte_counter; - byte_counter += rc; - src += rc; - - /* Decode MAX_HEADER (see also: 3GPP TS 44.065, 6.5.4.1, Table 10) */ - rc = decode_pcomp_16_bit_field(¶ms->max_header, NULL, src, - src_len - byte_counter, 60, 255); - if (rc <= 0) - return byte_counter; - byte_counter += rc; - src += rc; - - /* Decode Profiles (see also: 3GPP TS 44.065, 6.5.4.1, Table 10) */ - for (i = 0; i < 16; i++) { - params->profile_len = 0; - rc = decode_pcomp_16_bit_field(NULL, ¶ms->profile[i], src, - src_len - byte_counter, 0, - 65535); - if (rc <= 0) - return byte_counter; - byte_counter += rc; - src += rc; - params->profile_len = i + 1; - } - - /* Return consumed length */ - return byte_counter; -} - -/* Decode V.42bis parameter field - * (see also: 3GPP TS 44.065, 6.6.2.1, Table 7a) */ -static int decode_dcomp_v42bis_params(struct gprs_sndcp_dcomp_v42bis_params - *params, const uint8_t *src, - unsigned int src_len) -{ - int rc; - int byte_counter = 0; - - /* Mark all optional parameters invalid by default */ - params->p0 = -1; - params->p1 = -1; - params->p2 = -1; - - /* Decode applicable SAPIs */ - rc = decode_pcomp_applicable_sapis(params->nsapi, ¶ms->nsapi_len, - src, src_len); - if (rc > 0) { - byte_counter += rc; - src += rc; - } else - return byte_counter; - - /* Decode P0 (see also: 3GPP TS 44.065, 6.6.2.1, Table 7a) */ - rc = decode_pcomp_8_bit_field(¶ms->p0, NULL, src, - src_len - byte_counter, 0, 3); - if (rc <= 0) - return byte_counter; - byte_counter += rc; - src += rc; - - /* Decode P1 (see also: 3GPP TS 44.065, 6.6.2.1, Table 7a) */ - rc = decode_pcomp_16_bit_field(¶ms->p1, NULL, src, - src_len - byte_counter, 512, 65535); - if (rc <= 0) - return byte_counter; - byte_counter += rc; - src += rc; - - /* Decode P2 (see also: 3GPP TS 44.065, 6.6.2.1, Table 7a) */ - rc = decode_pcomp_8_bit_field(¶ms->p2, NULL, src, - src_len - byte_counter, 6, 250); - if (rc <= 0) - return byte_counter; - byte_counter += rc; - src += rc; - - /* Return consumed length */ - return byte_counter; -} - -/* Decode V44 parameter field (see also: 3GPP TS 44.065, 6.6.3.1, Table 7c) */ -static int decode_dcomp_v44_params(struct gprs_sndcp_dcomp_v44_params *params, - const uint8_t *src, unsigned int src_len) -{ - int rc; - int byte_counter = 0; - - /* Mark all optional parameters invalid by default */ - params->c0 = -1; - params->p0 = -1; - params->p1t = -1; - params->p1r = -1; - params->p3t = -1; - params->p3r = -1; - - /* Decode applicable SAPIs */ - rc = decode_pcomp_applicable_sapis(params->nsapi, ¶ms->nsapi_len, - src, src_len); - if (rc > 0) { - byte_counter += rc; - src += rc; - } else - return byte_counter; - - /* Decode C0 (see also: 3GPP TS 44.065, 6.6.3.1, Table 7c) */ - rc = decode_pcomp_8_bit_field(¶ms->c0, NULL, src, - src_len - byte_counter, 0, 255); - if (rc <= 0) - return byte_counter; - if ((params->c0 != 0x80) && (params->c0 != 0xC0)) - return -EINVAL; - byte_counter += rc; - src += rc; - - /* Decode P0 (see also: 3GPP TS 44.065, 6.6.3.1, Table 7c) */ - rc = decode_pcomp_8_bit_field(¶ms->p0, NULL, src, - src_len - byte_counter, 0, 3); - if (rc <= 0) - return byte_counter; - byte_counter += rc; - src += rc; - - /* Decode P1T (see also: 3GPP TS 44.065, 6.6.3.1, Table 7c) */ - rc = decode_pcomp_16_bit_field(¶ms->p1t, NULL, src, - src_len - byte_counter, 265, 65535); - if (rc <= 0) - return byte_counter; - byte_counter += rc; - src += rc; - - /* Decode P1R (see also: 3GPP TS 44.065, 6.6.3.1, Table 7c) */ - rc = decode_pcomp_16_bit_field(¶ms->p1r, NULL, src, - src_len - byte_counter, 265, 65535); - if (rc <= 0) - return byte_counter; - byte_counter += rc; - src += rc; - - /* Decode P3T (see also: 3GPP TS 44.065, 6.6.3.1, Table 7c) */ - rc = decode_pcomp_16_bit_field(¶ms->p3t, NULL, src, - src_len - byte_counter, 265, 65535); - if (rc <= 0) - return byte_counter; - if (params->p3t < 2 * params->p1t) - return -EINVAL; - byte_counter += rc; - src += rc; - - /* Decode P3R (see also: 3GPP TS 44.065, 6.6.3.1, Table 7c) */ - rc = decode_pcomp_16_bit_field(¶ms->p3r, NULL, src, - src_len - byte_counter, 265, 65535); - if (rc <= 0) - return byte_counter; - if (params->p3r < 2 * params->p1r) - return -EINVAL; - byte_counter += rc; - src += rc; - - /* Return consumed length */ - return byte_counter; -} - -/* Lookup algorithm identfier by entity ID */ -static int lookup_algorithm_identifier(int entity, const struct - entity_algo_table - *lt, unsigned int lt_len, int compclass) -{ - int i; - - if (!lt) - return -1; - - for (i = 0; i < lt_len; i++) { - if ((lt[i].entity == entity) - && (lt[i].compclass == compclass)) - return lt[i].algo; - } - - return -1; -} - -/* Helper function for decode_comp_field(), decodes - * numeric pcomp/dcomp values */ -static int decode_comp_values(struct gprs_sndcp_comp_field *comp_field, - const uint8_t *src, int compclass) -{ - int src_counter = 0; - int i; - - if (comp_field->p) { - /* Determine the number of expected PCOMP/DCOMP values */ - if (compclass == SNDCP_XID_PROTOCOL_COMPRESSION) { - /* For protocol compression */ - switch (comp_field->algo) { - case RFC_1144: - comp_field->comp_len = RFC1144_PCOMP_NUM; - break; - case RFC_2507: - comp_field->comp_len = RFC2507_PCOMP_NUM; - break; - case ROHC: - comp_field->comp_len = ROHC_PCOMP_NUM; - break; - - /* Exit if the algorithem type encodes - something unknown / unspecified */ - default: - return -EINVAL; - } - } else { - /* For data compression */ - switch (comp_field->algo) { - case V42BIS: - comp_field->comp_len = V42BIS_DCOMP_NUM; - break; - case V44: - comp_field->comp_len = V44_DCOMP_NUM; - break; - - /* Exit if the algorithem type encodes - something unknown / unspecified */ - default: - return -EINVAL; - } - } - - for (i = 0; i < comp_field->comp_len; i++) { - if (i & 1) { - comp_field->comp[i] = (*src) & 0x0F; - src++; - src_counter++; - } else - comp_field->comp[i] = ((*src) >> 4) & 0x0F; - } - - if (i & 1) { - src++; - src_counter++; - } - } - - return src_counter; -} - -/* Helper function for decode_comp_field(), decodes the parameters - * which are protocol compression specific */ -static int decode_pcomp_params(struct gprs_sndcp_comp_field *comp_field, - const uint8_t *src, int src_len) -{ - int rc; - - switch (comp_field->algo) { - case RFC_1144: - comp_field->rfc1144_params = talloc_zero(comp_field, struct - gprs_sndcp_pcomp_rfc1144_params); - rc = decode_pcomp_rfc1144_params(comp_field->rfc1144_params, - src, src_len); - if (rc < 0) - talloc_free(comp_field->rfc1144_params); - break; - case RFC_2507: - comp_field->rfc2507_params = talloc_zero(comp_field, struct - gprs_sndcp_pcomp_rfc2507_params); - rc = decode_pcomp_rfc2507_params(comp_field->rfc2507_params, - src, src_len); - if (rc < 0) - talloc_free(comp_field->rfc1144_params); - break; - case ROHC: - comp_field->rohc_params = talloc_zero(comp_field, struct - gprs_sndcp_pcomp_rohc_params); - rc = decode_pcomp_rohc_params(comp_field->rohc_params, src, - src_len); - if (rc < 0) - talloc_free(comp_field->rohc_params); - break; - - /* If no suitable decoder is detected, - leave the remaining bytes undecoded */ - default: - rc = src_len; - } - - if (rc < 0) { - comp_field->rfc1144_params = NULL; - comp_field->rfc2507_params = NULL; - comp_field->rohc_params = NULL; - } - - return rc; -} - -/* Helper function for decode_comp_field(), decodes the parameters - * which are data compression specific */ -static int decode_dcomp_params(struct gprs_sndcp_comp_field *comp_field, - const uint8_t *src, int src_len) -{ - int rc; - - switch (comp_field->algo) { - case V42BIS: - comp_field->v42bis_params = talloc_zero(comp_field, struct - gprs_sndcp_dcomp_v42bis_params); - rc = decode_dcomp_v42bis_params(comp_field->v42bis_params, src, - src_len); - if (rc < 0) - talloc_free(comp_field->v42bis_params); - break; - case V44: - comp_field->v44_params = talloc_zero(comp_field, struct - gprs_sndcp_dcomp_v44_params); - rc = decode_dcomp_v44_params(comp_field->v44_params, src, - src_len); - if (rc < 0) - talloc_free(comp_field->v44_params); - break; - - /* If no suitable decoder is detected, - * leave the remaining bytes undecoded */ - default: - rc = src_len; - } - - if (rc < 0) { - comp_field->v42bis_params = NULL; - comp_field->v44_params = NULL; - } - - return rc; -} - -/* Decode data or protocol control information compression field - * (see also: 3GPP TS 44.065, 6.6.1.1, Figure 9 and - * 3GPP TS 44.065, 6.5.1.1, Figure 7) */ -static int decode_comp_field(struct gprs_sndcp_comp_field *comp_field, - const uint8_t *src, unsigned int src_len, - const struct entity_algo_table *lt, - unsigned int lt_len, int compclass) -{ - int src_counter = 0; - unsigned int len; - int rc; - - OSMO_ASSERT(comp_field); - - /* Exit immediately if it is clear that no - parseable data is present */ - if (src_len < 1 || !src) - return -EINVAL; - - /* Zero out target struct */ - memset(comp_field, 0, sizeof(struct gprs_sndcp_comp_field)); - - /* Decode Propose bit and Entity number */ - if ((*src) & 0x80) - comp_field->p = 1; - comp_field->entity = (*src) & 0x1F; - src_counter++; - src++; - - /* Decode algorithm number (if present) */ - if (comp_field->p) { - comp_field->algo = (*src) & 0x1F; - src_counter++; - src++; - } - /* Alternatively take the information from the lookup table */ - else - comp_field->algo = - lookup_algorithm_identifier(comp_field->entity, lt, - lt_len, compclass); - - /* Decode length field */ - len = *src; - src_counter++; - src++; - - /* Decode PCOMP/DCOMP values */ - rc = decode_comp_values(comp_field, src, compclass); - if (rc < 0) - return -EINVAL; - src_counter += rc; - src += rc; - len -= rc; - - /* Decode algorithm specific payload data */ - if (compclass == SNDCP_XID_PROTOCOL_COMPRESSION) - rc = decode_pcomp_params(comp_field, src, len); - else if (compclass == SNDCP_XID_DATA_COMPRESSION) - rc = decode_dcomp_params(comp_field, src, len); - else - return -EINVAL; - - if (rc >= 0) - src_counter += rc; - else - return -EINVAL; - - /* Return consumed length */ - return src_counter; -} - -/* Helper function for gprs_sndcp_decode_xid() to decode XID blocks */ -static int decode_xid_block(struct llist_head *comp_fields, uint8_t tag, - uint16_t tag_len, const uint8_t *val, - const struct entity_algo_table *lt, - unsigned int lt_len) -{ - struct gprs_sndcp_comp_field *comp_field; - int byte_counter = 0; - int comp_field_count = 0; - int rc; - - byte_counter = 0; - do { - /* Bail if more than the maximum number of - comp_fields is generated */ - if (comp_field_count > MAX_ENTITIES * 2) { - return -EINVAL; - } - - /* Parse and add comp_field */ - comp_field = - talloc_zero(comp_fields, struct gprs_sndcp_comp_field); - - rc = decode_comp_field(comp_field, val + byte_counter, - tag_len - byte_counter, lt, lt_len, tag); - - if (rc < 0) { - talloc_free(comp_field); - return -EINVAL; - } - - byte_counter += rc; - llist_add(&comp_field->list, comp_fields); - comp_field_count++; - } - while (tag_len - byte_counter > 0); - - return byte_counter; -} - -/* Transform an SNDCP-XID message (src) into a list of SNDCP-XID fields */ -static int gprs_sndcp_decode_xid(int *version, struct llist_head *comp_fields, - const uint8_t *src, unsigned int src_len, - const struct entity_algo_table *lt, - unsigned int lt_len) -{ - int src_pos = 0; - uint8_t tag; - uint16_t tag_len; - const uint8_t *val; - int byte_counter = 0; - int rc; - int tlv_count = 0; - - /* Preset version value as invalid */ - if (version) - *version = -1; - - /* Valid TLV-Tag and types */ - static const struct tlv_definition sndcp_xid_def = { - .def = { - [SNDCP_XID_VERSION_NUMBER] = {TLV_TYPE_TLV,}, - [SNDCP_XID_DATA_COMPRESSION] = {TLV_TYPE_TLV,}, - [SNDCP_XID_PROTOCOL_COMPRESSION] = {TLV_TYPE_TLV,}, - }, - }; - - /* Parse TLV-Encoded SNDCP-XID message and defer payload - to the apporpiate sub-parser functions */ - while (1) { - - /* Bail if an the maximum number of TLV fields - * have been parsed */ - if (tlv_count >= 3) { - talloc_free(comp_fields); - return -EINVAL; - } - - /* Parse TLV field */ - rc = tlv_parse_one(&tag, &tag_len, &val, &sndcp_xid_def, - src + src_pos, src_len - src_pos); - if (rc > 0) - src_pos += rc; - else { - talloc_free(comp_fields); - return -EINVAL; - } - - /* Decode sndcp xid version number */ - if (version && tag == SNDCP_XID_VERSION_NUMBER) - *version = val[0]; - - /* Decode compression parameters */ - if ((tag == SNDCP_XID_PROTOCOL_COMPRESSION) - || (tag == SNDCP_XID_DATA_COMPRESSION)) { - rc = decode_xid_block(comp_fields, tag, tag_len, val, - lt, lt_len); - - if (rc < 0) { - talloc_free(comp_fields); - return -EINVAL; - } else - byte_counter += rc; - } - - /* Stop when no further TLV elements can be expected */ - if (src_len - src_pos <= 2) - break; - - tlv_count++; - } - - return 0; -} - -/* Fill up lookutable from a list with comression entitiy fields */ -static int gprs_sndcp_fill_table(struct - entity_algo_table *lt, - unsigned int lt_len, - const struct llist_head *comp_fields) -{ - struct gprs_sndcp_comp_field *comp_field; - int i = 0; - int rc; - - if (!comp_fields) - return -EINVAL; - if (!lt) - return -EINVAL; - - memset(lt, 0, sizeof(*lt)); - - llist_for_each_entry(comp_field, comp_fields, list) { - if (comp_field->algo >= 0) { - lt[i].entity = comp_field->entity; - lt[i].algo = comp_field->algo; - rc = gprs_sndcp_get_compression_class(comp_field); - - if (rc < 0) { - memset(lt, 0, sizeof(*lt)); - return -EINVAL; - } - - lt[i].compclass = rc; - i++; - } - } - - return i; -} - -/* Complete comp field params - * (if a param (dst) is not valid, it will be copied from source (src) */ -static int complete_comp_field_params(struct gprs_sndcp_comp_field - *comp_field_dst, const struct - gprs_sndcp_comp_field *comp_field_src) -{ - if (comp_field_dst->algo < 0) - return -EINVAL; - - if (comp_field_dst->rfc1144_params && comp_field_src->rfc1144_params) { - if (comp_field_dst->rfc1144_params->s01 < 0) { - comp_field_dst->rfc1144_params->s01 = - comp_field_src->rfc1144_params->s01; - } - return 0; - } - - if (comp_field_dst->rfc2507_params && comp_field_src->rfc2507_params) { - - if (comp_field_dst->rfc2507_params->f_max_period < 0) { - comp_field_dst->rfc2507_params->f_max_period = - comp_field_src->rfc2507_params->f_max_period; - } - if (comp_field_dst->rfc2507_params->f_max_time < 0) { - comp_field_dst->rfc2507_params->f_max_time = - comp_field_src->rfc2507_params->f_max_time; - } - if (comp_field_dst->rfc2507_params->max_header < 0) { - comp_field_dst->rfc2507_params->max_header = - comp_field_src->rfc2507_params->max_header; - } - if (comp_field_dst->rfc2507_params->tcp_space < 0) { - comp_field_dst->rfc2507_params->tcp_space = - comp_field_src->rfc2507_params->tcp_space; - } - if (comp_field_dst->rfc2507_params->non_tcp_space < 0) { - comp_field_dst->rfc2507_params->non_tcp_space = - comp_field_src->rfc2507_params->non_tcp_space; - } - return 0; - } - - if (comp_field_dst->rohc_params && comp_field_src->rohc_params) { - if (comp_field_dst->rohc_params->max_cid < 0) { - comp_field_dst->rohc_params->max_cid = - comp_field_src->rohc_params->max_cid; - } - if (comp_field_dst->rohc_params->max_header < 0) { - comp_field_dst->rohc_params->max_header = - comp_field_src->rohc_params->max_header; - } - if (comp_field_dst->rohc_params->profile_len > 0) { - memcpy(comp_field_dst->rohc_params->profile, - comp_field_src->rohc_params->profile, - sizeof(comp_field_dst->rohc_params->profile)); - comp_field_dst->rohc_params->profile_len = - comp_field_src->rohc_params->profile_len; - } - - return 0; - } - - if (comp_field_dst->v42bis_params && comp_field_src->v42bis_params) { - if (comp_field_dst->v42bis_params->p0 < 0) { - comp_field_dst->v42bis_params->p0 = - comp_field_src->v42bis_params->p0; - } - if (comp_field_dst->v42bis_params->p1 < 0) { - comp_field_dst->v42bis_params->p1 = - comp_field_src->v42bis_params->p1; - } - if (comp_field_dst->v42bis_params->p2 < 0) { - comp_field_dst->v42bis_params->p2 = - comp_field_src->v42bis_params->p2; - } - return 0; - } - - if (comp_field_dst->v44_params && comp_field_src->v44_params) { - if (comp_field_dst->v44_params->c0 < 0) { - comp_field_dst->v44_params->c0 = - comp_field_src->v44_params->c0; - } - if (comp_field_dst->v44_params->p0 < 0) { - comp_field_dst->v44_params->p0 = - comp_field_src->v44_params->p0; - } - if (comp_field_dst->v44_params->p1t < 0) { - comp_field_dst->v44_params->p1t = - comp_field_src->v44_params->p1t; - } - if (comp_field_dst->v44_params->p1r < 0) { - comp_field_dst->v44_params->p1r = - comp_field_src->v44_params->p1r; - } - if (comp_field_dst->v44_params->p3t < 0) { - comp_field_dst->v44_params->p3t = - comp_field_src->v44_params->p3t; - } - if (comp_field_dst->v44_params->p3r < 0) { - comp_field_dst->v44_params->p3r = - comp_field_src->v44_params->p3r; - } - return 0; - } - - /* There should be at least exist one param set - * in the destination struct, otherwise something - * must be wrong! */ - return -EINVAL; -} - -/* Complete missing parameters in a comp_field */ -static int gprs_sndcp_complete_comp_field(struct gprs_sndcp_comp_field - *comp_field, const struct llist_head - *comp_fields) -{ - struct gprs_sndcp_comp_field *comp_field_src; - int rc = 0; - - llist_for_each_entry(comp_field_src, comp_fields, list) { - if (comp_field_src->entity == comp_field->entity) { - - /* Complete header fields */ - if (comp_field_src->comp_len > 0) { - memcpy(comp_field->comp, - comp_field_src->comp, - sizeof(comp_field_src->comp)); - comp_field->comp_len = comp_field_src->comp_len; - } - - /* Complete parameter fields */ - rc = complete_comp_field_params(comp_field, - comp_field_src); - } - } - - return rc; -} - -/* Complete missing parameters of all comp_field in a list */ -static int gprs_sndcp_complete_comp_fields(struct llist_head - *comp_fields_incomplete, - const struct llist_head *comp_fields) -{ - struct gprs_sndcp_comp_field *comp_field_incomplete; - int rc; - - llist_for_each_entry(comp_field_incomplete, comp_fields_incomplete, - list) { - - rc = gprs_sndcp_complete_comp_field(comp_field_incomplete, - comp_fields); - if (rc < 0) - return -EINVAL; - - } - - return 0; -} - -/* Transform an SNDCP-XID message (src) into a list of SNDCP-XID fields */ -struct llist_head *gprs_sndcp_parse_xid(int *version, - const void *ctx, - const uint8_t *src, - unsigned int src_len, - const struct llist_head - *comp_fields_req) -{ - int rc; - int lt_len; - struct llist_head *comp_fields; - struct entity_algo_table lt[MAX_ENTITIES * 2]; - - /* In case of a zero length field, just exit */ - if (src_len == 0) - return NULL; - - /* We should go any further if we have a field length greater - * zero and a null pointer as buffer! */ - OSMO_ASSERT(src); - - comp_fields = talloc_zero(ctx, struct llist_head); - INIT_LLIST_HEAD(comp_fields); - - if (comp_fields_req) { - /* Generate lookup table */ - lt_len = - gprs_sndcp_fill_table(lt, MAX_ENTITIES * 2, - comp_fields_req); - if (lt_len < 0) { - talloc_free(comp_fields); - return NULL; - } - - /* Parse SNDCP-CID XID-Field */ - rc = gprs_sndcp_decode_xid(version, comp_fields, src, src_len, - lt, lt_len); - if (rc < 0) { - talloc_free(comp_fields); - return NULL; - } - - rc = gprs_sndcp_complete_comp_fields(comp_fields, - comp_fields_req); - if (rc < 0) { - talloc_free(comp_fields); - return NULL; - } - - } else { - /* Parse SNDCP-CID XID-Field */ - rc = gprs_sndcp_decode_xid(version, comp_fields, src, src_len, - NULL, 0); - if (rc < 0) { - talloc_free(comp_fields); - return NULL; - } - } - - return comp_fields; -} - -/* Helper for gprs_sndcp_dump_comp_fields(), - * dumps protocol compression parameters */ -static void dump_pcomp_params(const struct gprs_sndcp_comp_field - *comp_field, unsigned int logl) -{ - int i; - - switch (comp_field->algo) { - case RFC_1144: - if (comp_field->rfc1144_params == NULL) { - LOGP(DSNDCP, logl, - " gprs_sndcp_pcomp_rfc1144_params=NULL\n"); - break; - } - LOGP(DSNDCP, logl, " gprs_sndcp_pcomp_rfc1144_params {\n"); - LOGP(DSNDCP, logl, - " nsapi_len=%d;\n", - comp_field->rfc1144_params->nsapi_len); - if (comp_field->rfc1144_params->nsapi_len == 0) - LOGP(DSNDCP, logl, " nsapi[] = NULL;\n"); - for (i = 0; i < comp_field->rfc1144_params->nsapi_len; i++) { - LOGP(DSNDCP, logl, - " nsapi[%d]=%d;\n", i, - comp_field->rfc1144_params->nsapi[i]); - } - LOGP(DSNDCP, logl, " s01=%d;\n", - comp_field->rfc1144_params->s01); - LOGP(DSNDCP, logl, " }\n"); - break; - case RFC_2507: - if (comp_field->rfc2507_params == NULL) { - LOGP(DSNDCP, logl, - " gprs_sndcp_pcomp_rfc2507_params=NULL\n"); - break; - } - LOGP(DSNDCP, logl, " gprs_sndcp_pcomp_rfc2507_params {\n"); - LOGP(DSNDCP, logl, - " nsapi_len=%d;\n", - comp_field->rfc2507_params->nsapi_len); - if (comp_field->rfc2507_params->nsapi_len == 0) - LOGP(DSNDCP, logl, " nsapi[] = NULL;\n"); - for (i = 0; i < comp_field->rfc2507_params->nsapi_len; i++) { - LOGP(DSNDCP, logl, - " nsapi[%d]=%d;\n", i, - comp_field->rfc2507_params->nsapi[i]); - } - LOGP(DSNDCP, logl, - " f_max_period=%d;\n", - comp_field->rfc2507_params->f_max_period); - LOGP(DSNDCP, logl, - " f_max_time=%d;\n", - comp_field->rfc2507_params->f_max_time); - LOGP(DSNDCP, logl, - " max_header=%d;\n", - comp_field->rfc2507_params->max_header); - LOGP(DSNDCP, logl, - " tcp_space=%d;\n", - comp_field->rfc2507_params->tcp_space); - LOGP(DSNDCP, logl, - " non_tcp_space=%d;\n", - comp_field->rfc2507_params->non_tcp_space); - LOGP(DSNDCP, logl, " }\n"); - break; - case ROHC: - if (comp_field->rohc_params == NULL) { - LOGP(DSNDCP, logl, - " gprs_sndcp_pcomp_rohc_params=NULL\n"); - break; - } - LOGP(DSNDCP, logl, " gprs_sndcp_pcomp_rohc_params {\n"); - LOGP(DSNDCP, logl, - " nsapi_len=%d;\n", - comp_field->rohc_params->nsapi_len); - if (comp_field->rohc_params->nsapi_len == 0) - LOGP(DSNDCP, logl, " nsapi[] = NULL;\n"); - for (i = 0; i < comp_field->rohc_params->nsapi_len; i++) { - LOGP(DSNDCP, logl, - " nsapi[%d]=%d;\n", i, - comp_field->rohc_params->nsapi[i]); - } - LOGP(DSNDCP, logl, - " max_cid=%d;\n", comp_field->rohc_params->max_cid); - LOGP(DSNDCP, logl, - " max_header=%d;\n", - comp_field->rohc_params->max_header); - LOGP(DSNDCP, logl, - " profile_len=%d;\n", - comp_field->rohc_params->profile_len); - if (comp_field->rohc_params->profile_len == 0) - LOGP(DSNDCP, logl, " profile[] = NULL;\n"); - for (i = 0; i < comp_field->rohc_params->profile_len; i++) - LOGP(DSNDCP, logl, - " profile[%d]=%04x;\n", - i, comp_field->rohc_params->profile[i]); - LOGP(DSNDCP, logl, " }\n"); - break; - } - -} - -/* Helper for gprs_sndcp_dump_comp_fields(), - * data protocol compression parameters */ -static void dump_dcomp_params(const struct gprs_sndcp_comp_field - *comp_field, unsigned int logl) -{ - int i; - - switch (comp_field->algo) { - case V42BIS: - if (comp_field->v42bis_params == NULL) { - LOGP(DSNDCP, logl, - " gprs_sndcp_dcomp_v42bis_params=NULL\n"); - break; - } - LOGP(DSNDCP, logl, " gprs_sndcp_dcomp_v42bis_params {\n"); - LOGP(DSNDCP, logl, - " nsapi_len=%d;\n", - comp_field->v42bis_params->nsapi_len); - if (comp_field->v42bis_params->nsapi_len == 0) - LOGP(DSNDCP, logl, " nsapi[] = NULL;\n"); - for (i = 0; i < comp_field->v42bis_params->nsapi_len; i++) - LOGP(DSNDCP, logl, - " nsapi[%d]=%d;\n", i, - comp_field->v42bis_params->nsapi[i]); - LOGP(DSNDCP, logl, " p0=%d;\n", - comp_field->v42bis_params->p0); - LOGP(DSNDCP, logl, " p1=%d;\n", - comp_field->v42bis_params->p1); - LOGP(DSNDCP, logl, " p2=%d;\n", - comp_field->v42bis_params->p2); - LOGP(DSNDCP, logl, " }\n"); - break; - case V44: - if (comp_field->v44_params == NULL) { - LOGP(DSNDCP, logl, - " gprs_sndcp_dcomp_v44_params=NULL\n"); - break; - } - LOGP(DSNDCP, logl, " gprs_sndcp_dcomp_v44_params {\n"); - LOGP(DSNDCP, logl, - " nsapi_len=%d;\n", - comp_field->v44_params->nsapi_len); - if (comp_field->v44_params->nsapi_len == 0) - LOGP(DSNDCP, logl, " nsapi[] = NULL;\n"); - for (i = 0; i < comp_field->v44_params->nsapi_len; i++) { - LOGP(DSNDCP, logl, - " nsapi[%d]=%d;\n", i, - comp_field->v44_params->nsapi[i]); - } - LOGP(DSNDCP, logl, " c0=%d;\n", - comp_field->v44_params->c0); - LOGP(DSNDCP, logl, " p0=%d;\n", - comp_field->v44_params->p0); - LOGP(DSNDCP, logl, " p1t=%d;\n", - comp_field->v44_params->p1t); - LOGP(DSNDCP, logl, " p1r=%d;\n", - comp_field->v44_params->p1r); - LOGP(DSNDCP, logl, " p3t=%d;\n", - comp_field->v44_params->p3t); - LOGP(DSNDCP, logl, " p3r=%d;\n", - comp_field->v44_params->p3r); - LOGP(DSNDCP, logl, " }\n"); - break; - } -} - -/* Dump a list with SNDCP-XID fields (Debug) */ -void gprs_sndcp_dump_comp_fields(const struct llist_head *comp_fields, - unsigned int logl) -{ - struct gprs_sndcp_comp_field *comp_field; - int i; - int compclass; - - OSMO_ASSERT(comp_fields); - - llist_for_each_entry(comp_field, comp_fields, list) { - LOGP(DSNDCP, logl, "SNDCP-XID:\n"); - LOGP(DSNDCP, logl, "struct gprs_sndcp_comp_field {\n"); - LOGP(DSNDCP, logl, " entity=%d;\n", comp_field->entity); - LOGP(DSNDCP, logl, " algo=%d;\n", comp_field->algo); - LOGP(DSNDCP, logl, " comp_len=%d;\n", comp_field->comp_len); - if (comp_field->comp_len == 0) - LOGP(DSNDCP, logl, " comp[] = NULL;\n"); - for (i = 0; i < comp_field->comp_len; i++) { - LOGP(DSNDCP, logl, " comp[%d]=%d;\n", i, - comp_field->comp[i]); - } - - compclass = gprs_sndcp_get_compression_class(comp_field); - - if (compclass == SNDCP_XID_PROTOCOL_COMPRESSION) { - dump_pcomp_params(comp_field, logl); - } else if (compclass == SNDCP_XID_DATA_COMPRESSION) { - dump_dcomp_params(comp_field, logl); - } - - LOGP(DSNDCP, logl, "}\n"); - } - -} diff --git a/openbsc/src/gprs/gprs_subscriber.c b/openbsc/src/gprs/gprs_subscriber.c deleted file mode 100644 index 1bb51418..00000000 --- a/openbsc/src/gprs/gprs_subscriber.c +++ /dev/null @@ -1,921 +0,0 @@ -/* MS subscriber data handling */ - -/* (C) 2014 by sysmocom s.f.m.c. GmbH - * (C) 2015 by Holger Hans Peter Freyther - * - * All Rights Reserved - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation; either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - * - */ - -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include - -#include - -#include -#include -#include - -#define SGSN_SUBSCR_MAX_RETRIES 3 -#define SGSN_SUBSCR_RETRY_INTERVAL 10 - -#define LOGGSUPP(level, gsup, fmt, args...) \ - LOGP(DGPRS, level, "GSUP(%s) " fmt, \ - (gsup)->imsi, \ - ## args) - -extern void *tall_bsc_ctx; - -LLIST_HEAD(_gprs_subscribers); -struct llist_head * const gprs_subscribers = &_gprs_subscribers; - -static int gsup_read_cb(struct gsup_client *gsupc, struct msgb *msg); - -/* TODO: Some functions are specific to the SGSN, but this file is more general - * (it has gprs_* name). Either move these functions elsewhere, split them and - * move a part, or replace the gprs_ prefix by sgsn_. The applies to - * gprs_subscr_init, gsup_read_cb, and gprs_subscr_tx_gsup_message. - */ - -int gprs_subscr_init(struct sgsn_instance *sgi) -{ - const char *addr_str; - - if (!sgi->cfg.gsup_server_addr.sin_addr.s_addr) - return 0; - - addr_str = inet_ntoa(sgi->cfg.gsup_server_addr.sin_addr); - - sgi->gsup_client = gsup_client_create( - addr_str, sgi->cfg.gsup_server_port, - &gsup_read_cb, - &sgi->cfg.oap); - - if (!sgi->gsup_client) - return -1; - - return 1; -} - -static int gsup_read_cb(struct gsup_client *gsupc, struct msgb *msg) -{ - int rc; - - rc = gprs_subscr_rx_gsup_message(msg); - msgb_free(msg); - if (rc < 0) - return -1; - - return rc; -} - -int gprs_subscr_purge(struct gprs_subscr *subscr); - -static struct sgsn_subscriber_data *sgsn_subscriber_data_alloc(void *ctx) -{ - struct sgsn_subscriber_data *sdata; - int idx; - - sdata = talloc_zero(ctx, struct sgsn_subscriber_data); - - sdata->error_cause = SGSN_ERROR_CAUSE_NONE; - - for (idx = 0; idx < ARRAY_SIZE(sdata->auth_triplets); idx++) - sdata->auth_triplets[idx].key_seq = GSM_KEY_SEQ_INVAL; - - INIT_LLIST_HEAD(&sdata->pdp_list); - - return sdata; -} - -struct sgsn_subscriber_pdp_data* sgsn_subscriber_pdp_data_alloc( - struct sgsn_subscriber_data *sdata) -{ - struct sgsn_subscriber_pdp_data* pdata; - - pdata = talloc_zero(sdata, struct sgsn_subscriber_pdp_data); - - llist_add_tail(&pdata->list, &sdata->pdp_list); - - return pdata; -} - -struct gprs_subscr *gprs_subscr_get_by_imsi(const char *imsi) -{ - struct gprs_subscr *gsub; - - if (!imsi || !*imsi) - return NULL; - - llist_for_each_entry(gsub, gprs_subscribers, entry) { - if (!strcmp(gsub->imsi, imsi)) - return gprs_subscr_get(gsub); - } - return NULL; -} - -static struct gprs_subscr *gprs_subscr_alloc(void) -{ - struct gprs_subscr *gsub; - gsub = talloc_zero(tall_bsc_ctx, struct gprs_subscr); - if (!gsub) - return NULL; - llist_add_tail(&gsub->entry, gprs_subscribers); - gsub->use_count = 1; - gsub->tmsi = GSM_RESERVED_TMSI; - return gsub; -} - -struct gprs_subscr *gprs_subscr_get_or_create(const char *imsi) -{ - struct gprs_subscr *gsub; - - gsub = gprs_subscr_get_by_imsi(imsi); - if (!gsub) { - gsub = gprs_subscr_alloc(); - if (!gsub) - return NULL; - osmo_strlcpy(gsub->imsi, imsi, sizeof(gsub->imsi)); - } - - if (!gsub->sgsn_data) - gsub->sgsn_data = sgsn_subscriber_data_alloc(gsub); - return gsub; -} - -void gprs_subscr_cleanup(struct gprs_subscr *subscr) -{ - if (subscr->sgsn_data->mm) { - gprs_subscr_put(subscr->sgsn_data->mm->subscr); - subscr->sgsn_data->mm->subscr = NULL; - subscr->sgsn_data->mm = NULL; - } - - if (subscr->flags & GPRS_SUBSCRIBER_ENABLE_PURGE) { - gprs_subscr_purge(subscr); - subscr->flags &= ~GPRS_SUBSCRIBER_ENABLE_PURGE; - } -} - -void gprs_subscr_cancel(struct gprs_subscr *subscr) -{ - subscr->authorized = 0; - subscr->flags |= GPRS_SUBSCRIBER_CANCELLED; - subscr->flags &= ~GPRS_SUBSCRIBER_ENABLE_PURGE; - - gprs_subscr_update(subscr); - gprs_subscr_cleanup(subscr); -} - -static int gprs_subscr_tx_gsup_message(struct gprs_subscr *subscr, - struct osmo_gsup_message *gsup_msg) -{ - struct msgb *msg = gsup_client_msgb_alloc(); - - if (strlen(gsup_msg->imsi) == 0 && subscr) - osmo_strlcpy(gsup_msg->imsi, subscr->imsi, - sizeof(gsup_msg->imsi)); - gsup_msg->cn_domain = OSMO_GSUP_CN_DOMAIN_PS; - osmo_gsup_encode(msg, gsup_msg); - - LOGGSUBSCRP(LOGL_INFO, subscr, - "Sending GSUP, will send: %s\n", msgb_hexdump(msg)); - - if (!sgsn->gsup_client) { - msgb_free(msg); - return -ENOTSUP; - } - - return gsup_client_send(sgsn->gsup_client, msg); -} - -static int gprs_subscr_tx_gsup_error_reply(struct gprs_subscr *subscr, - struct osmo_gsup_message *gsup_orig, - enum gsm48_gmm_cause cause) -{ - struct osmo_gsup_message gsup_reply = {0}; - - osmo_strlcpy(gsup_reply.imsi, gsup_orig->imsi, - sizeof(gsup_reply.imsi)); - gsup_reply.cause = cause; - gsup_reply.message_type = - OSMO_GSUP_TO_MSGT_ERROR(gsup_orig->message_type); - - return gprs_subscr_tx_gsup_message(subscr, &gsup_reply); -} - -static int gprs_subscr_handle_gsup_auth_res(struct gprs_subscr *subscr, - struct osmo_gsup_message *gsup_msg) -{ - unsigned idx; - struct sgsn_subscriber_data *sdata = subscr->sgsn_data; - - LOGGSUBSCRP(LOGL_INFO, subscr, - "Got SendAuthenticationInfoResult, num_auth_vectors = %zu\n", - gsup_msg->num_auth_vectors); - - if (gsup_msg->num_auth_vectors > 0) { - memset(sdata->auth_triplets, 0, sizeof(sdata->auth_triplets)); - - for (idx = 0; idx < ARRAY_SIZE(sdata->auth_triplets); idx++) - sdata->auth_triplets[idx].key_seq = GSM_KEY_SEQ_INVAL; - } - - for (idx = 0; idx < gsup_msg->num_auth_vectors; idx++) { - size_t key_seq = idx; - LOGGSUBSCRP(LOGL_DEBUG, subscr, - "Adding auth tuple, cksn = %zu\n", key_seq); - if (key_seq >= ARRAY_SIZE(sdata->auth_triplets)) { - LOGGSUBSCRP(LOGL_NOTICE, subscr, - "Skipping auth triplet with invalid cksn %zu\n", - key_seq); - continue; - } - sdata->auth_triplets[key_seq].vec = gsup_msg->auth_vectors[idx]; - sdata->auth_triplets[key_seq].key_seq = key_seq; - } - - sdata->auth_triplets_updated = 1; - sdata->error_cause = SGSN_ERROR_CAUSE_NONE; - - gprs_subscr_update_auth_info(subscr); - - return 0; -} - -static int gprs_subscr_pdp_data_clear(struct gprs_subscr *subscr) -{ - struct sgsn_subscriber_pdp_data *pdp, *pdp2; - int count = 0; - - llist_for_each_entry_safe(pdp, pdp2, &subscr->sgsn_data->pdp_list, list) { - llist_del(&pdp->list); - talloc_free(pdp); - count += 1; - } - - return count; -} - -static struct sgsn_subscriber_pdp_data *gprs_subscr_pdp_data_get_by_id( - struct gprs_subscr *subscr, unsigned context_id) -{ - struct sgsn_subscriber_pdp_data *pdp; - - llist_for_each_entry(pdp, &subscr->sgsn_data->pdp_list, list) { - if (pdp->context_id == context_id) - return pdp; - } - - return NULL; -} - - -static void gprs_subscr_gsup_insert_data(struct gprs_subscr *subscr, - struct osmo_gsup_message *gsup_msg) -{ - struct sgsn_subscriber_data *sdata = subscr->sgsn_data; - unsigned idx; - int rc; - - if (gsup_msg->msisdn_enc) { - if (gsup_msg->msisdn_enc_len > sizeof(sdata->msisdn)) { - LOGP(DGPRS, LOGL_ERROR, "MSISDN too long (%zu)\n", - gsup_msg->msisdn_enc_len); - sdata->msisdn_len = 0; - } else { - memcpy(sdata->msisdn, gsup_msg->msisdn_enc, - gsup_msg->msisdn_enc_len); - sdata->msisdn_len = gsup_msg->msisdn_enc_len; - } - } - - if (gsup_msg->hlr_enc) { - if (gsup_msg->hlr_enc_len > sizeof(sdata->hlr)) { - LOGP(DGPRS, LOGL_ERROR, "HLR-Number too long (%zu)\n", - gsup_msg->hlr_enc_len); - sdata->hlr_len = 0; - } else { - memcpy(sdata->hlr, gsup_msg->hlr_enc, - gsup_msg->hlr_enc_len); - sdata->hlr_len = gsup_msg->hlr_enc_len; - } - } - - if (gsup_msg->pdp_info_compl) { - rc = gprs_subscr_pdp_data_clear(subscr); - if (rc > 0) - LOGP(DGPRS, LOGL_INFO, "Cleared existing PDP info\n"); - } - - for (idx = 0; idx < gsup_msg->num_pdp_infos; idx++) { - struct osmo_gsup_pdp_info *pdp_info = &gsup_msg->pdp_infos[idx]; - size_t ctx_id = pdp_info->context_id; - struct sgsn_subscriber_pdp_data *pdp_data; - - if (pdp_info->apn_enc_len >= sizeof(pdp_data->apn_str)-1) { - LOGGSUBSCRP(LOGL_ERROR, subscr, - "APN too long, context id = %zu, APN = %s\n", - ctx_id, osmo_hexdump(pdp_info->apn_enc, - pdp_info->apn_enc_len)); - continue; - } - - if (pdp_info->qos_enc_len > sizeof(pdp_data->qos_subscribed)) { - LOGGSUBSCRP(LOGL_ERROR, subscr, - "QoS info too long (%zu)\n", - pdp_info->qos_enc_len); - continue; - } - - LOGGSUBSCRP(LOGL_INFO, subscr, - "Will set PDP info, context id = %zu, APN = %s\n", - ctx_id, osmo_hexdump(pdp_info->apn_enc, pdp_info->apn_enc_len)); - - /* Set PDP info [ctx_id] */ - pdp_data = gprs_subscr_pdp_data_get_by_id(subscr, ctx_id); - if (!pdp_data) { - pdp_data = sgsn_subscriber_pdp_data_alloc(subscr->sgsn_data); - pdp_data->context_id = ctx_id; - } - - OSMO_ASSERT(pdp_data != NULL); - pdp_data->pdp_type = pdp_info->pdp_type; - gprs_apn_to_str(pdp_data->apn_str, - pdp_info->apn_enc, pdp_info->apn_enc_len); - memcpy(pdp_data->qos_subscribed, pdp_info->qos_enc, pdp_info->qos_enc_len); - pdp_data->qos_subscribed_len = pdp_info->qos_enc_len; - } -} - -static int gprs_subscr_handle_gsup_upd_loc_res(struct gprs_subscr *subscr, - struct osmo_gsup_message *gsup_msg) -{ - /* contrary to MAP, we allow piggy-backing subscriber data onto - * the UPDATE LOCATION RESULT, and don't mandate the use of a - * separate nested INSERT SUBSCRIBER DATA transaction */ - gprs_subscr_gsup_insert_data(subscr, gsup_msg); - - subscr->authorized = 1; - subscr->sgsn_data->error_cause = SGSN_ERROR_CAUSE_NONE; - - subscr->flags |= GPRS_SUBSCRIBER_ENABLE_PURGE; - - gprs_subscr_update(subscr); - return 0; -} - -static int gprs_subscr_handle_gsup_dsd_req(struct gprs_subscr *subscr, - struct osmo_gsup_message *gsup_msg) -{ - struct osmo_gsup_message gsup_reply = {0}; - - if (gsup_msg->cn_domain != OSMO_GSUP_CN_DOMAIN_PS) { - LOGGSUBSCRP(LOGL_ERROR, subscr, - "Rx GSUP message %s not supported for CS\n", - osmo_gsup_message_type_name(gsup_msg->message_type)); - gsup_reply.cause = GMM_CAUSE_MSGT_NOTEXIST_NOTIMPL; - gsup_reply.message_type = OSMO_GSUP_MSGT_DELETE_DATA_ERROR; - } else { - gsm0408_gprs_access_cancelled(subscr->sgsn_data->mm, - GMM_CAUSE_GPRS_NOTALLOWED); - gsup_reply.message_type = OSMO_GSUP_MSGT_DELETE_DATA_RESULT; - } - - return gprs_subscr_tx_gsup_message(subscr, &gsup_reply); -} - -static int gprs_subscr_handle_gsup_isd_req(struct gprs_subscr *subscr, - struct osmo_gsup_message *gsup_msg) -{ - struct osmo_gsup_message gsup_reply = {0}; - - gprs_subscr_gsup_insert_data(subscr, gsup_msg); - - subscr->authorized = 1; - subscr->sgsn_data->error_cause = SGSN_ERROR_CAUSE_NONE; - subscr->flags |= GPRS_SUBSCRIBER_ENABLE_PURGE; - gprs_subscr_update(subscr); - - gsup_reply.message_type = OSMO_GSUP_MSGT_INSERT_DATA_RESULT; - return gprs_subscr_tx_gsup_message(subscr, &gsup_reply); -} - -static int check_cause(int cause) -{ - switch (cause) { - case GMM_CAUSE_IMSI_UNKNOWN ... GMM_CAUSE_ILLEGAL_ME: - case GMM_CAUSE_GPRS_NOTALLOWED ... GMM_CAUSE_NO_GPRS_PLMN: - return EACCES; - - case GMM_CAUSE_MSC_TEMP_NOTREACH ... GMM_CAUSE_CONGESTION: - return EHOSTUNREACH; - - case GMM_CAUSE_SEM_INCORR_MSG ... GMM_CAUSE_PROTO_ERR_UNSPEC: - default: - return EINVAL; - } -} - -static int gprs_subscr_handle_gsup_auth_err(struct gprs_subscr *subscr, - struct osmo_gsup_message *gsup_msg) -{ - unsigned idx; - struct sgsn_subscriber_data *sdata = subscr->sgsn_data; - int cause_err; - - cause_err = check_cause(gsup_msg->cause); - - LOGGSUBSCRP(LOGL_DEBUG, subscr, - "Send authentication info has failed with cause %d, " - "handled as: %s\n", - gsup_msg->cause, strerror(cause_err)); - - switch (cause_err) { - case EACCES: - LOGGSUBSCRP(LOGL_NOTICE, subscr, - "GPRS send auth info req failed, access denied, " - "GMM cause = '%s' (%d)\n", - get_value_string(gsm48_gmm_cause_names, gsup_msg->cause), - gsup_msg->cause); - /* Clear auth tuples */ - memset(sdata->auth_triplets, 0, sizeof(sdata->auth_triplets)); - for (idx = 0; idx < ARRAY_SIZE(sdata->auth_triplets); idx++) - sdata->auth_triplets[idx].key_seq = GSM_KEY_SEQ_INVAL; - - subscr->authorized = 0; - sdata->error_cause = gsup_msg->cause; - gprs_subscr_update_auth_info(subscr); - break; - - case EHOSTUNREACH: - LOGGSUBSCRP(LOGL_NOTICE, subscr, - "GPRS send auth info req failed, GMM cause = '%s' (%d)\n", - get_value_string(gsm48_gmm_cause_names, gsup_msg->cause), - gsup_msg->cause); - - sdata->error_cause = gsup_msg->cause; - gprs_subscr_update_auth_info(subscr); - break; - - default: - case EINVAL: - LOGGSUBSCRP(LOGL_ERROR, subscr, - "GSUP protocol remote error, GMM cause = '%s' (%d)\n", - get_value_string(gsm48_gmm_cause_names, gsup_msg->cause), - gsup_msg->cause); - break; - } - - return -gsup_msg->cause; -} - -static int gprs_subscr_handle_gsup_upd_loc_err(struct gprs_subscr *subscr, - struct osmo_gsup_message *gsup_msg) -{ - int cause_err; - - cause_err = check_cause(gsup_msg->cause); - - LOGGSUBSCRP(LOGL_DEBUG, subscr, - "Update location has failed with cause %d, handled as: %s\n", - gsup_msg->cause, strerror(cause_err)); - - switch (cause_err) { - case EACCES: - LOGGSUBSCRP(LOGL_NOTICE, subscr, - "GPRS update location failed, access denied, " - "GMM cause = '%s' (%d)\n", - get_value_string(gsm48_gmm_cause_names, gsup_msg->cause), - gsup_msg->cause); - - subscr->authorized = 0; - subscr->sgsn_data->error_cause = gsup_msg->cause; - gprs_subscr_update_auth_info(subscr); - break; - - case EHOSTUNREACH: - LOGGSUBSCRP(LOGL_NOTICE, subscr, - "GPRS update location failed, GMM cause = '%s' (%d)\n", - get_value_string(gsm48_gmm_cause_names, gsup_msg->cause), - gsup_msg->cause); - - subscr->sgsn_data->error_cause = gsup_msg->cause; - gprs_subscr_update_auth_info(subscr); - break; - - default: - case EINVAL: - LOGGSUBSCRP(LOGL_ERROR, subscr, - "GSUP protocol remote error, GMM cause = '%s' (%d)\n", - get_value_string(gsm48_gmm_cause_names, gsup_msg->cause), - gsup_msg->cause); - break; - } - - return -gsup_msg->cause; -} - -static int gprs_subscr_handle_gsup_purge_no_subscr( - struct osmo_gsup_message *gsup_msg) -{ - if (OSMO_GSUP_IS_MSGT_ERROR(gsup_msg->message_type)) { - LOGGSUPP(LOGL_NOTICE, gsup_msg, - "Purge MS has failed with cause '%s' (%d)\n", - get_value_string(gsm48_gmm_cause_names, gsup_msg->cause), - gsup_msg->cause); - return -gsup_msg->cause; - } - - LOGGSUPP(LOGL_INFO, gsup_msg, "Completing purge MS\n"); - return 0; -} - -static int gprs_subscr_handle_gsup_purge_res(struct gprs_subscr *subscr, - struct osmo_gsup_message *gsup_msg) -{ - LOGGSUBSCRP(LOGL_INFO, subscr, "Completing purge MS\n"); - - /* Force silent cancellation */ - subscr->sgsn_data->error_cause = SGSN_ERROR_CAUSE_NONE; - gprs_subscr_cancel(subscr); - - return 0; -} - -static int gprs_subscr_handle_gsup_purge_err(struct gprs_subscr *subscr, - struct osmo_gsup_message *gsup_msg) -{ - LOGGSUBSCRP(LOGL_NOTICE, subscr, - "Purge MS has failed with cause '%s' (%d)\n", - get_value_string(gsm48_gmm_cause_names, gsup_msg->cause), - gsup_msg->cause); - - /* In GSM 09.02, 19.1.4.4, the text and the SDL diagram imply that - * the subscriber data is not removed if the request has failed. On the - * other hand, keeping the subscriber data in either error case - * (subscriber unknown, syntactical message error, connection error) - * doesn't seem to give any advantage, since the data will be restored - * on the next Attach Request anyway. - * This approach ensures, that the subscriber record will not stick if - * an error happens. - */ - - /* TODO: Check whether this behaviour is acceptable and either just - * remove this TODO-notice or change the implementation to not delete - * the subscriber data (eventually resetting the ENABLE_PURGE flag and - * restarting the expiry timer based on the cause). - * - * Subscriber Unknown: cancel subscr - * Temporary network problems: do nothing (handled by timer based retry) - * Message problems (syntax, nyi, ...): cancel subscr (retry won't help) - */ - - gprs_subscr_handle_gsup_purge_res(subscr, gsup_msg); - - return -gsup_msg->cause; -} - -static int gprs_subscr_handle_loc_cancel_req(struct gprs_subscr *subscr, - struct osmo_gsup_message *gsup_msg) -{ - struct osmo_gsup_message gsup_reply = {0}; - int is_update_procedure = !gsup_msg->cancel_type || - gsup_msg->cancel_type == OSMO_GSUP_CANCEL_TYPE_UPDATE; - - LOGGSUBSCRP(LOGL_INFO, subscr, "Cancelling MS subscriber (%s)\n", - is_update_procedure ? - "update procedure" : "subscription withdraw"); - - gsup_reply.message_type = OSMO_GSUP_MSGT_LOCATION_CANCEL_RESULT; - gprs_subscr_tx_gsup_message(subscr, &gsup_reply); - - if (is_update_procedure) - subscr->sgsn_data->error_cause = SGSN_ERROR_CAUSE_NONE; - else - /* Since a withdraw cause is not specified, just abort the - * current attachment. The following re-attachment should then - * be rejected with a proper cause value. - */ - subscr->sgsn_data->error_cause = GMM_CAUSE_IMPL_DETACHED; - - gprs_subscr_cancel(subscr); - - return 0; -} - -static int gprs_subscr_handle_unknown_imsi(struct osmo_gsup_message *gsup_msg) -{ - if (OSMO_GSUP_IS_MSGT_REQUEST(gsup_msg->message_type)) { - gprs_subscr_tx_gsup_error_reply(NULL, gsup_msg, - GMM_CAUSE_IMSI_UNKNOWN); - LOGP(DGPRS, LOGL_NOTICE, - "Unknown IMSI %s, discarding GSUP request " - "of type 0x%02x\n", - gsup_msg->imsi, gsup_msg->message_type); - } else if (OSMO_GSUP_IS_MSGT_ERROR(gsup_msg->message_type)) { - LOGP(DGPRS, LOGL_NOTICE, - "Unknown IMSI %s, discarding GSUP error " - "of type 0x%02x, cause '%s' (%d)\n", - gsup_msg->imsi, gsup_msg->message_type, - get_value_string(gsm48_gmm_cause_names, gsup_msg->cause), - gsup_msg->cause); - } else { - LOGP(DGPRS, LOGL_NOTICE, - "Unknown IMSI %s, discarding GSUP response " - "of type 0x%02x\n", - gsup_msg->imsi, gsup_msg->message_type); - } - - return -GMM_CAUSE_IMSI_UNKNOWN; -} - -int gprs_subscr_rx_gsup_message(struct msgb *msg) -{ - uint8_t *data = msgb_l2(msg); - size_t data_len = msgb_l2len(msg); - int rc = 0; - - struct osmo_gsup_message gsup_msg = {0}; - struct gprs_subscr *subscr; - - rc = osmo_gsup_decode(data, data_len, &gsup_msg); - if (rc < 0) { - LOGP(DGPRS, LOGL_ERROR, - "decoding GSUP message fails with error '%s' (%d)\n", - get_value_string(gsm48_gmm_cause_names, -rc), -rc); - return rc; - } - - if (!gsup_msg.imsi[0]) { - LOGP(DGPRS, LOGL_ERROR, "Missing IMSI in GSUP message\n"); - - if (OSMO_GSUP_IS_MSGT_REQUEST(gsup_msg.message_type)) - gprs_subscr_tx_gsup_error_reply(NULL, &gsup_msg, - GMM_CAUSE_INV_MAND_INFO); - return -GMM_CAUSE_INV_MAND_INFO; - } - - if (!gsup_msg.cause && OSMO_GSUP_IS_MSGT_ERROR(gsup_msg.message_type)) - gsup_msg.cause = GMM_CAUSE_NET_FAIL; - - subscr = gprs_subscr_get_by_imsi(gsup_msg.imsi); - - if (!subscr) { - switch (gsup_msg.message_type) { - case OSMO_GSUP_MSGT_PURGE_MS_RESULT: - case OSMO_GSUP_MSGT_PURGE_MS_ERROR: - return gprs_subscr_handle_gsup_purge_no_subscr(&gsup_msg); - default: - return gprs_subscr_handle_unknown_imsi(&gsup_msg); - } - } - - LOGGSUBSCRP(LOGL_INFO, subscr, - "Received GSUP message %s\n", - osmo_gsup_message_type_name(gsup_msg.message_type)); - - switch (gsup_msg.message_type) { - case OSMO_GSUP_MSGT_LOCATION_CANCEL_REQUEST: - rc = gprs_subscr_handle_loc_cancel_req(subscr, &gsup_msg); - break; - - case OSMO_GSUP_MSGT_SEND_AUTH_INFO_RESULT: - rc = gprs_subscr_handle_gsup_auth_res(subscr, &gsup_msg); - break; - - case OSMO_GSUP_MSGT_SEND_AUTH_INFO_ERROR: - rc = gprs_subscr_handle_gsup_auth_err(subscr, &gsup_msg); - break; - - case OSMO_GSUP_MSGT_UPDATE_LOCATION_RESULT: - rc = gprs_subscr_handle_gsup_upd_loc_res(subscr, &gsup_msg); - break; - - case OSMO_GSUP_MSGT_UPDATE_LOCATION_ERROR: - rc = gprs_subscr_handle_gsup_upd_loc_err(subscr, &gsup_msg); - break; - - case OSMO_GSUP_MSGT_PURGE_MS_ERROR: - rc = gprs_subscr_handle_gsup_purge_err(subscr, &gsup_msg); - break; - - case OSMO_GSUP_MSGT_PURGE_MS_RESULT: - rc = gprs_subscr_handle_gsup_purge_res(subscr, &gsup_msg); - break; - - case OSMO_GSUP_MSGT_INSERT_DATA_REQUEST: - rc = gprs_subscr_handle_gsup_isd_req(subscr, &gsup_msg); - break; - - case OSMO_GSUP_MSGT_DELETE_DATA_REQUEST: - rc = gprs_subscr_handle_gsup_dsd_req(subscr, &gsup_msg); - break; - - default: - LOGGSUBSCRP(LOGL_ERROR, subscr, - "Rx GSUP message %s not valid at SGSN\n", - osmo_gsup_message_type_name(gsup_msg.message_type)); - if (OSMO_GSUP_IS_MSGT_REQUEST(gsup_msg.message_type)) - gprs_subscr_tx_gsup_error_reply( - subscr, &gsup_msg, GMM_CAUSE_MSGT_NOTEXIST_NOTIMPL); - rc = -GMM_CAUSE_MSGT_NOTEXIST_NOTIMPL; - break; - }; - - gprs_subscr_put(subscr); - - return rc; -} - -int gprs_subscr_purge(struct gprs_subscr *subscr) -{ - struct sgsn_subscriber_data *sdata = subscr->sgsn_data; - struct osmo_gsup_message gsup_msg = {0}; - - LOGGSUBSCRP(LOGL_INFO, subscr, "purging MS subscriber\n"); - - gsup_msg.message_type = OSMO_GSUP_MSGT_PURGE_MS_REQUEST; - - /* Provide the HLR number in case it is known */ - gsup_msg.hlr_enc_len = sdata->hlr_len; - gsup_msg.hlr_enc = sdata->hlr; - - return gprs_subscr_tx_gsup_message(subscr, &gsup_msg); -} - -static int gprs_subscr_query_auth_info(struct gprs_subscr *subscr, - const uint8_t *auts, - const uint8_t *auts_rand) -{ - struct osmo_gsup_message gsup_msg = {0}; - - /* Make sure we have a complete resync or clearly no resync. */ - OSMO_ASSERT((auts != NULL) == (auts_rand != NULL)); - - LOGGSUBSCRP(LOGL_INFO, subscr, "requesting auth info%s\n", - auts ? " with AUTS (UMTS Resynch)" : ""); - - gsup_msg.message_type = OSMO_GSUP_MSGT_SEND_AUTH_INFO_REQUEST; - gsup_msg.auts = auts; - gsup_msg.rand = auts_rand; - return gprs_subscr_tx_gsup_message(subscr, &gsup_msg); -} - -int gprs_subscr_location_update(struct gprs_subscr *subscr) -{ - struct osmo_gsup_message gsup_msg = {0}; - - LOGGSUBSCRP(LOGL_INFO, subscr, - "subscriber data is not available\n"); - - gsup_msg.message_type = OSMO_GSUP_MSGT_UPDATE_LOCATION_REQUEST; - return gprs_subscr_tx_gsup_message(subscr, &gsup_msg); -} - -void gprs_subscr_update(struct gprs_subscr *subscr) -{ - LOGGSUBSCRP(LOGL_DEBUG, subscr, "Updating subscriber data\n"); - - subscr->flags &= ~GPRS_SUBSCRIBER_UPDATE_LOCATION_PENDING; - subscr->flags &= ~GPRS_SUBSCRIBER_FIRST_CONTACT; - - if (subscr->sgsn_data->mm) - sgsn_update_subscriber_data(subscr->sgsn_data->mm); -} - -void gprs_subscr_update_auth_info(struct gprs_subscr *subscr) -{ - LOGGSUBSCRP(LOGL_DEBUG, subscr, - "Updating subscriber authentication info\n"); - - subscr->flags &= ~GPRS_SUBSCRIBER_UPDATE_AUTH_INFO_PENDING; - subscr->flags &= ~GPRS_SUBSCRIBER_FIRST_CONTACT; - - if (subscr->sgsn_data->mm) - sgsn_update_subscriber_data(subscr->sgsn_data->mm); -} - -struct gprs_subscr *gprs_subscr_get_or_create_by_mmctx(struct sgsn_mm_ctx *mmctx) -{ - struct gprs_subscr *subscr = NULL; - - if (mmctx->subscr) - return gprs_subscr_get(mmctx->subscr); - - if (mmctx->imsi[0]) - subscr = gprs_subscr_get_by_imsi(mmctx->imsi); - - if (!subscr) { - subscr = gprs_subscr_get_or_create(mmctx->imsi); - subscr->flags |= GPRS_SUBSCRIBER_FIRST_CONTACT; - subscr->flags &= ~GPRS_SUBSCRIBER_ENABLE_PURGE; - } - - osmo_strlcpy(subscr->imei, mmctx->imei, sizeof(subscr->imei)); - - if (subscr->lac != mmctx->ra.lac) - subscr->lac = mmctx->ra.lac; - - subscr->sgsn_data->mm = mmctx; - mmctx->subscr = gprs_subscr_get(subscr); - - return subscr; -} - -int gprs_subscr_request_update_location(struct sgsn_mm_ctx *mmctx) -{ - struct gprs_subscr *subscr = NULL; - int rc; - - LOGMMCTXP(LOGL_DEBUG, mmctx, "Requesting subscriber data update\n"); - - subscr = gprs_subscr_get_or_create_by_mmctx(mmctx); - - subscr->flags |= GPRS_SUBSCRIBER_UPDATE_LOCATION_PENDING; - - rc = gprs_subscr_location_update(subscr); - gprs_subscr_put(subscr); - return rc; -} - -/*! \brief Send Update Auth Info request via GSUP, with or without resync. - * \param[in] mmctx MM context to request authentication tuples for. - * \param[in] auts 14 octet AUTS token for UMTS resync, or NULL. - * \param[in] auts_rand 16 octet Random token for UMTS resync, or NULL. - * In case of normal Authentication Info request, both \a auts and \a auts_rand - * must be NULL. For resync, both must be non-NULL. - */ -int gprs_subscr_request_auth_info(struct sgsn_mm_ctx *mmctx, - const uint8_t *auts, - const uint8_t *auts_rand) -{ - struct gprs_subscr *subscr = NULL; - int rc; - - LOGMMCTXP(LOGL_DEBUG, mmctx, "Requesting subscriber authentication info\n"); - - subscr = gprs_subscr_get_or_create_by_mmctx(mmctx); - - subscr->flags |= GPRS_SUBSCRIBER_UPDATE_AUTH_INFO_PENDING; - - rc = gprs_subscr_query_auth_info(subscr, auts, auts_rand); - gprs_subscr_put(subscr); - return rc; -} - -static void gprs_subscr_free(struct gprs_subscr *gsub) -{ - llist_del(&gsub->entry); - talloc_free(gsub); -} - -struct gprs_subscr *_gprs_subscr_get(struct gprs_subscr *gsub, - const char *file, int line) -{ - OSMO_ASSERT(gsub->use_count < INT_MAX); - gsub->use_count++; - LOGPSRC(DREF, LOGL_DEBUG, file, line, - "subscr %s usage increases to: %d\n", - gsub->imsi, gsub->use_count); - return gsub; -} - -struct gprs_subscr *_gprs_subscr_put(struct gprs_subscr *gsub, - const char *file, int line) -{ - gsub->use_count--; - LOGPSRC(DREF, gsub->use_count >= 0? LOGL_DEBUG : LOGL_ERROR, - file, line, - "subscr %s usage decreases to: %d%s\n", - gsub->imsi, gsub->use_count, - gsub->keep_in_ram? ", keep-in-ram flag is set" : ""); - if (gsub->use_count > 0) - return gsub; - if (gsub->keep_in_ram) - return gsub; - gprs_subscr_free(gsub); - return NULL; -} diff --git a/openbsc/src/gprs/gprs_utils.c b/openbsc/src/gprs/gprs_utils.c deleted file mode 100644 index 64ed9788..00000000 --- a/openbsc/src/gprs/gprs_utils.c +++ /dev/null @@ -1,274 +0,0 @@ -/* GPRS utility functions */ - -/* (C) 2010 by Harald Welte - * (C) 2010-2014 by On-Waves - * (C) 2013 by Holger Hans Peter Freyther - * All Rights Reserved - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation; either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - * - */ -#include - -#include -#include - -#include -#include -#include - -#include - -/* FIXME: this needs to go to libosmocore/msgb.c */ -struct msgb *gprs_msgb_copy(const struct msgb *msg, const char *name) -{ - struct libgb_msgb_cb *old_cb, *new_cb; - struct msgb *new_msg; - - new_msg = msgb_alloc(msg->data_len, name); - if (!new_msg) - return NULL; - - /* copy data */ - memcpy(new_msg->_data, msg->_data, new_msg->data_len); - - /* copy header */ - new_msg->len = msg->len; - new_msg->data += msg->data - msg->_data; - new_msg->head += msg->head - msg->_data; - new_msg->tail += msg->tail - msg->_data; - - if (msg->l1h) - new_msg->l1h = new_msg->_data + (msg->l1h - msg->_data); - if (msg->l2h) - new_msg->l2h = new_msg->_data + (msg->l2h - msg->_data); - if (msg->l3h) - new_msg->l3h = new_msg->_data + (msg->l3h - msg->_data); - if (msg->l4h) - new_msg->l4h = new_msg->_data + (msg->l4h - msg->_data); - - /* copy GB specific data */ - old_cb = LIBGB_MSGB_CB(msg); - new_cb = LIBGB_MSGB_CB(new_msg); - - if (old_cb->bssgph) - new_cb->bssgph = new_msg->_data + (old_cb->bssgph - msg->_data); - if (old_cb->llch) - new_cb->llch = new_msg->_data + (old_cb->llch - msg->_data); - - /* bssgp_cell_id is a pointer into the old msgb, so we need to make - * it a pointer into the new msgb */ - if (old_cb->bssgp_cell_id) - new_cb->bssgp_cell_id = new_msg->_data + - (old_cb->bssgp_cell_id - msg->_data); - new_cb->nsei = old_cb->nsei; - new_cb->bvci = old_cb->bvci; - new_cb->tlli = old_cb->tlli; - - return new_msg; -} - -/* TODO: Move this to libosmocore/msgb.c */ -int gprs_msgb_resize_area(struct msgb *msg, uint8_t *area, - size_t old_size, size_t new_size) -{ - int rc; - uint8_t *rest = area + old_size; - int rest_len = msg->len - old_size - (area - msg->data); - int delta_size = (int)new_size - (int)old_size; - - if (delta_size == 0) - return 0; - - if (delta_size > 0) { - rc = msgb_trim(msg, msg->len + delta_size); - if (rc < 0) - return rc; - } - - memmove(area + new_size, area + old_size, rest_len); - - if (msg->l1h >= rest) - msg->l1h += delta_size; - if (msg->l2h >= rest) - msg->l2h += delta_size; - if (msg->l3h >= rest) - msg->l3h += delta_size; - if (msg->l4h >= rest) - msg->l4h += delta_size; - - if (delta_size < 0) - msgb_trim(msg, msg->len + delta_size); - - return 0; -} - -/* TODO: Move these conversion functions to a utils file. */ -/* TODO: consolidate with gprs_apn2str(). */ -/** memmove apn_enc to out_str, replacing the length octets in apn_enc with '.' - * (omitting the first one) and terminating with a '\0'. - * out_str needs to have rest_chars amount of bytes or 1 whatever is bigger. - */ -char * gprs_apn_to_str(char *out_str, const uint8_t *apn_enc, size_t rest_chars) -{ - char *str = out_str; - - while (rest_chars > 0 && apn_enc[0]) { - size_t label_size = apn_enc[0]; - if (label_size + 1 > rest_chars) - return NULL; - - memmove(str, apn_enc + 1, label_size); - str += label_size; - rest_chars -= label_size + 1; - apn_enc += label_size + 1; - - if (rest_chars) - *(str++) = '.'; - } - str[0] = '\0'; - - return out_str; -} - -int gprs_str_to_apn(uint8_t *apn_enc, size_t max_len, const char *str) -{ - uint8_t *last_len_field; - int len; - - /* Can we even write the length field to the output? */ - if (max_len == 0) - return -1; - - /* Remember where we need to put the length once we know it */ - last_len_field = apn_enc; - len = 1; - apn_enc += 1; - - while (str[0]) { - if (len >= max_len) - return -1; - - if (str[0] == '.') { - *last_len_field = (apn_enc - last_len_field) - 1; - last_len_field = apn_enc; - } else { - *apn_enc = str[0]; - } - apn_enc += 1; - str += 1; - len += 1; - } - - *last_len_field = (apn_enc - last_len_field) - 1; - - return len; -} - -/* GSM 04.08, 10.5.7.3 GPRS Timer */ -int gprs_tmr_to_secs(uint8_t tmr) -{ - switch (tmr & GPRS_TMR_UNIT_MASK) { - case GPRS_TMR_2SECONDS: - return 2 * (tmr & GPRS_TMR_FACT_MASK); - default: - case GPRS_TMR_MINUTE: - return 60 * (tmr & GPRS_TMR_FACT_MASK); - case GPRS_TMR_6MINUTE: - return 360 * (tmr & GPRS_TMR_FACT_MASK); - case GPRS_TMR_DEACTIVATED: - return -1; - } -} - -/* This functions returns a tmr value such that - * - f is monotonic - * - f(s) <= s - * - f(s) == s if a tmr exists with s = gprs_tmr_to_secs(tmr) - * - the best possible resolution is used - * where - * f(s) = gprs_tmr_to_secs(gprs_secs_to_tmr_floor(s)) - */ -uint8_t gprs_secs_to_tmr_floor(int secs) -{ - if (secs < 0) - return GPRS_TMR_DEACTIVATED; - if (secs < 2 * 32) - return GPRS_TMR_2SECONDS | (secs / 2); - if (secs < 60 * 2) - /* Ensure monotonicity */ - return GPRS_TMR_2SECONDS | GPRS_TMR_FACT_MASK; - if (secs < 60 * 32) - return GPRS_TMR_MINUTE | (secs / 60); - if (secs < 360 * 6) - /* Ensure monotonicity */ - return GPRS_TMR_MINUTE | GPRS_TMR_FACT_MASK; - if (secs < 360 * 32) - return GPRS_TMR_6MINUTE | (secs / 360); - - return GPRS_TMR_6MINUTE | GPRS_TMR_FACT_MASK; -} - -/* GSM 04.08, 10.5.1.4 */ -int gprs_is_mi_tmsi(const uint8_t *value, size_t value_len) -{ - if (value_len != GSM48_TMSI_LEN) - return 0; - - if (!value || (value[0] & GSM_MI_TYPE_MASK) != GSM_MI_TYPE_TMSI) - return 0; - - return 1; -} - -/* GSM 04.08, 10.5.1.4 */ -int gprs_is_mi_imsi(const uint8_t *value, size_t value_len) -{ - if (value_len == 0) - return 0; - - if (!value || (value[0] & GSM_MI_TYPE_MASK) != GSM_MI_TYPE_IMSI) - return 0; - - return 1; -} - -int gprs_parse_mi_tmsi(const uint8_t *value, size_t value_len, uint32_t *tmsi) -{ - uint32_t tmsi_be; - - if (!gprs_is_mi_tmsi(value, value_len)) - return 0; - - memcpy(&tmsi_be, value + 1, sizeof(tmsi_be)); - - *tmsi = ntohl(tmsi_be); - return 1; -} - -void gprs_parse_tmsi(const uint8_t *value, uint32_t *tmsi) -{ - uint32_t tmsi_be; - - memcpy(&tmsi_be, value, sizeof(tmsi_be)); - - *tmsi = ntohl(tmsi_be); -} - -int gprs_ra_id_equals(const struct gprs_ra_id *id1, - const struct gprs_ra_id *id2) -{ - return (id1->mcc == id2->mcc && id1->mnc == id2->mnc && - id1->lac == id2->lac && id1->rac == id2->rac); -} diff --git a/openbsc/src/gprs/gtphub.c b/openbsc/src/gprs/gtphub.c deleted file mode 100644 index 211018b5..00000000 --- a/openbsc/src/gprs/gtphub.c +++ /dev/null @@ -1,2931 +0,0 @@ -/* GTP Hub Implementation */ - -/* (C) 2015 by sysmocom s.f.m.c. GmbH - * All Rights Reserved - * - * Author: Neels Hofmeyr - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation; either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include - -#include -#include -#include - -#include -#include -#include -#include -#include - - -static const int GTPH_GC_TICK_SECONDS = 1; - -void *osmo_gtphub_ctx; - -/* Convenience makro, note: only within this C file. */ -#define LOG(level, fmt, args...) \ - LOGP(DGTPHUB, level, fmt, ##args) - -#define ZERO_STRUCT(struct_pointer) memset(struct_pointer, '\0', \ - sizeof(*(struct_pointer))) - -/* TODO move this to osmocom/core/select.h ? */ -typedef int (*osmo_fd_cb_t)(struct osmo_fd *fd, unsigned int what); - -/* TODO move this to osmocom/core/linuxlist.h ? */ -#define __llist_first(head) (((head)->next == (head)) ? NULL : (head)->next) -#define llist_first(head, type, entry) \ - llist_entry(__llist_first(head), type, entry) - -#define __llist_last(head) (((head)->next == (head)) ? NULL : (head)->prev) -#define llist_last(head, type, entry) \ - llist_entry(__llist_last(head), type, entry) - -/* TODO move GTP header stuff to openggsn/gtp/ ? See gtp_decaps*() */ - -enum gtp_rc { - GTP_RC_UNKNOWN = 0, - GTP_RC_TINY = 1, /* no IEs (like ping/pong) */ - GTP_RC_PDU_C = 2, /* a real packet with IEs */ - GTP_RC_PDU_U = 3, /* a real packet with User data */ - - GTP_RC_TOOSHORT = -1, - GTP_RC_UNSUPPORTED_VERSION = -2, - GTP_RC_INVALID_IE = -3, -}; - -struct gtp_packet_desc { - union gtp_packet *data; - int data_len; - int header_len; - int version; - uint8_t type; - uint16_t seq; - uint32_t header_tei_rx; - uint32_t header_tei; - int rc; /* enum gtp_rc */ - unsigned int plane_idx; - unsigned int side_idx; - struct gtphub_tunnel *tun; - time_t timestamp; - union gtpie_member *ie[GTPIE_SIZE]; -}; - -struct pending_delete { - struct llist_head entry; - struct expiring_item expiry_entry; - - struct gtphub_tunnel *tun; - uint8_t teardown_ind; - uint8_t nsapi; -}; - - -/* counters */ - -enum gtphub_counters_io { - GTPH_CTR_PKTS_IN = 0, - GTPH_CTR_PKTS_OUT, - GTPH_CTR_BYTES_IN, - GTPH_CTR_BYTES_OUT -}; - -static const struct rate_ctr_desc gtphub_counters_io_desc[] = { - { "packets.in", "Packets ( In)" }, - { "packets.out", "Packets (Out)" }, - { "bytes.in", "Bytes ( In)" }, - { "bytes.out", "Bytes (Out)" }, -}; - -static const struct rate_ctr_group_desc gtphub_ctrg_io_desc = { - .group_name_prefix = "gtphub.bind", - .group_description = "I/O Statistics", - .num_ctr = ARRAY_SIZE(gtphub_counters_io_desc), - .ctr_desc = gtphub_counters_io_desc, - .class_id = OSMO_STATS_CLASS_GLOBAL, -}; - - -/* support */ - -static const char *gtp_type_str(uint8_t type) -{ - switch (type) { - case 1: - return " (Echo Request)"; - case 2: - return " (Echo Response)"; - case 16: - return " (Create PDP Ctx Request)"; - case 17: - return " (Create PDP Ctx Response)"; - case 18: - return " (Update PDP Ctx Request)"; - case 19: - return " (Update PDP Ctx Response)"; - case 20: - return " (Delete PDP Ctx Request)"; - case 21: - return " (Delete PDP Ctx Response)"; - case 255: - return " (User Data)"; - default: - return ""; - } -} - -void gsn_addr_copy(struct gsn_addr *gsna, const struct gsn_addr *src) -{ - *gsna = *src; -} - -int gsn_addr_from_sockaddr(struct gsn_addr *gsna, uint16_t *port, - const struct osmo_sockaddr *sa) -{ - char addr_str[256]; - char port_str[6]; - - if (osmo_sockaddr_to_strs(addr_str, sizeof(addr_str), - port_str, sizeof(port_str), - sa, (NI_NUMERICHOST | NI_NUMERICSERV)) - != 0) { - return -1; - } - - if (port) - *port = atoi(port_str); - - return gsn_addr_from_str(gsna, addr_str); -} - -int gsn_addr_from_str(struct gsn_addr *gsna, const char *numeric_addr_str) -{ - if ((!gsna) || (!numeric_addr_str)) - return -1; - - int af = AF_INET; - gsna->len = 4; - const char *pos = numeric_addr_str; - for (; *pos; pos++) { - if (*pos == ':') { - af = AF_INET6; - gsna->len = 16; - break; - } - } - - int rc = inet_pton(af, numeric_addr_str, gsna->buf); - if (rc != 1) { - LOG(LOGL_ERROR, "Cannot resolve numeric address: '%s'\n", - numeric_addr_str); - return -1; - } - return 0; -} - -const char *gsn_addr_to_str(const struct gsn_addr *gsna) -{ - static char buf[INET6_ADDRSTRLEN + 1]; - return gsn_addr_to_strb(gsna, buf, sizeof(buf)); -} - -const char *gsn_addr_to_strb(const struct gsn_addr *gsna, - char *strbuf, - int strbuf_len) -{ - int af; - switch (gsna->len) { - case 4: - af = AF_INET; - break; - case 16: - af = AF_INET6; - break; - default: - return NULL; - } - - const char *r = inet_ntop(af, gsna->buf, strbuf, strbuf_len); - if (!r) { - LOG(LOGL_ERROR, "Cannot convert gsn_addr to string:" - " %s: len=%d, buf=%s\n", - strerror(errno), - (int)gsna->len, - osmo_hexdump(gsna->buf, sizeof(gsna->buf))); - } - return r; -} - -int gsn_addr_same(const struct gsn_addr *a, const struct gsn_addr *b) -{ - if (a == b) - return 1; - if ((!a) || (!b)) - return 0; - if (a->len != b->len) - return 0; - return (memcmp(a->buf, b->buf, a->len) == 0)? 1 : 0; -} - -static int gsn_addr_get(struct gsn_addr *gsna, const struct gtp_packet_desc *p, - int idx) -{ - if (p->rc != GTP_RC_PDU_C) - return -1; - - unsigned int len; - /* gtpie.h fails to declare gtpie_gettlv()'s first arg as const. */ - if (gtpie_gettlv((union gtpie_member**)p->ie, GTPIE_GSN_ADDR, idx, - &len, gsna->buf, sizeof(gsna->buf)) - != 0) - return -1; - gsna->len = len; - return 0; -} - -static int gsn_addr_put(const struct gsn_addr *gsna, struct gtp_packet_desc *p, - int idx) -{ - if (p->rc != GTP_RC_PDU_C) - return -1; - - int ie_idx; - ie_idx = gtpie_getie(p->ie, GTPIE_GSN_ADDR, idx); - - if (ie_idx < 0) - return -1; - - struct gtpie_tlv *ie = &p->ie[ie_idx]->tlv; - int ie_l = ntoh16(ie->l); - if (ie_l != gsna->len) { - LOG(LOGL_ERROR, "Not implemented:" - " replace an IE address of different size:" - " replace %d with %d\n", (int)ie_l, (int)gsna->len); - return -1; - } - - memcpy(ie->v, gsna->buf, (int)ie_l); - return 0; -} - -/* Validate GTP version 0 data; analogous to validate_gtp1_header(), see there. - */ -void validate_gtp0_header(struct gtp_packet_desc *p) -{ - const struct gtp0_header *pheader = &(p->data->gtp0.h); - p->rc = GTP_RC_UNKNOWN; - p->header_len = 0; - - OSMO_ASSERT(p->data_len >= 1); - OSMO_ASSERT(p->version == 0); - - if (p->data_len < GTP0_HEADER_SIZE) { - LOG(LOGL_ERROR, "GTP0 packet too short: %d\n", p->data_len); - p->rc = GTP_RC_TOOSHORT; - return; - } - - p->type = ntoh8(pheader->type); - p->seq = ntoh16(pheader->seq); - p->header_tei_rx = 0; /* TODO */ - p->header_tei = p->header_tei_rx; - - if (p->data_len == GTP0_HEADER_SIZE) { - p->rc = GTP_RC_TINY; - p->header_len = GTP0_HEADER_SIZE; - return; - } - - /* Check packet length field versus length of packet */ - if (p->data_len != (ntoh16(pheader->length) + GTP0_HEADER_SIZE)) { - LOG(LOGL_ERROR, "GTP packet length field (%d + %d) does not" - " match actual length (%d)\n", - GTP0_HEADER_SIZE, (int)ntoh16(pheader->length), - p->data_len); - p->rc = GTP_RC_TOOSHORT; - return; - } - - LOG(LOGL_DEBUG, "GTP v0 TID = %" PRIu64 "\n", pheader->tid); - p->header_len = GTP0_HEADER_SIZE; - p->rc = GTP_RC_PDU_C; -} - -/* Validate GTP version 1 data, and update p->rc with the result, as well as - * p->header_len in case of a valid header. */ -void validate_gtp1_header(struct gtp_packet_desc *p) -{ - const struct gtp1_header_long *pheader = &(p->data->gtp1l.h); - p->rc = GTP_RC_UNKNOWN; - p->header_len = 0; - - OSMO_ASSERT(p->data_len >= 1); - OSMO_ASSERT(p->version == 1); - - if ((p->data_len < GTP1_HEADER_SIZE_LONG) - && (p->data_len != GTP1_HEADER_SIZE_SHORT)){ - LOG(LOGL_ERROR, "GTP packet too short: %d\n", p->data_len); - p->rc = GTP_RC_TOOSHORT; - return; - } - - p->type = ntoh8(pheader->type); - p->header_tei_rx = ntoh32(pheader->tei); - p->header_tei = p->header_tei_rx; - p->seq = ntoh16(pheader->seq); - - LOG(LOGL_DEBUG, "| GTPv1\n"); - LOG(LOGL_DEBUG, "| type = %" PRIu8 " 0x%02" PRIx8 "\n", p->type, p->type); - LOG(LOGL_DEBUG, "| length = %" PRIu16 " 0x%04" PRIx16 "\n", ntoh16(pheader->length), ntoh16(pheader->length)); - LOG(LOGL_DEBUG, "| TEI = %" PRIu32 " 0x%08" PRIx32 "\n", p->header_tei_rx, p->header_tei_rx); - LOG(LOGL_DEBUG, "| seq = %" PRIu16 " 0x%04" PRIx16 "\n", p->seq, p->seq); - LOG(LOGL_DEBUG, "| npdu = %" PRIu8 " 0x%02" PRIx8 "\n", pheader->npdu, pheader->npdu); - LOG(LOGL_DEBUG, "| next = %" PRIu8 " 0x%02" PRIx8 "\n", pheader->next, pheader->next); - - if (p->data_len <= GTP1_HEADER_SIZE_LONG) { - p->rc = GTP_RC_TINY; - p->header_len = GTP1_HEADER_SIZE_SHORT; - return; - } - - /* Check packet length field versus length of packet */ - int announced_len = ntoh16(pheader->length) + GTP1_HEADER_SIZE_SHORT; - if (p->data_len != announced_len) { - LOG(LOGL_ERROR, "GTP packet length field (%d + %d) does not" - " match actual length (%d)\n", - GTP1_HEADER_SIZE_SHORT, (int)ntoh16(pheader->length), - p->data_len); - p->rc = GTP_RC_TOOSHORT; - return; - } - - p->rc = GTP_RC_PDU_C; - p->header_len = GTP1_HEADER_SIZE_LONG; -} - -/* Examine whether p->data of size p->data_len has a valid GTP header. Set - * p->version, p->rc and p->header_len. On error, p->rc <= 0 (see enum - * gtp_rc). p->data must point at a buffer with p->data_len set. */ -void validate_gtp_header(struct gtp_packet_desc *p) -{ - p->rc = GTP_RC_UNKNOWN; - - /* Need at least 1 byte in order to check version */ - if (p->data_len < 1) { - LOG(LOGL_ERROR, "Discarding packet - too small: %d\n", - p->data_len); - p->rc = GTP_RC_TOOSHORT; - return; - } - - p->version = p->data->flags >> 5; - - switch (p->version) { - case 0: - validate_gtp0_header(p); - break; - case 1: - validate_gtp1_header(p); - break; - default: - LOG(LOGL_ERROR, "Unsupported GTP version: %d\n", p->version); - p->rc = GTP_RC_UNSUPPORTED_VERSION; - break; - } -} - - -/* Return the value of the i'th IMSI IEI by copying to *imsi. - * The first IEI is reached by passing i = 0. - * imsi must point at allocated space of (at least) 8 bytes. - * Return 1 on success, or 0 if not found. */ -static int get_ie_imsi(union gtpie_member *ie[], int i, uint8_t *imsi) -{ - return gtpie_gettv0(ie, GTPIE_IMSI, i, imsi, 8) == 0; -} - -/* Analogous to get_ie_imsi(). nsapi must point at a single uint8_t. */ -static int get_ie_nsapi(union gtpie_member *ie[], int i, uint8_t *nsapi) -{ - return gtpie_gettv1(ie, GTPIE_NSAPI, i, nsapi) == 0; -} - -static char imsi_digit_to_char(uint8_t nibble) -{ - nibble &= 0x0f; - if (nibble > 9) - return (nibble == 0x0f) ? '\0' : '?'; - return '0' + nibble; -} - -/* Return a human readable IMSI string, in a static buffer. - * imsi must point at 8 octets of IMSI IE encoded IMSI data. */ -static int imsi_to_str(uint8_t *imsi, const char **imsi_str) -{ - static char str[17]; - int i; - - for (i = 0; i < 8; i++) { - char c; - c = imsi_digit_to_char(imsi[i]); - if (c == '?') - return -1; - str[2*i] = c; - - c = imsi_digit_to_char(imsi[i] >> 4); - if (c == '?') - return -1; - str[2*i + 1] = c; - } - str[16] = '\0'; - *imsi_str = str; - return 1; -} - -/* Return 0 if not present, 1 if present and decoded successfully, -1 if - * present but cannot be decoded. */ -static int get_ie_imsi_str(union gtpie_member *ie[], int i, - const char **imsi_str) -{ - uint8_t imsi_buf[8]; - if (!get_ie_imsi(ie, i, imsi_buf)) - return 0; - return imsi_to_str(imsi_buf, imsi_str); -} - -/* Return 0 if not present, 1 if present and decoded successfully, -1 if - * present but cannot be decoded. */ -static int get_ie_apn_str(union gtpie_member *ie[], const char **apn_str) -{ - static char apn_buf[GSM_APN_LENGTH]; - unsigned int len; - if (gtpie_gettlv(ie, GTPIE_APN, 0, - &len, apn_buf, sizeof(apn_buf)) != 0) - return 0; - - if (len < 2) { - LOG(LOGL_ERROR, "APN IE: invalid length: %d\n", - (int)len); - return -1; - } - - if (len > (sizeof(apn_buf) - 1)) - len = sizeof(apn_buf) - 1; - apn_buf[len] = '\0'; - - *apn_str = gprs_apn_to_str(apn_buf, (uint8_t*)apn_buf, len); - if (!(*apn_str)) { - LOG(LOGL_ERROR, "APN IE: present but cannot be decoded: %s\n", - osmo_hexdump((uint8_t*)apn_buf, len)); - return -1; - } - return 1; -} - - -/* Validate header, and index information elements. Write decoded packet - * information to *res. res->data will point at the given data buffer. On - * error, p->rc is set <= 0 (see enum gtp_rc). */ -static void gtp_decode(const uint8_t *data, int data_len, - unsigned int from_side_idx, - unsigned int from_plane_idx, - struct gtp_packet_desc *res, - time_t now) -{ - ZERO_STRUCT(res); - res->data = (union gtp_packet*)data; - res->data_len = data_len; - res->side_idx = from_side_idx; - res->plane_idx = from_plane_idx; - res->timestamp = now; - - validate_gtp_header(res); - - if (res->rc <= 0) - return; - - LOG(LOGL_DEBUG, "Valid GTP header (v%d)\n", res->version); - - if (from_plane_idx == GTPH_PLANE_USER) { - res->rc = GTP_RC_PDU_U; - return; - } - - if (res->rc != GTP_RC_PDU_C) { - LOG(LOGL_DEBUG, "no IEs in this GTP packet\n"); - return; - } - - if (gtpie_decaps(res->ie, res->version, - (void*)(data + res->header_len), - res->data_len - res->header_len) != 0) { - res->rc = GTP_RC_INVALID_IE; - LOG(LOGL_ERROR, "INVALID: cannot decode IEs." - " Dropping GTP packet%s.\n", - gtp_type_str(res->type) - ); - return; - } - -#if 1 - /* TODO if () - (waiting for a commit from jerlbeck) */ - int i; - - for (i = 0; i < 10; i++) { - const char *imsi; - if (get_ie_imsi_str(res->ie, i, &imsi) < 1) - break; - LOG(LOGL_DEBUG, "| IMSI %s\n", imsi); - } - - for (i = 0; i < 10; i++) { - uint8_t nsapi; - if (!get_ie_nsapi(res->ie, i, &nsapi)) - break; - LOG(LOGL_DEBUG, "| NSAPI %d\n", (int)nsapi); - } - - for (i = 0; i < 2; i++) { - struct gsn_addr addr; - if (gsn_addr_get(&addr, res, i) == 0) - LOG(LOGL_DEBUG, "| addr %s\n", gsn_addr_to_str(&addr)); - } - - for (i = 0; i < 10; i++) { - uint32_t tei; - if (gtpie_gettv4(res->ie, GTPIE_TEI_DI, i, &tei) != 0) - break; - LOG(LOGL_DEBUG, "| TEI DI (USER) %" PRIu32 " 0x%08" PRIx32 "\n", - tei, tei); - } - - for (i = 0; i < 10; i++) { - uint32_t tei; - if (gtpie_gettv4(res->ie, GTPIE_TEI_C, i, &tei) != 0) - break; - LOG(LOGL_DEBUG, "| TEI (CTRL) %" PRIu32 " 0x%08" PRIx32 "\n", - tei, tei); - } -#endif -} - - -/* expiry */ - -void expiry_init(struct expiry *exq, int expiry_in_seconds) -{ - ZERO_STRUCT(exq); - exq->expiry_in_seconds = expiry_in_seconds; - INIT_LLIST_HEAD(&exq->items); -} - -void expiry_add(struct expiry *exq, struct expiring_item *item, time_t now) -{ - item->expiry = now + exq->expiry_in_seconds; - - OSMO_ASSERT(llist_empty(&exq->items) - || (item->expiry - >= llist_last(&exq->items, struct expiring_item, entry)->expiry)); - - /* Add/move to the tail to always sort by expiry, ascending. */ - llist_del(&item->entry); - llist_add_tail(&item->entry, &exq->items); -} - -int expiry_tick(struct expiry *exq, time_t now) -{ - int expired = 0; - struct expiring_item *m, *n; - llist_for_each_entry_safe(m, n, &exq->items, entry) { - if (m->expiry <= now) { - expiring_item_del(m); - expired ++; - } else { - /* The items are added sorted by expiry. So when we hit - * an unexpired entry, only more unexpired ones will - * follow. */ - break; - } - } - return expired; -} - -void expiry_clear(struct expiry *exq) -{ - struct expiring_item *m, *n; - llist_for_each_entry_safe(m, n, &exq->items, entry) { - expiring_item_del(m); - } -} - -void expiring_item_init(struct expiring_item *item) -{ - ZERO_STRUCT(item); - INIT_LLIST_HEAD(&item->entry); -} - -void expiring_item_del(struct expiring_item *item) -{ - OSMO_ASSERT(item); - llist_del(&item->entry); - INIT_LLIST_HEAD(&item->entry); - if (item->del_cb) { - /* avoid loops */ - del_cb_t del_cb = item->del_cb; - item->del_cb = 0; - (del_cb)(item); - } -} - - -/* nr_map, nr_pool */ - -void nr_pool_init(struct nr_pool *pool, nr_t nr_min, nr_t nr_max) -{ - *pool = (struct nr_pool){ - .nr_min = nr_min, - .nr_max = nr_max, - .last_nr = nr_max - }; -} - -nr_t nr_pool_next(struct nr_pool *pool) -{ - if (pool->last_nr >= pool->nr_max) - pool->last_nr = pool->nr_min; - else - pool->last_nr ++; - - return pool->last_nr; -} - -void nr_map_init(struct nr_map *map, struct nr_pool *pool, - struct expiry *exq) -{ - ZERO_STRUCT(map); - map->pool = pool; - map->add_items_to_expiry = exq; - INIT_LLIST_HEAD(&map->mappings); -} - -void nr_mapping_init(struct nr_mapping *m) -{ - ZERO_STRUCT(m); - INIT_LLIST_HEAD(&m->entry); - expiring_item_init(&m->expiry_entry); -} - -void nr_map_add(struct nr_map *map, struct nr_mapping *mapping, time_t now) -{ - /* Generate a mapped number */ - mapping->repl = nr_pool_next(map->pool); - - /* Add to the tail to always yield a list sorted by expiry, in - * ascending order. */ - llist_add_tail(&mapping->entry, &map->mappings); - nr_map_refresh(map, mapping, now); -} - -void nr_map_refresh(struct nr_map *map, struct nr_mapping *mapping, time_t now) -{ - if (!map->add_items_to_expiry) - return; - expiry_add(map->add_items_to_expiry, - &mapping->expiry_entry, - now); -} - -void nr_map_clear(struct nr_map *map) -{ - struct nr_mapping *m; - struct nr_mapping *n; - llist_for_each_entry_safe(m, n, &map->mappings, entry) { - nr_mapping_del(m); - } -} - -int nr_map_empty(const struct nr_map *map) -{ - return llist_empty(&map->mappings); -} - -struct nr_mapping *nr_map_get(const struct nr_map *map, - void *origin, nr_t nr_orig) -{ - struct nr_mapping *mapping; - llist_for_each_entry(mapping, &map->mappings, entry) { - if ((mapping->origin == origin) - && (mapping->orig == nr_orig)) - return mapping; - } - /* Not found. */ - return NULL; -} - -struct nr_mapping *nr_map_get_inv(const struct nr_map *map, nr_t nr_repl) -{ - struct nr_mapping *mapping; - llist_for_each_entry(mapping, &map->mappings, entry) { - if (mapping->repl == nr_repl) { - return mapping; - } - } - /* Not found. */ - return NULL; -} - -void nr_mapping_del(struct nr_mapping *mapping) -{ - OSMO_ASSERT(mapping); - llist_del(&mapping->entry); - INIT_LLIST_HEAD(&mapping->entry); - expiring_item_del(&mapping->expiry_entry); -} - - -/* gtphub */ - -const char* const gtphub_plane_idx_names[GTPH_PLANE_N] = { - "CTRL", - "USER", -}; - -const uint16_t gtphub_plane_idx_default_port[GTPH_PLANE_N] = { - 2123, - 2152, -}; - -const char* const gtphub_side_idx_names[GTPH_SIDE_N] = { - "SGSN", - "GGSN", -}; - -time_t gtphub_now(void) -{ - struct timespec now_tp; - OSMO_ASSERT(clock_gettime(CLOCK_MONOTONIC, &now_tp) >= 0); - return now_tp.tv_sec; -} - -/* Remove a gtphub_peer from its list and free it. */ -static void gtphub_peer_del(struct gtphub_peer *peer) -{ - OSMO_ASSERT(llist_empty(&peer->addresses)); - nr_map_clear(&peer->seq_map); - llist_del(&peer->entry); - talloc_free(peer); -} - -static void gtphub_peer_addr_del(struct gtphub_peer_addr *pa) -{ - OSMO_ASSERT(llist_empty(&pa->ports)); - llist_del(&pa->entry); - talloc_free(pa); -} - -static void gtphub_peer_port_del(struct gtphub_peer_port *pp) -{ - OSMO_ASSERT(pp->ref_count == 0); - llist_del(&pp->entry); - rate_ctr_group_free(pp->counters_io); - talloc_free(pp); -} - -/* From the information in the gtp_packet_desc, return the address of a GGSN. - * Return -1 on error. */ -static int gtphub_resolve_ggsn(struct gtphub *hub, - struct gtp_packet_desc *p, - struct gtphub_peer_port **pp); - -/* See gtphub_ext.c (wrapped by unit test) */ -struct gtphub_peer_port *gtphub_resolve_ggsn_addr(struct gtphub *hub, - const char *imsi_str, - const char *apn_ni_str); -int gtphub_ares_init(struct gtphub *hub); - -static void gtphub_zero(struct gtphub *hub) -{ - ZERO_STRUCT(hub); - INIT_LLIST_HEAD(&hub->ggsn_lookups); - INIT_LLIST_HEAD(&hub->resolved_ggsns); -} - -static int gtphub_sock_init(struct osmo_fd *ofd, - const struct gtphub_cfg_addr *addr, - osmo_fd_cb_t cb, - void *data, - int ofd_id) -{ - if (!addr->addr_str) { - LOG(LOGL_FATAL, "Cannot bind: empty address.\n"); - return -1; - } - if (!addr->port) { - LOG(LOGL_FATAL, "Cannot bind: zero port not permitted.\n"); - return -1; - } - - ofd->when = BSC_FD_READ; - ofd->cb = cb; - ofd->data = data; - ofd->priv_nr = ofd_id; - - int rc; - rc = osmo_sock_init_ofd(ofd, - AF_UNSPEC, SOCK_DGRAM, IPPROTO_UDP, - addr->addr_str, addr->port, - OSMO_SOCK_F_BIND); - if (rc < 1) { - LOG(LOGL_FATAL, "Cannot bind to %s port %d (rc %d)\n", - addr->addr_str, (int)addr->port, rc); - return -1; - } - - return 0; -} - -static void gtphub_sock_close(struct osmo_fd *ofd) -{ - close(ofd->fd); - osmo_fd_unregister(ofd); - ofd->cb = NULL; -} - -static void gtphub_bind_init(struct gtphub_bind *b) -{ - ZERO_STRUCT(b); - - INIT_LLIST_HEAD(&b->peers); - - b->counters_io = rate_ctr_group_alloc(osmo_gtphub_ctx, - >phub_ctrg_io_desc, 0); - OSMO_ASSERT(b->counters_io); -} - -static int gtphub_bind_start(struct gtphub_bind *b, - const struct gtphub_cfg_bind *cfg, - osmo_fd_cb_t cb, void *cb_data, - unsigned int ofd_id) -{ - LOG(LOGL_DEBUG, "Starting bind %s\n", b->label); - if (gsn_addr_from_str(&b->local_addr, cfg->bind.addr_str) != 0) { - LOG(LOGL_FATAL, "Invalid bind address for %s: %s\n", - b->label, cfg->bind.addr_str); - return -1; - } - if (gtphub_sock_init(&b->ofd, &cfg->bind, cb, cb_data, ofd_id) != 0) { - LOG(LOGL_FATAL, "Cannot bind for %s: %s\n", - b->label, cfg->bind.addr_str); - return -1; - } - b->local_port = cfg->bind.port; - return 0; -} - -static void gtphub_bind_free(struct gtphub_bind *b) -{ - OSMO_ASSERT(llist_empty(&b->peers)); - rate_ctr_group_free(b->counters_io); -} - -static void gtphub_bind_stop(struct gtphub_bind *b) { - gtphub_sock_close(&b->ofd); - gtphub_bind_free(b); -} - -/* Recv datagram from from->fd, write sender's address to *from_addr. - * Return the number of bytes read, zero on error. */ -static int gtphub_read(const struct osmo_fd *from, - struct osmo_sockaddr *from_addr, - uint8_t *buf, size_t buf_len) -{ - OSMO_ASSERT(from_addr); - - /* recvfrom requires the available length set in *from_addr_len. */ - from_addr->l = sizeof(from_addr->a); - errno = 0; - ssize_t received = recvfrom(from->fd, buf, buf_len, 0, - (struct sockaddr*)&from_addr->a, - &from_addr->l); - /* TODO use recvmsg and get a MSG_TRUNC flag to make sure the message - * is not truncated. Then maybe reduce buf's size. */ - - if (received <= 0) { - LOG((errno == EAGAIN? LOGL_DEBUG : LOGL_ERROR), - "error: %s\n", strerror(errno)); - return 0; - } - - LOG(LOGL_DEBUG, "Received %d bytes from %s: %s%s\n", - (int)received, osmo_sockaddr_to_str(from_addr), - osmo_hexdump(buf, received > 1000? 1000 : received), - received > 1000 ? "..." : ""); - - return received; -} - -static inline void gtphub_port_ref_count_inc(struct gtphub_peer_port *pp) -{ - OSMO_ASSERT(pp); - OSMO_ASSERT(pp->ref_count < UINT_MAX); - pp->ref_count++; -} - -static inline void gtphub_port_ref_count_dec(struct gtphub_peer_port *pp) -{ - OSMO_ASSERT(pp); - OSMO_ASSERT(pp->ref_count > 0); - pp->ref_count--; -} - -static inline void set_seq(struct gtp_packet_desc *p, uint16_t seq) -{ - OSMO_ASSERT(p->version == 1); - p->data->gtp1l.h.seq = hton16(seq); - p->seq = seq; -} - -static inline void set_tei(struct gtp_packet_desc *p, uint32_t tei) -{ - OSMO_ASSERT(p->version == 1); - p->data->gtp1l.h.tei = hton32(tei); - p->header_tei = tei; -} - -static void gtphub_mapping_del_cb(struct expiring_item *expi); - -static struct nr_mapping *gtphub_mapping_new() -{ - struct nr_mapping *nrm; - nrm = talloc_zero(osmo_gtphub_ctx, struct nr_mapping); - OSMO_ASSERT(nrm); - - nr_mapping_init(nrm); - nrm->expiry_entry.del_cb = gtphub_mapping_del_cb; - return nrm; -} - - -#define APPEND(args...) \ - l = snprintf(pos, left, args); \ - pos += l; \ - left -= l - -static const char *gtphub_tunnel_side_str(struct gtphub_tunnel *tun, - int side_idx) -{ - static char buf[256]; - char *pos = buf; - int left = sizeof(buf); - int l; - - struct gtphub_tunnel_endpoint *c, *u; - c = &tun->endpoint[side_idx][GTPH_PLANE_CTRL]; - u = &tun->endpoint[side_idx][GTPH_PLANE_USER]; - - /* print both only if they differ. */ - if (!c->peer) { - APPEND("(uninitialized)"); - } else { - APPEND("%s", gsn_addr_to_str(&c->peer->peer_addr->addr)); - } - - if (!u->peer) { - if (c->peer) { - APPEND("/(uninitialized)"); - } - } else if ((!c->peer) - || (!gsn_addr_same(&u->peer->peer_addr->addr, - &c->peer->peer_addr->addr))) { - APPEND("/%s", gsn_addr_to_str(&u->peer->peer_addr->addr)); - } - - APPEND(" (TEI C=%x U=%x)", - c->tei_orig, - u->tei_orig); - return buf; -} - -const char *gtphub_tunnel_str(struct gtphub_tunnel *tun) -{ - static char buf[512]; - char *pos = buf; - int left = sizeof(buf); - int l; - - if (!tun) - return "null-tunnel"; - - APPEND("TEI=%x: ", tun->tei_repl); - APPEND("%s", gtphub_tunnel_side_str(tun, GTPH_SIDE_SGSN)); - APPEND(" <-> %s", gtphub_tunnel_side_str(tun, GTPH_SIDE_GGSN)); - - return buf; -} - -#undef APPEND - -void gtphub_tunnel_endpoint_set_peer(struct gtphub_tunnel_endpoint *te, - struct gtphub_peer_port *pp) -{ - if (te->peer) - gtphub_port_ref_count_dec(te->peer); - te->peer = pp; - if (te->peer) - gtphub_port_ref_count_inc(te->peer); -} - -int gtphub_tunnel_complete(struct gtphub_tunnel *tun) -{ - if (!tun) - return 0; - if (!tun->tei_repl) - return 0; - int side_idx; - int plane_idx; - for_each_side_and_plane(side_idx, plane_idx) { - struct gtphub_tunnel_endpoint *te = - &tun->endpoint[side_idx][plane_idx]; - if (!(te->peer && te->tei_orig)) - return 0; - } - return 1; -} - -static void gtphub_tunnel_del_cb(struct expiring_item *expi) -{ - struct gtphub_tunnel *tun = container_of(expi, - struct gtphub_tunnel, - expiry_entry); - LOG(LOGL_DEBUG, "expired: %s\n", gtphub_tunnel_str(tun)); - - llist_del(&tun->entry); - INIT_LLIST_HEAD(&tun->entry); /* mark unused */ - - expi->del_cb = 0; /* avoid recursion loops */ - expiring_item_del(&tun->expiry_entry); /* usually already done, but make sure. */ - - int side_idx; - int plane_idx; - for_each_side_and_plane(side_idx, plane_idx) { - struct gtphub_tunnel_endpoint *te = &tun->endpoint[side_idx][plane_idx]; - - /* clear ref count */ - gtphub_tunnel_endpoint_set_peer(te, NULL); - - rate_ctr_group_free(te->counters_io); - } - - talloc_free(tun); -} - -static struct gtphub_tunnel *gtphub_tunnel_new() -{ - struct gtphub_tunnel *tun; - tun = talloc_zero(osmo_gtphub_ctx, struct gtphub_tunnel); - OSMO_ASSERT(tun); - - INIT_LLIST_HEAD(&tun->entry); - expiring_item_init(&tun->expiry_entry); - - int side_idx, plane_idx; - for_each_side_and_plane(side_idx, plane_idx) { - struct gtphub_tunnel_endpoint *te = &tun->endpoint[side_idx][plane_idx]; - te->counters_io = rate_ctr_group_alloc(osmo_gtphub_ctx, - >phub_ctrg_io_desc, - 0); - OSMO_ASSERT(te->counters_io); - } - - tun->expiry_entry.del_cb = gtphub_tunnel_del_cb; - return tun; -} - -static const char *gtphub_peer_strb(struct gtphub_peer *peer, char *buf, - int buflen) -{ - if (llist_empty(&peer->addresses)) - return "(addressless)"; - - struct gtphub_peer_addr *a = llist_first(&peer->addresses, - struct gtphub_peer_addr, - entry); - return gsn_addr_to_strb(&a->addr, buf, buflen); -} - -static const char *gtphub_port_strb(struct gtphub_peer_port *port, char *buf, - int buflen) -{ - if (!port) - return "(null port)"; - - snprintf(buf, buflen, "%s port %d", - gsn_addr_to_str(&port->peer_addr->addr), - (int)port->port); - return buf; -} - -const char *gtphub_peer_str(struct gtphub_peer *peer) -{ - static char buf[256]; - return gtphub_peer_strb(peer, buf, sizeof(buf)); -} - -const char *gtphub_port_str(struct gtphub_peer_port *port) -{ - static char buf[256]; - return gtphub_port_strb(port, buf, sizeof(buf)); -} - -static const char *gtphub_port_str2(struct gtphub_peer_port *port) -{ - static char buf[256]; - return gtphub_port_strb(port, buf, sizeof(buf)); -} - -static void gtphub_mapping_del_cb(struct expiring_item *expi) -{ - expi->del_cb = 0; /* avoid recursion loops */ - expiring_item_del(expi); /* usually already done, but make sure. */ - - struct nr_mapping *nrm = container_of(expi, - struct nr_mapping, - expiry_entry); - llist_del(&nrm->entry); - INIT_LLIST_HEAD(&nrm->entry); /* mark unused */ - - /* Just for log */ - struct gtphub_peer_port *from = nrm->origin; - OSMO_ASSERT(from); - LOG(LOGL_DEBUG, "expired: %d: nr mapping from %s: %u->%u\n", - (int)nrm->expiry_entry.expiry, - gtphub_port_str(from), - (unsigned int)nrm->orig, (unsigned int)nrm->repl); - - gtphub_port_ref_count_dec(from); - - talloc_free(nrm); -} - -static struct nr_mapping *gtphub_mapping_have(struct nr_map *map, - struct gtphub_peer_port *from, - nr_t orig_nr, - time_t now) -{ - struct nr_mapping *nrm; - - nrm = nr_map_get(map, from, orig_nr); - - if (!nrm) { - nrm = gtphub_mapping_new(); - nrm->orig = orig_nr; - nrm->origin = from; - nr_map_add(map, nrm, now); - gtphub_port_ref_count_inc(from); - LOG(LOGL_DEBUG, "peer %s: sequence map %d --> %d\n", - gtphub_port_str(from), - (int)(nrm->orig), (int)(nrm->repl)); - } else { - nr_map_refresh(map, nrm, now); - } - - OSMO_ASSERT(nrm); - return nrm; -} - -static void gtphub_map_seq(struct gtp_packet_desc *p, - struct gtphub_peer_port *from_port, - struct gtphub_peer_port *to_port) -{ - /* Store a mapping in to_peer's map, so when we later receive a GTP - * packet back from to_peer, the seq nr can be unmapped back to its - * origin (from_peer here). */ - struct nr_mapping *nrm; - nrm = gtphub_mapping_have(&to_port->peer_addr->peer->seq_map, - from_port, p->seq, p->timestamp); - - /* Change the GTP packet to yield the new, mapped seq nr */ - set_seq(p, nrm->repl); -} - -static struct gtphub_peer_port *gtphub_unmap_seq(struct gtp_packet_desc *p, - struct gtphub_peer_port *responding_port) -{ - OSMO_ASSERT(p->version == 1); - struct nr_mapping *nrm = - nr_map_get_inv(&responding_port->peer_addr->peer->seq_map, - p->seq); - if (!nrm) - return NULL; - LOG(LOGL_DEBUG, "peer %p: sequence unmap %d <-- %d\n", - nrm->origin, (int)(nrm->orig), (int)(nrm->repl)); - set_seq(p, nrm->orig); - return nrm->origin; -} - -static int gtphub_check_mapped_tei(struct gtphub_tunnel *new_tun, - struct gtphub_tunnel *iterated_tun, - uint32_t *tei_min, - uint32_t *tei_max) -{ - if (!new_tun->tei_repl || !iterated_tun->tei_repl) - return 1; - - *tei_min = (*tei_min < iterated_tun->tei_repl)? *tei_min : iterated_tun->tei_repl; - *tei_max = (*tei_max > iterated_tun->tei_repl)? *tei_max : iterated_tun->tei_repl; - - if (new_tun->tei_repl != iterated_tun->tei_repl) - return 1; - - /* new_tun->tei_repl is already taken. Try to find one out of the known - * range. */ - LOG(LOGL_DEBUG, "TEI replacement %d already taken.\n", new_tun->tei_repl); - - if ((*tei_max) < 0xffffffff) { - (*tei_max)++; - new_tun->tei_repl = *tei_max; - LOG(LOGL_DEBUG, "Using TEI %d instead.\n", new_tun->tei_repl); - return 1; - } else if ((*tei_min) > 1) { - (*tei_min)--; - new_tun->tei_repl = *tei_min; - LOG(LOGL_DEBUG, "Using TEI %d instead.\n", new_tun->tei_repl); - return 1; - } - - /* None seems to be available. */ - return 0; -} - -static int gtphub_check_reused_teis(struct gtphub *hub, - struct gtphub_tunnel *new_tun) -{ - uint32_t tei_min = 0xffffffff; - uint32_t tei_max = 0; - int side_idx; - int plane_idx; - struct gtphub_tunnel_endpoint *te; - struct gtphub_tunnel_endpoint *te2; - - struct gtphub_tunnel *tun, *ntun; - - llist_for_each_entry_safe(tun, ntun, &hub->tunnels, entry) { - if (tun == new_tun) - continue; - - /* Check whether the GSN sent a TEI that it is reusing from a - * previous tunnel. */ - int tun_continue = 0; - for_each_side(side_idx) { - for_each_plane(plane_idx) { - te = &tun->endpoint[side_idx][plane_idx]; - te2 = &new_tun->endpoint[side_idx][plane_idx]; - if ((te->tei_orig == 0) - || (te->tei_orig != te2->tei_orig) - || (!te->peer) - || (!te2->peer) - || !gsn_addr_same(&te->peer->peer_addr->addr, - &te2->peer->peer_addr->addr)) - continue; - - /* The peer is reusing a TEI that I believe to - * be part of another tunnel. The other tunnel - * must be stale, then. */ - LOG(LOGL_NOTICE, - "Expiring tunnel due to reused TEI:" - " %s peer %s sent %s TEI %x," - " previously used by tunnel %s...\n", - gtphub_side_idx_names[side_idx], - gtphub_port_str(te->peer), - gtphub_plane_idx_names[plane_idx], - te->tei_orig, - gtphub_tunnel_str(tun)); - LOG(LOGL_NOTICE, "...while establishing tunnel %s\n", - gtphub_tunnel_str(new_tun)); - - expiring_item_del(&tun->expiry_entry); - /* continue to find more matches. There shouldn't be - * any, but let's make sure. However, tun is deleted, - * so we need to skip to the next tunnel. */ - tun_continue = 1; - break; - } - if (tun_continue) - break; - } - if (tun_continue) - continue; - - /* Check whether the mapped TEI is already used by another - * tunnel. */ - if (!gtphub_check_mapped_tei(new_tun, tun, &tei_min, &tei_max)) { - LOG(LOGL_ERROR, - "No mapped TEI is readily available." - " Searching for holes between occupied" - " TEIs not implemented."); - return 0; - } - - } - - return 1; -} - -static void gtphub_tunnel_refresh(struct gtphub *hub, - struct gtphub_tunnel *tun, - time_t now) -{ - expiry_add(&hub->expire_slowly, - &tun->expiry_entry, - now); -} - -static struct gtphub_tunnel_endpoint *gtphub_unmap_tei(struct gtphub *hub, - struct gtp_packet_desc *p, - struct gtphub_peer_port *from, - struct gtphub_tunnel **unmapped_from_tun) -{ - OSMO_ASSERT(from); - int other_side = other_side_idx(p->side_idx); - - struct gtphub_tunnel *tun; - llist_for_each_entry(tun, &hub->tunnels, entry) { - struct gtphub_tunnel_endpoint *te_from = - &tun->endpoint[p->side_idx][p->plane_idx]; - struct gtphub_tunnel_endpoint *te_to = - &tun->endpoint[other_side][p->plane_idx]; - if ((tun->tei_repl == p->header_tei_rx) - && te_from->peer - && gsn_addr_same(&te_from->peer->peer_addr->addr, - &from->peer_addr->addr)) { - gtphub_tunnel_refresh(hub, tun, p->timestamp); - if (unmapped_from_tun) - *unmapped_from_tun = tun; - return te_to; - } - } - - if (unmapped_from_tun) - *unmapped_from_tun = NULL; - return NULL; -} - -static void gtphub_map_restart_counter(struct gtphub *hub, - struct gtp_packet_desc *p) -{ - if (p->rc != GTP_RC_PDU_C) - return; - - int ie_idx; - ie_idx = gtpie_getie(p->ie, GTPIE_RECOVERY, 0); - if (ie_idx < 0) - return; - - /* Always send gtphub's own restart counter */ - p->ie[ie_idx]->tv1.v = hton8(hub->restart_counter); -} - -static int gtphub_unmap_header_tei(struct gtphub_peer_port **to_port_p, - struct gtphub_tunnel **unmapped_from_tun, - struct gtphub *hub, - struct gtp_packet_desc *p, - struct gtphub_peer_port *from_port) -{ - OSMO_ASSERT(p->version == 1); - *to_port_p = NULL; - if (unmapped_from_tun) - *unmapped_from_tun = NULL; - - /* If the header's TEI is zero, no PDP context has been established - * yet. If nonzero, a mapping should actually already exist for this - * TEI, since it must have been announced in a PDP context creation. */ - if (!p->header_tei_rx) - return 0; - - /* to_peer has previously announced a TEI, which was stored and - * mapped in a tunnel struct. */ - struct gtphub_tunnel_endpoint *to; - to = gtphub_unmap_tei(hub, p, from_port, unmapped_from_tun); - if (!to) { - LOG(LOGL_ERROR, "Received unknown TEI %" PRIx32 " from %s\n", - p->header_tei_rx, gtphub_port_str(from_port)); - return -1; - } - - if (unmapped_from_tun) { - OSMO_ASSERT(*unmapped_from_tun); - LOG(LOGL_DEBUG, "Unmapped TEI coming from: %s\n", - gtphub_tunnel_str(*unmapped_from_tun)); - } - - uint32_t unmapped_tei = to->tei_orig; - set_tei(p, unmapped_tei); - - /* May be NULL for an invalidated tunnel. */ - *to_port_p = to->peer; - - return 0; -} - -static int gtphub_handle_create_pdp_ctx(struct gtphub *hub, - struct gtp_packet_desc *p, - struct gtphub_peer_port *from_ctrl, - struct gtphub_peer_port *to_ctrl) -{ - int plane_idx; - - osmo_static_assert((GTPH_PLANE_CTRL == 0) && (GTPH_PLANE_USER == 1), - plane_nrs_match_GSN_addr_IE_indices); - - struct gtphub_tunnel *tun = p->tun; - - if (p->type == GTP_CREATE_PDP_REQ) { - if (p->side_idx != GTPH_SIDE_SGSN) { - LOG(LOGL_ERROR, "Wrong side: Create PDP Context" - " Request from the GGSN side: %s", - gtphub_port_str(from_ctrl)); - return -1; - } - - if (tun) { - LOG(LOGL_ERROR, "Not implemented: Received" - " Create PDP Context Request for an already" - " established tunnel:" - " from %s, tunnel %s\n", - gtphub_port_str(from_ctrl), - gtphub_tunnel_str(p->tun)); - return -1; - } - - /* A new tunnel. */ - p->tun = tun = gtphub_tunnel_new(); - - /* Create TEI mapping */ - tun->tei_repl = nr_pool_next(&hub->tei_pool); - - llist_add(&tun->entry, &hub->tunnels); - gtphub_tunnel_refresh(hub, tun, p->timestamp); - /* The endpoint peers on this side (SGSN) will be set from IEs - * below. Also set the GGSN Ctrl endpoint, for logging. */ - gtphub_tunnel_endpoint_set_peer(&tun->endpoint[GTPH_SIDE_GGSN][GTPH_PLANE_CTRL], - to_ctrl); - } else if (p->type == GTP_CREATE_PDP_RSP) { - if (p->side_idx != GTPH_SIDE_GGSN) { - LOG(LOGL_ERROR, "Wrong side: Create PDP Context" - " Response from the SGSN side: %s", - gtphub_port_str(from_ctrl)); - return -1; - } - - /* The tunnel should already have been resolved from the header - * TEI and be available in tun (== p->tun). Just fill in the - * GSN Addresses below.*/ - OSMO_ASSERT(tun); - OSMO_ASSERT(tun->tei_repl == p->header_tei_rx); - OSMO_ASSERT(to_ctrl); - } - - uint8_t ie_type[] = { GTPIE_TEI_C, GTPIE_TEI_DI }; - int ie_mandatory = (p->type == GTP_CREATE_PDP_REQ); - unsigned int side_idx = p->side_idx; - - for (plane_idx = 0; plane_idx < 2; plane_idx++) { - int rc; - struct gsn_addr use_addr; - uint16_t use_port; - uint32_t tei_from_ie; - int ie_idx; - - /* Fetch GSN Address and TEI from IEs. As ensured by above - * static asserts, plane_idx corresponds to the GSN Address IE - * index (the first one = 0 = ctrl, second one = 1 = user). */ - rc = gsn_addr_get(&use_addr, p, plane_idx); - if (rc) { - LOG(LOGL_ERROR, "Cannot read %s GSN Address IE\n", - gtphub_plane_idx_names[plane_idx]); - return -1; - } - LOG(LOGL_DEBUG, "Read %s GSN addr %s (%d)\n", - gtphub_plane_idx_names[plane_idx], - gsn_addr_to_str(&use_addr), - use_addr.len); - - ie_idx = gtpie_getie(p->ie, ie_type[plane_idx], 0); - if (ie_idx < 0) { - if (ie_mandatory) { - LOG(LOGL_ERROR, - "Create PDP Context message invalid:" - " missing IE %d\n", - (int)ie_type[plane_idx]); - return -1; - } - tei_from_ie = 0; - } - else - tei_from_ie = ntoh32(p->ie[ie_idx]->tv4.v); - - /* Make sure an entry for this peer address with default port - * exists. - * - * Exception: if sgsn_use_sender is set, instead use the - * sender's address and port for Ctrl -- the User port is not - * known until the first User packet arrives. - * - * Note: doing this here is just an optimization, because - * gtphub_handle_buf() has code to replace the tunnel - * endpoints' addresses with the sender (needed for User - * plane). We could just ignore sgsn_use_sender here. But if we - * set up a default port here and replace it in - * gtphub_handle_buf(), we'd be creating a peer port just to - * expire it right away. */ - if (hub->sgsn_use_sender && (side_idx == GTPH_SIDE_SGSN)) { - gsn_addr_from_sockaddr(&use_addr, &use_port, &from_ctrl->sa); - } else { - use_port = gtphub_plane_idx_default_port[plane_idx]; - - } - - struct gtphub_peer_port *peer_from_ie; - peer_from_ie = gtphub_port_have(hub, - &hub->to_gsns[side_idx][plane_idx], - &use_addr, use_port); - - gtphub_tunnel_endpoint_set_peer(&tun->endpoint[side_idx][plane_idx], - peer_from_ie); - - if (!tei_from_ie && - !tun->endpoint[side_idx][plane_idx].tei_orig) { - LOG(LOGL_ERROR, - "Create PDP Context message omits %s TEI, but" - " no TEI has been announced for this tunnel: %s\n", - gtphub_plane_idx_names[plane_idx], - gtphub_tunnel_str(tun)); - return -1; - } - - if (tei_from_ie) { - /* Replace TEI in GTP packet IE */ - tun->endpoint[side_idx][plane_idx].tei_orig = tei_from_ie; - p->ie[ie_idx]->tv4.v = hton32(tun->tei_repl); - - if (!gtphub_check_reused_teis(hub, tun)) { - /* It's highly unlikely that all TEIs are - * taken. But the code looking for an unused - * TEI is, at the time of writing this comment, - * not able to find gaps in the TEI space. To - * explicitly alert the user of this problem, - * rather abort than carry on. */ - LOG(LOGL_FATAL, "TEI range exhausted. Cannot create TEI mapping, aborting.\n"); - abort(); - } - } - - /* Replace the GSN address to reflect gtphub. */ - rc = gsn_addr_put(&hub->to_gsns[other_side_idx(side_idx)][plane_idx].local_addr, - p, plane_idx); - if (rc) { - LOG(LOGL_ERROR, "Cannot write %s GSN Address IE\n", - gtphub_plane_idx_names[plane_idx]); - return -1; - } - } - - if (p->type == GTP_CREATE_PDP_REQ) { - LOG(LOGL_DEBUG, "New tunnel, first half: %s\n", - gtphub_tunnel_str(tun)); - } else if (p->type == GTP_CREATE_PDP_RSP) { - LOG(LOGL_DEBUG, "New tunnel: %s\n", - gtphub_tunnel_str(tun)); - } - - return 0; -} - -static void pending_delete_del_cb(struct expiring_item *expi) -{ - struct pending_delete *pd; - pd = container_of(expi, struct pending_delete, expiry_entry); - - llist_del(&pd->entry); - INIT_LLIST_HEAD(&pd->entry); - - pd->expiry_entry.del_cb = 0; - expiring_item_del(&pd->expiry_entry); - - talloc_free(pd); -} - -static struct pending_delete *pending_delete_new(void) -{ - struct pending_delete *pd = talloc_zero(osmo_gtphub_ctx, struct pending_delete); - INIT_LLIST_HEAD(&pd->entry); - expiring_item_init(&pd->expiry_entry); - pd->expiry_entry.del_cb = pending_delete_del_cb; - return pd; -} - -static int gtphub_handle_delete_pdp_ctx(struct gtphub *hub, - struct gtp_packet_desc *p, - struct gtphub_peer_port *from_ctrl, - struct gtphub_peer_port *to_ctrl) -{ - struct gtphub_tunnel *known_tun = p->tun; - - if (p->type == GTP_DELETE_PDP_REQ) { - if (!known_tun) { - LOG(LOGL_ERROR, "Cannot find tunnel for Delete PDP Context Request.\n"); - return -1; - } - - /* Store the Delete Request until a successful Response is seen. */ - uint8_t teardown_ind; - uint8_t nsapi; - - if (gtpie_gettv1(p->ie, GTPIE_TEARDOWN, 0, &teardown_ind) != 0) { - LOG(LOGL_ERROR, "Missing Teardown Ind IE in Delete PDP Context Request.\n"); - return -1; - } - - if (gtpie_gettv1(p->ie, GTPIE_NSAPI, 0, &nsapi) != 0) { - LOG(LOGL_ERROR, "Missing NSAPI IE in Delete PDP Context Request.\n"); - return -1; - } - - struct pending_delete *pd = NULL; - - struct pending_delete *pdi = NULL; - llist_for_each_entry(pdi, &hub->pending_deletes, entry) { - if ((pdi->tun == known_tun) - && (pdi->teardown_ind == teardown_ind) - && (pdi->nsapi == nsapi)) { - pd = pdi; - break; - } - } - - if (!pd) { - pd = pending_delete_new(); - pd->tun = known_tun; - pd->teardown_ind = teardown_ind; - pd->nsapi = nsapi; - - LOG(LOGL_DEBUG, "Tunnel delete pending: %s\n", - gtphub_tunnel_str(known_tun)); - llist_add(&pd->entry, &hub->pending_deletes); - } - - /* Add or refresh timeout. */ - expiry_add(&hub->expire_quickly, &pd->expiry_entry, p->timestamp); - - /* If a pending_delete should expire before the response to - * indicate success comes in, the responding peer will have the - * tunnel deactivated, while the requesting peer gets no reply - * and keeps the tunnel. The hope is that the requesting peer - * will try again and get a useful response. */ - } else if (p->type == GTP_DELETE_PDP_RSP) { - /* Find the Delete Request for this Response. */ - struct pending_delete *pd = NULL; - - struct pending_delete *pdi; - llist_for_each_entry(pdi, &hub->pending_deletes, entry) { - if (known_tun == pdi->tun) { - pd = pdi; - break; - } - } - - if (!pd) { - LOG(LOGL_ERROR, "Delete PDP Context Response:" - " Cannot find matching request."); - /* If we delete the tunnel now, anyone can send a - * Delete response to kill tunnels at will. */ - return -1; - } - - /* TODO handle teardown_ind and nsapi */ - - expiring_item_del(&pd->expiry_entry); - - uint8_t cause; - if (gtpie_gettv1(p->ie, GTPIE_CAUSE, 0, &cause) != 0) { - LOG(LOGL_ERROR, "Delete PDP Context Response:" - " Missing Cause IE."); - /* If we delete the tunnel now, at least one of the - * peers may still think it is active. */ - return -1; - } - - if (cause != GTPCAUSE_ACC_REQ) { - LOG(LOGL_NOTICE, - "Delete PDP Context Response indicates failure;" - "for %s\n", - gtphub_tunnel_str(known_tun)); - return -1; - } - - LOG(LOGL_DEBUG, "Delete PDP Context: removing tunnel %s\n", - gtphub_tunnel_str(known_tun)); - p->tun = NULL; - expiring_item_del(&known_tun->expiry_entry); - } - - return 0; -} - -static int gtphub_handle_update_pdp_ctx(struct gtphub *hub, - struct gtp_packet_desc *p, - struct gtphub_peer_port *from_ctrl, - struct gtphub_peer_port *to_ctrl) -{ - /* TODO */ - return 0; -} - -/* Read GSN address IEs from p, and make sure these peer addresses exist in - * bind[plane_idx] with default ports, in their respective planes (both Ctrl - * and User). Map TEIs announced in IEs, and write mapped TEIs in-place into - * the packet p. */ -static int gtphub_handle_pdp_ctx(struct gtphub *hub, - struct gtp_packet_desc *p, - struct gtphub_peer_port *from_ctrl, - struct gtphub_peer_port *to_ctrl) -{ - OSMO_ASSERT(p->plane_idx == GTPH_PLANE_CTRL); - - switch (p->type) { - case GTP_CREATE_PDP_REQ: - case GTP_CREATE_PDP_RSP: - return gtphub_handle_create_pdp_ctx(hub, p, - from_ctrl, to_ctrl); - - case GTP_DELETE_PDP_REQ: - case GTP_DELETE_PDP_RSP: - return gtphub_handle_delete_pdp_ctx(hub, p, - from_ctrl, to_ctrl); - - case GTP_UPDATE_PDP_REQ: - case GTP_UPDATE_PDP_RSP: - return gtphub_handle_update_pdp_ctx(hub, p, - from_ctrl, to_ctrl); - - default: - /* Nothing to do for this message type. */ - return 0; - } - -} - -static int gtphub_send_del_pdp_ctx(struct gtphub *hub, - struct gtphub_tunnel *tun, - int to_side) -{ - static uint8_t del_ctx_msg[16] = { - 0x32, /* GTP v1 flags */ - GTP_DELETE_PDP_REQ, - 0x00, 0x08, /* Length in network byte order */ - 0x00, 0x00, 0x00, 0x00, /* TEI to be replaced */ - 0, 0, /* Seq, to be replaced */ - 0, 0, /* no extensions */ - 0x13, 0xff, /* 19: Teardown ind = 1 */ - 0x14, 0 /* 20: NSAPI = 0 */ - }; - - uint32_t *tei = (uint32_t*)&del_ctx_msg[4]; - uint16_t *seq = (uint16_t*)&del_ctx_msg[8]; - - struct gtphub_tunnel_endpoint *te = - &tun->endpoint[to_side][GTPH_PLANE_CTRL]; - - if (! te->peer) - return 0; - - *tei = hton32(te->tei_orig); - *seq = hton16(nr_pool_next(&te->peer->peer_addr->peer->seq_pool)); - - struct gtphub_bind *to_bind = &hub->to_gsns[to_side][GTPH_PLANE_CTRL]; - int rc = gtphub_write(&to_bind->ofd, &te->peer->sa, - del_ctx_msg, sizeof(del_ctx_msg)); - if (rc != 0) { - LOG(LOGL_ERROR, - "Failed to send out-of-band Delete PDP Context Request to %s\n", - gtphub_port_str(te->peer)); - } - return rc; -} - -/* Tell all peers on the other end of tunnels that PDP contexts are void. */ -static void gtphub_restarted(struct gtphub *hub, - struct gtp_packet_desc *p, - struct gtphub_peer_port *pp) -{ - LOG(LOGL_NOTICE, "Peer has restarted: %s\n", - gtphub_port_str(pp)); - - int deleted_count = 0; - struct gtphub_tunnel *tun; - llist_for_each_entry(tun, &hub->tunnels, entry) { - int side_idx; - for_each_side(side_idx) { - struct gtphub_tunnel_endpoint *te = &tun->endpoint[side_idx][GTPH_PLANE_CTRL]; - struct gtphub_tunnel_endpoint *te2 = &tun->endpoint[other_side_idx(side_idx)][GTPH_PLANE_CTRL]; - if ((!te->peer) - || (!te2->tei_orig) - || (pp->peer_addr->peer != te->peer->peer_addr->peer)) - continue; - - LOG(LOGL_DEBUG, "Deleting tunnel due to peer restart: %s\n", - gtphub_tunnel_str(tun)); - deleted_count ++; - - /* Send a Delete PDP Context Request to the - * peer on the other side, remember the pending - * delete and wait for the response to delete - * the tunnel. Clear this side of the tunnel to - * make sure it isn't used. - * - * Should the delete message send fail, or if no - * response is received, this tunnel will expire. If - * its TEIs come up in a new PDP Context Request, it - * will be removed. If messages for this tunnel should - * come in (from the not restarted side), they will be - * dropped because the tunnel is rendered unusable. */ - gtphub_send_del_pdp_ctx(hub, tun, other_side_idx(side_idx)); - - gtphub_tunnel_endpoint_set_peer(&tun->endpoint[side_idx][GTPH_PLANE_CTRL], - NULL); - gtphub_tunnel_endpoint_set_peer(&tun->endpoint[side_idx][GTPH_PLANE_USER], - NULL); - } - } - - if (deleted_count) - LOG(LOGL_NOTICE, "Deleting %d tunnels due to restart of: %s\n", - deleted_count, - gtphub_port_str(pp)); -} - -static int get_restart_count(struct gtp_packet_desc *p) -{ - int ie_idx; - ie_idx = gtpie_getie(p->ie, GTPIE_RECOVERY, 0); - if (ie_idx < 0) - return -1; - return ntoh8(p->ie[ie_idx]->tv1.v); -} - -static void gtphub_check_restart_counter(struct gtphub *hub, - struct gtp_packet_desc *p, - struct gtphub_peer_port *from) -{ - /* If the peer is sending a Recovery IE (7.7.11) with a restart counter - * that doesn't match the peer's previously sent restart counter, clear - * that peer and cancel PDP contexts. */ - - int restart = get_restart_count(p); - - if ((restart < 0) || (restart > 255)) - return; - - if ((from->last_restart_count >= 0) && (from->last_restart_count <= 255)) { - if (from->last_restart_count != restart) { - gtphub_restarted(hub, p, from); - } - } - - from->last_restart_count = restart; -} - -static int from_sgsns_read_cb(struct osmo_fd *from_sgsns_ofd, unsigned int what) -{ - unsigned int plane_idx = from_sgsns_ofd->priv_nr; - OSMO_ASSERT(plane_idx < GTPH_PLANE_N); - LOG(LOGL_DEBUG, "=== reading from SGSN (%s)\n", - gtphub_plane_idx_names[plane_idx]); - - if (!(what & BSC_FD_READ)) - return 0; - - struct gtphub *hub = from_sgsns_ofd->data; - - static uint8_t buf[4096]; - struct osmo_sockaddr from_addr; - struct osmo_sockaddr to_addr; - struct osmo_fd *to_ofd; - int len; - uint8_t *reply_buf; - - len = gtphub_read(from_sgsns_ofd, &from_addr, buf, sizeof(buf)); - if (len < 1) - return 0; - - len = gtphub_handle_buf(hub, GTPH_SIDE_SGSN, plane_idx, &from_addr, - buf, len, gtphub_now(), - &reply_buf, &to_ofd, &to_addr); - if (len < 1) - return 0; - - return gtphub_write(to_ofd, &to_addr, reply_buf, len); -} - -static int from_ggsns_read_cb(struct osmo_fd *from_ggsns_ofd, unsigned int what) -{ - unsigned int plane_idx = from_ggsns_ofd->priv_nr; - OSMO_ASSERT(plane_idx < GTPH_PLANE_N); - LOG(LOGL_DEBUG, "=== reading from GGSN (%s)\n", - gtphub_plane_idx_names[plane_idx]); - if (!(what & BSC_FD_READ)) - return 0; - - struct gtphub *hub = from_ggsns_ofd->data; - - static uint8_t buf[4096]; - struct osmo_sockaddr from_addr; - struct osmo_sockaddr to_addr; - struct osmo_fd *to_ofd; - int len; - uint8_t *reply_buf; - - len = gtphub_read(from_ggsns_ofd, &from_addr, buf, sizeof(buf)); - if (len < 1) - return 0; - - len = gtphub_handle_buf(hub, GTPH_SIDE_GGSN, plane_idx, &from_addr, - buf, len, gtphub_now(), - &reply_buf, &to_ofd, &to_addr); - if (len < 1) - return 0; - - return gtphub_write(to_ofd, &to_addr, reply_buf, len); -} - -static int gtphub_unmap(struct gtphub *hub, - struct gtp_packet_desc *p, - struct gtphub_peer_port *from, - struct gtphub_peer_port *to_proxy, - struct gtphub_peer_port **final_unmapped, - struct gtphub_peer_port **unmapped_from_seq) -{ - /* Always (try to) unmap sequence and TEI numbers, which need to be - * replaced in the packet. Either way, give precedence to the proxy, if - * configured. */ - - if (unmapped_from_seq) - *unmapped_from_seq = NULL; - if (final_unmapped) - *final_unmapped = NULL; - p->tun = NULL; - - struct gtphub_peer_port *from_seq = NULL; - struct gtphub_peer_port *from_tei = NULL; - struct gtphub_peer_port *unmapped = NULL; - - from_seq = gtphub_unmap_seq(p, from); - - if (gtphub_unmap_header_tei(&from_tei, &p->tun, hub, p, from) < 0) - return -1; - - struct gtphub_peer *from_peer = from->peer_addr->peer; - if (from_seq && from_tei && (from_seq != from_tei)) { - LOG(LOGL_DEBUG, - "Seq unmap and TEI unmap yield two different peers." - " Using seq unmap." - " (from %s %s: seq %d yields %s, tei %u yields %s)\n", - gtphub_plane_idx_names[p->plane_idx], - gtphub_peer_str(from_peer), - (int)p->seq, - gtphub_port_str(from_seq), - (unsigned int)p->header_tei_rx, - gtphub_port_str2(from_tei) - ); - } - unmapped = (from_seq? from_seq : from_tei); - - if (unmapped && to_proxy && (unmapped != to_proxy)) { - LOG(LOGL_NOTICE, - "Unmap yields a different peer than the configured proxy." - " Using proxy." - " unmapped: %s proxy: %s\n", - gtphub_port_str(unmapped), - gtphub_port_str2(to_proxy) - ); - } - unmapped = (to_proxy? to_proxy : unmapped); - - if (!unmapped) { - /* Return no error, but returned pointers are all NULL. */ - return 0; - } - - if (unmapped_from_seq) - *unmapped_from_seq = from_seq; - if (final_unmapped) - *final_unmapped = unmapped; - return 0; -} - -static int gsn_addr_to_sockaddr(struct gsn_addr *src, - uint16_t port, - struct osmo_sockaddr *dst) -{ - return osmo_sockaddr_init_udp(dst, gsn_addr_to_str(src), port); -} - -/* If p is an Echo request, replace p's data with the matching response and - * return 1. If p is no Echo request, return 0, or -1 if an invalid packet is - * detected. */ -static int gtphub_handle_echo_req(struct gtphub *hub, struct gtp_packet_desc *p, - uint8_t **reply_buf) -{ - if (p->type != GTP_ECHO_REQ) - return 0; - - static uint8_t echo_response_data[14] = { - 0x32, /* GTP v1 flags */ - GTP_ECHO_RSP, - 0x00, 14 - 8, /* Length in network byte order */ - 0x00, 0x00, 0x00, 0x00, /* Zero TEI */ - 0, 0, /* Seq, to be replaced */ - 0, 0, /* no extensions */ - 0x0e, /* Recovery IE */ - 0 /* Restart counter, to be replaced */ - }; - uint16_t *seq = (uint16_t*)&echo_response_data[8]; - uint8_t *recovery = &echo_response_data[13]; - - *seq = hton16(p->seq); - *recovery = hub->restart_counter; - - *reply_buf = echo_response_data; - - return sizeof(echo_response_data); -} - -struct gtphub_peer_port *gtphub_known_addr_have_port(const struct gtphub_bind *bind, - const struct osmo_sockaddr *addr); - -/* Parse buffer as GTP packet, replace elements in-place and return the ofd and - * address to forward to. Return a pointer to the osmo_fd, but copy the - * sockaddr to *to_addr. The reason for this is that the sockaddr may expire at - * any moment, while the osmo_fd is guaranteed to persist. Return the number of - * bytes to forward, 0 or less on failure. */ -int gtphub_handle_buf(struct gtphub *hub, - unsigned int side_idx, - unsigned int plane_idx, - const struct osmo_sockaddr *from_addr, - uint8_t *buf, - size_t received, - time_t now, - uint8_t **reply_buf, - struct osmo_fd **to_ofd, - struct osmo_sockaddr *to_addr) -{ - struct gtphub_bind *from_bind = &hub->to_gsns[side_idx][plane_idx]; - struct gtphub_bind *to_bind = &hub->to_gsns[other_side_idx(side_idx)][plane_idx]; - - rate_ctr_add(&from_bind->counters_io->ctr[GTPH_CTR_BYTES_IN], - received); - - struct gtp_packet_desc p; - gtp_decode(buf, received, side_idx, plane_idx, &p, now); - - LOG(LOGL_DEBUG, "%s rx %s from %s %s%s\n", - (side_idx == GTPH_SIDE_GGSN)? "<-" : "->", - gtphub_plane_idx_names[plane_idx], - gtphub_side_idx_names[side_idx], - osmo_sockaddr_to_str(from_addr), - gtp_type_str(p.type)); - - if (p.rc <= 0) { - LOG(LOGL_ERROR, "INVALID: dropping GTP packet%s from %s %s %s\n", - gtp_type_str(p.type), - gtphub_side_idx_names[side_idx], - gtphub_plane_idx_names[plane_idx], - osmo_sockaddr_to_str(from_addr)); - return -1; - } - - rate_ctr_inc(&from_bind->counters_io->ctr[GTPH_CTR_PKTS_IN]); - - int reply_len; - reply_len = gtphub_handle_echo_req(hub, &p, reply_buf); - if (reply_len > 0) { - /* It was an echo. Nothing left to do. */ - osmo_sockaddr_copy(to_addr, from_addr); - *to_ofd = &from_bind->ofd; - - rate_ctr_inc(&from_bind->counters_io->ctr[GTPH_CTR_PKTS_OUT]); - rate_ctr_add(&from_bind->counters_io->ctr[GTPH_CTR_BYTES_OUT], - reply_len); - LOG(LOGL_DEBUG, "%s Echo response to %s: %d bytes to %s\n", - (side_idx == GTPH_SIDE_GGSN)? "-->" : "<--", - gtphub_side_idx_names[side_idx], - (int)reply_len, osmo_sockaddr_to_str(to_addr)); - return reply_len; - } - if (reply_len < 0) - return -1; - - *to_ofd = &to_bind->ofd; - - /* If a proxy is configured, check that it's indeed that proxy talking - * to us. A proxy is a forced 1:1 connection, e.g. to another gtphub, - * so no-one else is allowed to talk to us from that side. */ - struct gtphub_peer_port *from_peer = hub->proxy[side_idx][plane_idx]; - if (from_peer) { - if (osmo_sockaddr_cmp(&from_peer->sa, from_addr) != 0) { - LOG(LOGL_ERROR, - "Rejecting: %s proxy configured, but GTP packet" - " received on %s bind is from another sender:" - " proxy: %s sender: %s\n", - gtphub_side_idx_names[side_idx], - gtphub_side_idx_names[side_idx], - gtphub_port_str(from_peer), - osmo_sockaddr_to_str(from_addr)); - return -1; - } - } - - if (!from_peer) { - /* Find or create a peer with a matching address. The sender's - * port may in fact differ. */ - from_peer = gtphub_known_addr_have_port(from_bind, from_addr); - } - - /* If any PDP context has been created, we already have an entry for - * this GSN. If we don't have an entry, a GGSN has nothing to tell us - * about, while an SGSN may initiate a PDP context. */ - if (!from_peer) { - if (side_idx == GTPH_SIDE_GGSN) { - LOG(LOGL_ERROR, "Dropping packet%s: unknown GGSN peer: %s\n", - gtp_type_str(p.type), - osmo_sockaddr_to_str(from_addr)); - return -1; - } else { - /* SGSN */ - /* A new peer. If this is on the Ctrl plane, an SGSN - * may make first contact without being known yet, so - * create the peer struct for the current sender. */ - if (plane_idx != GTPH_PLANE_CTRL) { - LOG(LOGL_ERROR, - "Dropping packet%s: User plane peer was not" - "announced by PDP Context: %s\n", - gtp_type_str(p.type), - osmo_sockaddr_to_str(from_addr)); - return -1; - } - - struct gsn_addr from_gsna; - uint16_t from_port; - if (gsn_addr_from_sockaddr(&from_gsna, &from_port, from_addr) != 0) - return -1; - - from_peer = gtphub_port_have(hub, from_bind, &from_gsna, from_port); - } - } - - if (!from_peer) { - /* This could theoretically happen for invalid address data or - * somesuch. */ - LOG(LOGL_ERROR, "Dropping packet%s: invalid %s peer: %s\n", - gtp_type_str(p.type), - gtphub_side_idx_names[side_idx], - osmo_sockaddr_to_str(from_addr)); - return -1; - } - - rate_ctr_add(&from_peer->counters_io->ctr[GTPH_CTR_BYTES_IN], - received); - rate_ctr_inc(&from_peer->counters_io->ctr[GTPH_CTR_PKTS_IN]); - - LOG(LOGL_DEBUG, "from %s peer: %s\n", gtphub_side_idx_names[side_idx], - gtphub_port_str(from_peer)); - - gtphub_check_restart_counter(hub, &p, from_peer); - gtphub_map_restart_counter(hub, &p); - - struct gtphub_peer_port *to_peer_from_seq; - struct gtphub_peer_port *to_peer; - if (gtphub_unmap(hub, &p, from_peer, - hub->proxy[other_side_idx(side_idx)][plane_idx], - &to_peer, &to_peer_from_seq) - != 0) { - return -1; - } - - if (p.tun) { - struct gtphub_tunnel_endpoint *te = &p.tun->endpoint[p.side_idx][p.plane_idx]; - rate_ctr_add(&te->counters_io->ctr[GTPH_CTR_BYTES_IN], - received); - rate_ctr_inc(&te->counters_io->ctr[GTPH_CTR_PKTS_IN]); - } - - if ((!to_peer) && (side_idx == GTPH_SIDE_SGSN)) { - if (gtphub_resolve_ggsn(hub, &p, &to_peer) < 0) - return -1; - } - - if (!to_peer && p.tun && p.type == GTP_DELETE_PDP_RSP) { - /* It's a delete confirmation for a tunnel that is partly - * invalid, probably marked unsuable due to a restarted peer. - * Remove the tunnel and be happy without forwarding. */ - expiring_item_del(&p.tun->expiry_entry); - p.tun = NULL; - return 0; - } - - if (!to_peer) { - LOG(LOGL_ERROR, "No %s to send to. Dropping packet%s" - " (type=%" PRIu8 ", header-TEI=%" PRIx32 ", seq=%" PRIx16 ").\n", - gtphub_side_idx_names[other_side_idx(side_idx)], - gtp_type_str(p.type), - p.type, p.header_tei_rx, p.seq - ); - return -1; - } - - if (plane_idx == GTPH_PLANE_CTRL) { - /* This may be a Create PDP Context response. If it is, there - * are other addresses in the GTP message to set up apart from - * the sender. */ - if (gtphub_handle_pdp_ctx(hub, &p, from_peer, to_peer) - != 0) - return -1; - } - - /* Either to_peer was resolved from an existing tunnel, - * or a PDP Ctx and thus a tunnel has just been created, - * or the tunnel has been deleted due to this message. */ - OSMO_ASSERT(p.tun || (p.type == GTP_DELETE_PDP_RSP)); - - /* If the GGSN is replying to an SGSN request, the sequence nr has - * already been unmapped above (to_peer_from_seq != NULL), and we need not - * create a new mapping. */ - if (!to_peer_from_seq) - gtphub_map_seq(&p, from_peer, to_peer); - - osmo_sockaddr_copy(to_addr, &to_peer->sa); - - *reply_buf = (uint8_t*)p.data; - - if (received) { - rate_ctr_inc(&to_bind->counters_io->ctr[GTPH_CTR_PKTS_OUT]); - rate_ctr_add(&to_bind->counters_io->ctr[GTPH_CTR_BYTES_OUT], - received); - - rate_ctr_inc(&to_peer->counters_io->ctr[GTPH_CTR_PKTS_OUT]); - rate_ctr_add(&to_peer->counters_io->ctr[GTPH_CTR_BYTES_OUT], - received); - } - - if (p.tun) { - struct gtphub_tunnel_endpoint *te = &p.tun->endpoint[other_side_idx(p.side_idx)][p.plane_idx]; - rate_ctr_inc(&te->counters_io->ctr[GTPH_CTR_PKTS_OUT]); - rate_ctr_add(&te->counters_io->ctr[GTPH_CTR_BYTES_OUT], - received); - } - - LOG(LOGL_DEBUG, "%s Forward to %s:" - " header-TEI %" PRIx32", seq %" PRIx16", %d bytes to %s\n", - (side_idx == GTPH_SIDE_SGSN)? "-->" : "<--", - gtphub_side_idx_names[other_side_idx(side_idx)], - p.header_tei, p.seq, - (int)received, osmo_sockaddr_to_str(to_addr)); - return received; -} - -static void resolved_gssn_del_cb(struct expiring_item *expi) -{ - struct gtphub_resolved_ggsn *ggsn; - ggsn = container_of(expi, struct gtphub_resolved_ggsn, expiry_entry); - - gtphub_port_ref_count_dec(ggsn->peer); - llist_del(&ggsn->entry); - - ggsn->expiry_entry.del_cb = 0; - expiring_item_del(&ggsn->expiry_entry); - - talloc_free(ggsn); -} - -void gtphub_resolved_ggsn(struct gtphub *hub, const char *apn_oi_str, - struct gsn_addr *resolved_addr, - time_t now) -{ - struct gtphub_peer_port *pp; - struct gtphub_resolved_ggsn *ggsn; - - LOG(LOGL_DEBUG, "Resolved GGSN callback: %s %s\n", - apn_oi_str, osmo_hexdump((unsigned char*)resolved_addr, - sizeof(*resolved_addr))); - - pp = gtphub_port_have(hub, &hub->to_gsns[GTPH_SIDE_GGSN][GTPH_PLANE_CTRL], - resolved_addr, 2123); - if (!pp) { - LOG(LOGL_ERROR, "Internal: Cannot create/find peer '%s'\n", - gsn_addr_to_str(resolved_addr)); - return; - } - - ggsn = talloc_zero(osmo_gtphub_ctx, struct gtphub_resolved_ggsn); - OSMO_ASSERT(ggsn); - INIT_LLIST_HEAD(&ggsn->entry); - expiring_item_init(&ggsn->expiry_entry); - - ggsn->peer = pp; - gtphub_port_ref_count_inc(pp); - - osmo_strlcpy(ggsn->apn_oi_str, apn_oi_str, sizeof(ggsn->apn_oi_str)); - - ggsn->expiry_entry.del_cb = resolved_gssn_del_cb; - expiry_add(&hub->expire_slowly, &ggsn->expiry_entry, now); - - llist_add(&ggsn->entry, &hub->resolved_ggsns); -} - -static int gtphub_gc_peer_port(struct gtphub_peer_port *pp) -{ - return pp->ref_count == 0; -} - -static int gtphub_gc_peer_addr(struct gtphub_peer_addr *pa) -{ - struct gtphub_peer_port *pp, *npp; - llist_for_each_entry_safe(pp, npp, &pa->ports, entry) { - if (gtphub_gc_peer_port(pp)) { - LOG(LOGL_DEBUG, "expired: peer %s\n", - gtphub_port_str(pp)); - gtphub_peer_port_del(pp); - } - } - return llist_empty(&pa->ports); -} - -static int gtphub_gc_peer(struct gtphub_peer *p) -{ - struct gtphub_peer_addr *pa, *npa; - llist_for_each_entry_safe(pa, npa, &p->addresses, entry) { - if (gtphub_gc_peer_addr(pa)) { - gtphub_peer_addr_del(pa); - } - } - - /* Note that there's a ref_count in each gtphub_peer_port instance - * listed within p->addresses, referenced by TEI mappings from - * hub->tei_map. As long as those don't expire, this peer will stay. */ - - return llist_empty(&p->addresses) - && nr_map_empty(&p->seq_map); -} - -static void gtphub_gc_bind(struct gtphub_bind *b) -{ - struct gtphub_peer *p, *n; - llist_for_each_entry_safe(p, n, &b->peers, entry) { - if (gtphub_gc_peer(p)) { - gtphub_peer_del(p); - } - } -} - -void gtphub_gc(struct gtphub *hub, time_t now) -{ - int expired; - expired = expiry_tick(&hub->expire_quickly, now); - expired += expiry_tick(&hub->expire_slowly, now); - - /* ... */ - - if (expired) { - int s, p; - for_each_side_and_plane(s, p) { - gtphub_gc_bind(&hub->to_gsns[s][p]); - } - } -} - -static void gtphub_gc_cb(void *data) -{ - struct gtphub *hub = data; - gtphub_gc(hub, gtphub_now()); - osmo_timer_schedule(&hub->gc_timer, GTPH_GC_TICK_SECONDS, 0); -} - -static void gtphub_gc_start(struct gtphub *hub) -{ - osmo_timer_setup(&hub->gc_timer, gtphub_gc_cb, hub); - osmo_timer_schedule(&hub->gc_timer, GTPH_GC_TICK_SECONDS, 0); -} - -/* called by unit tests */ -void gtphub_init(struct gtphub *hub) -{ - gtphub_zero(hub); - - INIT_LLIST_HEAD(&hub->tunnels); - INIT_LLIST_HEAD(&hub->pending_deletes); - - expiry_init(&hub->expire_quickly, GTPH_EXPIRE_QUICKLY_SECS); - expiry_init(&hub->expire_slowly, GTPH_EXPIRE_SLOWLY_MINUTES * 60); - - nr_pool_init(&hub->tei_pool, 1, 0xffffffff); - - int side_idx; - int plane_idx; - for_each_side_and_plane(side_idx, plane_idx) { - gtphub_bind_init(&hub->to_gsns[side_idx][plane_idx]); - } - - hub->to_gsns[GTPH_SIDE_SGSN][GTPH_PLANE_CTRL].label = "SGSN Ctrl"; - hub->to_gsns[GTPH_SIDE_GGSN][GTPH_PLANE_CTRL].label = "GGSN Ctrl"; - hub->to_gsns[GTPH_SIDE_SGSN][GTPH_PLANE_USER].label = "SGSN User"; - hub->to_gsns[GTPH_SIDE_GGSN][GTPH_PLANE_USER].label = "GGSN User"; -} - -/* For the test suite, this is kept separate from gtphub_stop(), which also - * closes sockets. The test suite avoids using sockets and would cause - * segfaults when trying to close uninitialized ofds. */ -void gtphub_free(struct gtphub *hub) -{ - /* By expiring all mappings, a garbage collection should free - * everything else. A gtphub_bind_free() will assert that everything is - * indeed empty. */ - expiry_clear(&hub->expire_quickly); - expiry_clear(&hub->expire_slowly); - - int side_idx; - int plane_idx; - for_each_side_and_plane(side_idx, plane_idx) { - gtphub_gc_bind(&hub->to_gsns[side_idx][plane_idx]); - gtphub_bind_free(&hub->to_gsns[side_idx][plane_idx]); - } -} - -void gtphub_stop(struct gtphub *hub) -{ - int side_idx; - int plane_idx; - for_each_side_and_plane(side_idx, plane_idx) { - gtphub_bind_stop(&hub->to_gsns[side_idx][plane_idx]); - } - gtphub_free(hub); -} - -static int gtphub_make_proxy(struct gtphub *hub, - struct gtphub_peer_port **pp, - struct gtphub_bind *bind, - const struct gtphub_cfg_addr *addr) -{ - if (!addr->addr_str) - return 0; - - struct gsn_addr gsna; - if (gsn_addr_from_str(&gsna, addr->addr_str) != 0) - return -1; - - *pp = gtphub_port_have(hub, bind, &gsna, addr->port); - - /* This is *the* proxy. Make sure it is never expired. */ - gtphub_port_ref_count_inc(*pp); - return 0; -} - -int gtphub_start(struct gtphub *hub, struct gtphub_cfg *cfg, - uint8_t restart_counter) -{ - gtphub_init(hub); - - hub->restart_counter = restart_counter; - hub->sgsn_use_sender = cfg->sgsn_use_sender? 1 : 0; - - /* If a Ctrl plane proxy is configured, ares will never be used. */ - if (!cfg->proxy[GTPH_SIDE_GGSN][GTPH_PLANE_CTRL].addr_str) { - if (gtphub_ares_init(hub) != 0) { - LOG(LOGL_FATAL, "Failed to initialize ares\n"); - return -1; - } - } - - int side_idx; - int plane_idx; - for_each_side_and_plane(side_idx, plane_idx) { - int rc; - rc = gtphub_bind_start(&hub->to_gsns[side_idx][plane_idx], - &cfg->to_gsns[side_idx][plane_idx], - (side_idx == GTPH_SIDE_SGSN) - ? from_sgsns_read_cb - : from_ggsns_read_cb, - hub, plane_idx); - if (rc) { - LOG(LOGL_FATAL, "Failed to bind for %ss (%s)\n", - gtphub_side_idx_names[side_idx], - gtphub_plane_idx_names[plane_idx]); - return rc; - } - } - - for_each_side_and_plane(side_idx, plane_idx) { - if (gtphub_make_proxy(hub, - &hub->proxy[side_idx][plane_idx], - &hub->to_gsns[side_idx][plane_idx], - &cfg->proxy[side_idx][plane_idx]) - != 0) { - LOG(LOGL_FATAL, "Cannot configure %s proxy" - " %s port %d.\n", - gtphub_side_idx_names[side_idx], - cfg->proxy[side_idx][plane_idx].addr_str, - (int)cfg->proxy[side_idx][plane_idx].port); - return -1; - } - } - - for_each_side_and_plane(side_idx, plane_idx) { - if (hub->proxy[side_idx][plane_idx]) - LOG(LOGL_NOTICE, "Using %s %s proxy %s\n", - gtphub_side_idx_names[side_idx], - gtphub_plane_idx_names[plane_idx], - gtphub_port_str(hub->proxy[side_idx][plane_idx])); - } - - if (hub->sgsn_use_sender) - LOG(LOGL_NOTICE, "Using sender address and port for SGSN instead of GSN Addr IE and default ports.\n"); - - gtphub_gc_start(hub); - return 0; -} - -static struct gtphub_peer_addr *gtphub_peer_find_addr(const struct gtphub_peer *peer, - const struct gsn_addr *addr) -{ - struct gtphub_peer_addr *a; - llist_for_each_entry(a, &peer->addresses, entry) { - if (gsn_addr_same(&a->addr, addr)) - return a; - } - return NULL; -} - -static struct gtphub_peer_port *gtphub_addr_find_port(const struct gtphub_peer_addr *a, - uint16_t port) -{ - OSMO_ASSERT(port); - struct gtphub_peer_port *pp; - llist_for_each_entry(pp, &a->ports, entry) { - if (pp->port == port) - return pp; - } - return NULL; -} - -static struct gtphub_peer_addr *gtphub_addr_find(const struct gtphub_bind *bind, - const struct gsn_addr *addr) -{ - struct gtphub_peer *peer; - llist_for_each_entry(peer, &bind->peers, entry) { - struct gtphub_peer_addr *a = gtphub_peer_find_addr(peer, addr); - if (a) - return a; - } - return NULL; -} - -static struct gtphub_peer_port *gtphub_port_find(const struct gtphub_bind *bind, - const struct gsn_addr *addr, - uint16_t port) -{ - struct gtphub_peer_addr *a = gtphub_addr_find(bind, addr); - if (!a) - return NULL; - return gtphub_addr_find_port(a, port); -} - -struct gtphub_peer_port *gtphub_port_find_sa(const struct gtphub_bind *bind, - const struct osmo_sockaddr *addr) -{ - struct gsn_addr gsna; - uint16_t port; - gsn_addr_from_sockaddr(&gsna, &port, addr); - return gtphub_port_find(bind, &gsna, port); -} - -static struct gtphub_peer *gtphub_peer_new(struct gtphub *hub, - struct gtphub_bind *bind) -{ - struct gtphub_peer *peer = talloc_zero(osmo_gtphub_ctx, - struct gtphub_peer); - OSMO_ASSERT(peer); - - INIT_LLIST_HEAD(&peer->addresses); - - nr_pool_init(&peer->seq_pool, 0, 0xffff); - nr_map_init(&peer->seq_map, &peer->seq_pool, &hub->expire_quickly); - - /* TODO use something random to pick the initial sequence nr. - 0x6d31 produces the ASCII character sequence 'm1', currently used in - gtphub_nc_test.sh. */ - peer->seq_pool.last_nr = 0x6d31 - 1; - - llist_add(&peer->entry, &bind->peers); - return peer; -} - -static struct gtphub_peer_addr *gtphub_peer_add_addr(struct gtphub_peer *peer, - const struct gsn_addr *addr) -{ - struct gtphub_peer_addr *a; - a = talloc_zero(osmo_gtphub_ctx, struct gtphub_peer_addr); - OSMO_ASSERT(a); - a->peer = peer; - gsn_addr_copy(&a->addr, addr); - INIT_LLIST_HEAD(&a->ports); - llist_add(&a->entry, &peer->addresses); - - return a; -} - -static struct gtphub_peer_addr *gtphub_addr_have(struct gtphub *hub, - struct gtphub_bind *bind, - const struct gsn_addr *addr) -{ - struct gtphub_peer_addr *a = gtphub_addr_find(bind, addr); - if (a) - return a; - - /* If we haven't found an address, that means we need to create an - * entirely new peer for the new address. More addresses may be added - * to this peer later, but not via this function. */ - struct gtphub_peer *peer = gtphub_peer_new(hub, bind); - - a = gtphub_peer_add_addr(peer, addr); - - LOG(LOGL_DEBUG, "New peer address: %s %s\n", - bind->label, - gsn_addr_to_str(&a->addr)); - - return a; -} - -static struct gtphub_peer_port *gtphub_addr_add_port(struct gtphub_peer_addr *a, - uint16_t port) -{ - struct gtphub_peer_port *pp; - - pp = talloc_zero(osmo_gtphub_ctx, struct gtphub_peer_port); - OSMO_ASSERT(pp); - pp->peer_addr = a; - pp->port = port; - pp->last_restart_count = -1; - - if (gsn_addr_to_sockaddr(&a->addr, port, &pp->sa) != 0) { - talloc_free(pp); - return NULL; - } - - pp->counters_io = rate_ctr_group_alloc(osmo_gtphub_ctx, - >phub_ctrg_io_desc, 0); - - llist_add(&pp->entry, &a->ports); - - LOG(LOGL_DEBUG, "New peer port: %s port %d\n", - gsn_addr_to_str(&a->addr), - (int)port); - - return pp; -} - -struct gtphub_peer_port *gtphub_port_have(struct gtphub *hub, - struct gtphub_bind *bind, - const struct gsn_addr *addr, - uint16_t port) -{ - struct gtphub_peer_addr *a = gtphub_addr_have(hub, bind, addr); - - struct gtphub_peer_port *pp = gtphub_addr_find_port(a, port); - if (pp) - return pp; - - return gtphub_addr_add_port(a, port); -} - -/* Find a GGSN peer with a matching address. If the address is known but the - * port not, create a new port for that peer address. */ -struct gtphub_peer_port *gtphub_known_addr_have_port(const struct gtphub_bind *bind, - const struct osmo_sockaddr *addr) -{ - struct gtphub_peer_addr *pa; - struct gtphub_peer_port *pp; - - struct gsn_addr gsna; - uint16_t port; - gsn_addr_from_sockaddr(&gsna, &port, addr); - - pa = gtphub_addr_find(bind, &gsna); - if (!pa) - return NULL; - - pp = gtphub_addr_find_port(pa, port); - - if (!pp) - pp = gtphub_addr_add_port(pa, port); - - return pp; -} - - -/* Return 0 if the message in p is not applicable for GGSN resolution, -1 if - * resolution should be possible but failed, and 1 if resolution was - * successful. *pp will be set to NULL if <1 is returned. */ -static int gtphub_resolve_ggsn(struct gtphub *hub, - struct gtp_packet_desc *p, - struct gtphub_peer_port **pp) -{ - *pp = NULL; - - /* TODO determine from message type whether IEs should be present? */ - - int rc; - const char *imsi_str; - rc = get_ie_imsi_str(p->ie, 0, &imsi_str); - if (rc < 1) - return rc; - OSMO_ASSERT(imsi_str); - - const char *apn_str; - rc = get_ie_apn_str(p->ie, &apn_str); - if (rc < 1) - return rc; - OSMO_ASSERT(apn_str); - - *pp = gtphub_resolve_ggsn_addr(hub, imsi_str, apn_str); - return (*pp)? 1 : -1; -} - - -/* TODO move to osmocom/core/socket.c ? */ -/* use this in osmo_sock_init() to remove dup. */ -/* Internal: call getaddrinfo for osmo_sockaddr_init(). The caller is required - to call freeaddrinfo(*result), iff zero is returned. */ -static int _osmo_getaddrinfo(struct addrinfo **result, - uint16_t family, uint16_t type, uint8_t proto, - const char *host, uint16_t port) -{ - struct addrinfo hints; - char portbuf[16]; - - sprintf(portbuf, "%u", port); - memset(&hints, '\0', sizeof(struct addrinfo)); - hints.ai_family = family; - if (type == SOCK_RAW) { - /* Workaround for glibc, that returns EAI_SERVICE (-8) if - * SOCK_RAW and IPPROTO_GRE is used. - */ - hints.ai_socktype = SOCK_DGRAM; - hints.ai_protocol = IPPROTO_UDP; - } else { - hints.ai_socktype = type; - hints.ai_protocol = proto; - } - - return getaddrinfo(host, portbuf, &hints, result); -} - -/* TODO move to osmocom/core/socket.c ? */ -int osmo_sockaddr_init(struct osmo_sockaddr *addr, - uint16_t family, uint16_t type, uint8_t proto, - const char *host, uint16_t port) -{ - struct addrinfo *res; - int rc; - rc = _osmo_getaddrinfo(&res, family, type, proto, host, port); - - if (rc != 0) { - LOG(LOGL_ERROR, "getaddrinfo returned error %d\n", (int)rc); - return -EINVAL; - } - - OSMO_ASSERT(res->ai_addrlen <= sizeof(addr->a)); - memcpy(&addr->a, res->ai_addr, res->ai_addrlen); - addr->l = res->ai_addrlen; - freeaddrinfo(res); - - return 0; -} - -int osmo_sockaddr_to_strs(char *addr_str, size_t addr_str_len, - char *port_str, size_t port_str_len, - const struct osmo_sockaddr *addr, - int flags) -{ - int rc; - - if ((addr->l < 1) || (addr->l > sizeof(addr->a))) { - LOGP(DGTPHUB, LOGL_ERROR, "Invalid address size: %d\n", addr->l); - return -1; - } - - if (addr->l > sizeof(addr->a)) { - LOGP(DGTPHUB, LOGL_ERROR, "Invalid address: too long: %d\n", - addr->l); - return -1; - } - - rc = getnameinfo((struct sockaddr*)&addr->a, addr->l, - addr_str, addr_str_len, - port_str, port_str_len, - flags); - - if (rc) - LOGP(DGTPHUB, LOGL_ERROR, "Invalid address: %s: %s\n", - gai_strerror(rc), osmo_hexdump((uint8_t*)&addr->a, - addr->l)); - - return rc; -} - -const char *osmo_sockaddr_to_strb(const struct osmo_sockaddr *addr, - char *buf, size_t buf_len) -{ - const int portbuf_len = 6; - OSMO_ASSERT(buf_len > portbuf_len); - char *portbuf = buf + buf_len - portbuf_len; - buf_len -= portbuf_len; - if (osmo_sockaddr_to_strs(buf, buf_len, - portbuf, portbuf_len, - addr, - NI_NUMERICHOST | NI_NUMERICSERV)) - return NULL; - - char *pos = buf + strnlen(buf, buf_len-1); - size_t len = buf_len - (pos - buf); - - snprintf(pos, len, " port %s", portbuf); - buf[buf_len-1] = '\0'; - - return buf; -} - -const char *osmo_sockaddr_to_str(const struct osmo_sockaddr *addr) -{ - static char buf[256]; - const char *result = osmo_sockaddr_to_strb(addr, buf, sizeof(buf)); - if (! result) - return "(invalid)"; - return result; -} - -int osmo_sockaddr_cmp(const struct osmo_sockaddr *a, - const struct osmo_sockaddr *b) -{ - if (a == b) - return 0; - if (!a) - return -1; - if (!b) - return 1; - if (a->l != b->l) { - /* Lengths are not the same, but determine the order. Will - * anyone ever sort a list by osmo_sockaddr though...? */ - int cmp = memcmp(&a->a, &b->a, (a->l < b->l)? a->l : b->l); - if (cmp == 0) { - if (a->l < b->l) - return -1; - else - return 1; - } - return cmp; - } - return memcmp(&a->a, &b->a, a->l); -} - -void osmo_sockaddr_copy(struct osmo_sockaddr *dst, - const struct osmo_sockaddr *src) -{ - OSMO_ASSERT(src->l <= sizeof(dst->a)); - memcpy(&dst->a, &src->a, src->l); - dst->l = src->l; -} diff --git a/openbsc/src/gprs/gtphub_ares.c b/openbsc/src/gprs/gtphub_ares.c deleted file mode 100644 index afeeda65..00000000 --- a/openbsc/src/gprs/gtphub_ares.c +++ /dev/null @@ -1,220 +0,0 @@ -/* GTP Hub Implementation */ - -/* (C) 2015 by sysmocom s.f.m.c. GmbH - * All Rights Reserved - * - * gtphub_ares.c. - * - * This file is kept separate so that these functions can be wrapped for - * gtphub_test.c. When a function and its callers are in the same compilational - * unit, the wrappability may be optimized away. - * - * Author: Neels Hofmeyr - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation; either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - */ - -#include -#include - -#include -#include - -#include -#include - -/* TODO split GRX ares from sgsn into a separate struct and allow use without - * globals. */ -#include -extern struct sgsn_instance *sgsn; - -struct sgsn_instance sgsn_inst = { 0 }; -struct sgsn_instance *sgsn = &sgsn_inst; - -extern void *osmo_gtphub_ctx; - -int gtphub_ares_init(struct gtphub *hub) -{ - return sgsn_ares_init(sgsn); -} - -struct ggsn_lookup { - struct llist_head entry; - struct expiring_item expiry_entry; - - struct gtphub *hub; - - char imsi_str[GSM23003_IMSI_MAX_DIGITS+1]; - char apn_ni_str[GSM_APN_LENGTH]; - char apn_oi_str[GSM_APN_LENGTH]; - int have_3dig_mnc; -}; - -static int start_ares_query(struct ggsn_lookup *lookup); - -static void ggsn_lookup_cb(void *arg, int status, int timeouts, - struct hostent *hostent) -{ - struct ggsn_lookup *lookup = arg; - LOGP(DGTPHUB, LOGL_NOTICE, "ggsn_lookup_cb(%p / %p)", lookup, - &lookup->expiry_entry); - - if (status != ARES_SUCCESS) { - LOGP(DGTPHUB, LOGL_ERROR, "DNS query failed.\n"); - - /* Need to try with three digits now */ - if (!lookup->have_3dig_mnc) { - lookup->have_3dig_mnc = 1; - if (start_ares_query(lookup) == 0) - return; - } - - LOGP(DGTPHUB, LOGL_ERROR, "Failed to resolve GGSN. (%p)\n", - lookup); - goto remove_from_queue; - } - - struct gsn_addr resolved_addr; - if (hostent->h_length > sizeof(resolved_addr.buf)) { - LOGP(DGTPHUB, LOGL_ERROR, "Addr size too large: %d > %d\n", - (int)hostent->h_length, (int)sizeof(resolved_addr.buf)); - goto remove_from_queue; - } - - /* Get the first addr from the list */ - char *addr0 = hostent->h_addr_list[0]; - if (!addr0) { - LOGP(DGTPHUB, LOGL_ERROR, "No host address.\n"); - goto remove_from_queue; - } - - memcpy(resolved_addr.buf, addr0, hostent->h_length); - resolved_addr.len = hostent->h_length; - - LOGP(DGTPHUB, LOGL_NOTICE, "resolved addr %s\n", - osmo_hexdump((unsigned char*)&resolved_addr, - sizeof(resolved_addr))); - - gtphub_resolved_ggsn(lookup->hub, lookup->apn_oi_str, &resolved_addr, - gtphub_now()); - -remove_from_queue: - LOGP(DGTPHUB, LOGL_ERROR, "Removing GGSN lookup. (%p / %p)\n", lookup, - &lookup->expiry_entry); - expiring_item_del(&lookup->expiry_entry); -} - -static void make_addr_str(struct ggsn_lookup *lookup) -{ - char *apn_oi_str; - apn_oi_str = osmo_apn_qualify_from_imsi(lookup->imsi_str, - lookup->apn_ni_str, - lookup->have_3dig_mnc); - osmo_strlcpy(lookup->apn_oi_str, apn_oi_str, - sizeof(lookup->apn_oi_str)); -} - -static int start_ares_query(struct ggsn_lookup *lookup) -{ - LOGP(DGTPHUB, LOGL_DEBUG, "Going to query %s (%p / %p)\n", - lookup->apn_oi_str, lookup, &lookup->expiry_entry); - - int rc = sgsn_ares_query(sgsn, lookup->apn_oi_str, ggsn_lookup_cb, - lookup); - if (rc != 0) - LOGP(DGTPHUB, LOGL_ERROR, "Failed to start ares query.\n"); - return rc; -} - -static void ggsn_lookup_del_cb(struct expiring_item *expi) -{ - struct ggsn_lookup *lookup; - lookup = container_of(expi, struct ggsn_lookup, expiry_entry); - - LOGP(DGTPHUB, LOGL_NOTICE, "ggsn_lookup_del_cb(%p / %p)\n", lookup, - expi); - - lookup->expiry_entry.del_cb = 0; - expiring_item_del(expi); - - llist_del(&lookup->entry); - talloc_free(lookup); -} - -struct gtphub_peer_port *gtphub_resolve_ggsn_addr(struct gtphub *hub, - const char *imsi_str, - const char *apn_ni_str) -{ - OSMO_ASSERT(imsi_str); - OSMO_ASSERT(apn_ni_str); - - struct ggsn_lookup *lookup = talloc_zero(osmo_gtphub_ctx, - struct ggsn_lookup); - OSMO_ASSERT(lookup); - - LOGP(DGTPHUB, LOGL_DEBUG, "Request to resolve IMSI" - " '%s' with APN-NI '%s' (%p / %p)\n", - imsi_str, apn_ni_str, lookup, &lookup->expiry_entry); - - expiring_item_init(&lookup->expiry_entry); - lookup->hub = hub; - - osmo_strlcpy(lookup->imsi_str, imsi_str, sizeof(lookup->imsi_str)); - osmo_strlcpy(lookup->apn_ni_str, apn_ni_str, - sizeof(lookup->apn_ni_str)); - - make_addr_str(lookup); - - struct ggsn_lookup *active; - llist_for_each_entry(active, &hub->ggsn_lookups, entry) { - if (strncmp(active->apn_oi_str, lookup->apn_oi_str, - sizeof(lookup->apn_oi_str)) == 0) { - LOGP(DGTPHUB, LOGL_DEBUG, - "Query already pending for %s\n", - lookup->apn_oi_str); - /* A query already pending. Just tip our hat. */ - return NULL; - } - } - - struct gtphub_resolved_ggsn *resolved; - llist_for_each_entry(resolved, &hub->resolved_ggsns, entry) { - if (strncmp(resolved->apn_oi_str, lookup->apn_oi_str, - sizeof(lookup->apn_oi_str)) == 0) { - LOGP(DGTPHUB, LOGL_DEBUG, - "GGSN resolved from cache: %s -> %s\n", - lookup->apn_oi_str, - gtphub_port_str(resolved->peer)); - return resolved->peer; - } - } - - /* Kick off a resolution, but so far return nothing. The hope is that - * the peer will resend the request (a couple of times), and by then - * the GGSN will be resolved. */ - LOGP(DGTPHUB, LOGL_DEBUG, - "Sending out DNS query for %s..." - " (Returning failure, hoping for a retry once resolution" - " has concluded)\n", - lookup->apn_oi_str); - - llist_add(&lookup->entry, &hub->ggsn_lookups); - - lookup->expiry_entry.del_cb = ggsn_lookup_del_cb; - expiry_add(&hub->expire_quickly, &lookup->expiry_entry, gtphub_now()); - - start_ares_query(lookup); - - return NULL; -} diff --git a/openbsc/src/gprs/gtphub_main.c b/openbsc/src/gprs/gtphub_main.c deleted file mode 100644 index 73a122c3..00000000 --- a/openbsc/src/gprs/gtphub_main.c +++ /dev/null @@ -1,357 +0,0 @@ -/* GTP Hub main program */ - -/* (C) 2015 by sysmocom s.f.m.c. GmbH - * All Rights Reserved - * - * Author: Neels Hofmeyr - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation; either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - */ - -#include -#include -#include -#include -#include -#include - -#define _GNU_SOURCE -#include - -#include -#include -#include -#include -#include - -#include -#include -#include - -#include -#include -#include - -#include "../../bscconfig.h" - -extern void *osmo_gtphub_ctx; - - -const char *gtphub_copyright = - "Copyright (C) 2015 sysmocom s.f.m.c GmbH \r\n" - "License AGPLv3+: GNU AGPL version 2 or later \r\n" - "This is free software: you are free to change and redistribute it.\r\n" - "There is NO WARRANTY, to the extent permitted by law.\r\n"; - -static struct log_info_cat gtphub_categories[] = { - [DGTPHUB] = { - .name = "DGTPHUB", - .description = "GTP Hub", - .color = "\033[1;33m", - .enabled = 1, - .loglevel = LOGL_INFO, - }, -}; - -int gtphub_log_filter_fn(const struct log_context *ctx, - struct log_target *tar) -{ - return 0; -} - -static const struct log_info gtphub_log_info = { - .filter_fn = gtphub_log_filter_fn, - .cat = gtphub_categories, - .num_cat = ARRAY_SIZE(gtphub_categories), -}; - -void log_cfg(struct gtphub_cfg *cfg) -{ - int side_idx, plane_idx; - for_each_side_and_plane(side_idx, plane_idx) { - struct gtphub_cfg_addr *a; - a = &cfg->to_gsns[side_idx][plane_idx].bind; - LOGP(DGTPHUB, LOGL_NOTICE, - "to-%ss bind, %s: %s port %d\n", - gtphub_side_idx_names[side_idx], - gtphub_plane_idx_names[plane_idx], - a->addr_str, a->port); - } -} - -static void signal_handler(int signal) -{ - fprintf(stdout, "signal %d received\n", signal); - - switch (signal) { - case SIGINT: - osmo_signal_dispatch(SS_L_GLOBAL, S_L_GLOBAL_SHUTDOWN, NULL); - sleep(1); - exit(0); - break; - case SIGABRT: - /* in case of abort, we want to obtain a talloc report - * and then return to the caller, who will abort the process */ - case SIGUSR1: - case SIGUSR2: - talloc_report_full(osmo_gtphub_ctx, stderr); - break; - default: - break; - } -} - -extern int bsc_vty_go_parent(struct vty *vty); - -static struct vty_app_info vty_info = { - .name = "OsmoGTPhub", - .version = PACKAGE_VERSION, - .go_parent_cb = bsc_vty_go_parent, - .is_config_node = bsc_vty_is_config_node, -}; - -struct cmdline_cfg { - const char *config_file; - const char *restart_counter_file; - int daemonize; -}; - -static uint8_t next_restart_count(const char *path) -{ - int umask_was = umask(022); - - uint8_t counter = 0; - - FILE *f = fopen(path, "r"); - if (f) { - int rc = fscanf(f, "%hhu", &counter); - - if (rc != 1) - goto failed_to_read; - - char c; - while (fread(&c, 1, 1, f) > 0) { - switch (c) { - case ' ': - case '\t': - case '\n': - case '\r': - break; - default: - goto failed_to_read; - } - } - fclose(f); - } - - counter ++; - - f = fopen(path, "w"); - if (!f) - goto failed_to_write; - if (fprintf(f, "%" PRIu8 "\n", counter) < 2) - goto failed_to_write; - if (fclose(f)) { - f = NULL; - goto failed_to_write; - } - - umask(umask_was); - - LOGP(DGTPHUB, LOGL_NOTICE, "Restarted with counter %hhu\n", counter); - return counter; - -failed_to_read: - fclose(f); - umask(umask_was); - LOGP(DGTPHUB, LOGL_FATAL, "Restart counter file cannot be parsed:" - " %s\n", path); - exit(1); - -failed_to_write: - if (f) - fclose(f); - umask(umask_was); - LOGP(DGTPHUB, LOGL_FATAL, "Restart counter file cannot be written:" - " %s\n", path); - exit(1); -} - -static void print_help(struct cmdline_cfg *ccfg) -{ - printf("gtphub commandline options\n"); - printf(" -h,--help This text.\n"); - printf(" -D,--daemonize Fork the process into a background daemon.\n"); - printf(" -d,--debug Enable Debugging for this category.\n"); - printf(" Pass '-d list' to get a category listing.\n"); - printf(" -s,--disable-color\n"); - printf(" -c,--config-file The config file to use [%s].\n", - ccfg->config_file); - printf(" -e,--log-level Set a global log level.\n"); - printf(" -r,--restart-file File for counting restarts [%s].\n", - ccfg->restart_counter_file); -} - -static void list_categories(void) -{ - printf("Avaliable debug categories:\n"); - int i; - for (i = 0; i < gtphub_log_info.num_cat; ++i) { - if (!gtphub_log_info.cat[i].name) - continue; - - printf("%s\n", gtphub_log_info.cat[i].name); - } -} - -static void handle_options(struct cmdline_cfg *ccfg, int argc, char **argv) -{ - while (1) { - int option_index = 0, c; - static struct option long_options[] = { - {"help", 0, 0, 'h'}, - {"debug", 1, 0, 'd'}, - {"daemonize", 0, 0, 'D'}, - {"config-file", 1, 0, 'c'}, - {"disable-color", 0, 0, 's'}, - {"timestamp", 0, 0, 'T'}, - {"log-level", 1, 0, 'e'}, - {"restart-file", 1, 0, 'r'}, - {NULL, 0, 0, 0} - }; - - c = getopt_long(argc, argv, "hd:Dc:sTe:r:", - long_options, &option_index); - if (c == -1) { - if (optind < argc) { - LOGP(DGTPHUB, LOGL_FATAL, - "Excess commandline arguments ('%s').\n", - argv[optind]); - exit(2); - } - break; - } - - switch (c) { - case 'h': - //print_usage(); - print_help(ccfg); - exit(0); - case 's': - log_set_use_color(osmo_stderr_target, 0); - break; - case 'd': - if (strcmp("list", optarg) == 0) { - list_categories(); - exit(0); - } else - log_parse_category_mask(osmo_stderr_target, optarg); - break; - case 'D': - ccfg->daemonize = 1; - break; - case 'c': - ccfg->config_file = optarg; - break; - case 'T': - log_set_print_timestamp(osmo_stderr_target, 1); - break; - case 'e': - log_set_log_level(osmo_stderr_target, atoi(optarg)); - break; - case 'r': - ccfg->restart_counter_file = optarg; - break; - default: - LOGP(DGTPHUB, LOGL_FATAL, "Invalid command line argument, abort.\n"); - exit(1); - break; - } - } -} - -int main(int argc, char **argv) -{ - int rc; - - struct cmdline_cfg _ccfg; - struct cmdline_cfg *ccfg = &_ccfg; - memset(ccfg, '\0', sizeof(*ccfg)); - ccfg->config_file = "./gtphub.conf"; - ccfg->restart_counter_file = "./gtphub_restart_count"; - - struct gtphub_cfg _cfg; - struct gtphub_cfg *cfg = &_cfg; - memset(cfg, '\0', sizeof(*cfg)); - - struct gtphub _hub; - struct gtphub *hub = &_hub; - - osmo_gtphub_ctx = talloc_named_const(NULL, 0, "osmo_gtphub"); - msgb_talloc_ctx_init(osmo_gtphub_ctx, 0); - - signal(SIGINT, &signal_handler); - signal(SIGABRT, &signal_handler); - signal(SIGUSR1, &signal_handler); - signal(SIGUSR2, &signal_handler); - osmo_init_ignore_signals(); - - osmo_init_logging(>phub_log_info); - - vty_info.copyright = gtphub_copyright; - vty_init(&vty_info); - logging_vty_add_cmds(NULL); - gtphub_vty_init(hub, cfg); - - rate_ctr_init(osmo_gtphub_ctx); - - handle_options(ccfg, argc, argv); - - rc = gtphub_cfg_read(cfg, ccfg->config_file); - if (rc < 0) { - LOGP(DGTPHUB, LOGL_FATAL, "Cannot parse config file '%s'\n", - ccfg->config_file); - exit(2); - } - - /* start telnet after reading config for vty_get_bind_addr() */ - rc = telnet_init_dynif(osmo_gtphub_ctx, 0, vty_get_bind_addr(), - OSMO_VTY_PORT_GTPHUB); - if (rc < 0) - exit(1); - - if (gtphub_start(hub, cfg, - next_restart_count(ccfg->restart_counter_file)) - != 0) - return -1; - - log_cfg(cfg); - - if (ccfg->daemonize) { - rc = osmo_daemonize(); - if (rc < 0) { - LOGP(DGTPHUB, LOGL_FATAL, "Error during daemonize"); - exit(1); - } - } - - while (1) { - rc = osmo_select_main(0); - if (rc < 0) - exit(3); - } - - /* not reached */ - exit(0); -} diff --git a/openbsc/src/gprs/gtphub_sock.c b/openbsc/src/gprs/gtphub_sock.c deleted file mode 100644 index 60bebaae..00000000 --- a/openbsc/src/gprs/gtphub_sock.c +++ /dev/null @@ -1,60 +0,0 @@ -/* GTP Hub Implementation */ - -/* (C) 2015 by sysmocom s.f.m.c. GmbH - * All Rights Reserved - * - * gtphub_sock.c. - * - * This file is kept separate so that these functions can be wrapped for - * gtphub_test.c. When a function and its callers are in the same compilational - * unit, the wrappability may be optimized away. - * - * Author: Neels Hofmeyr - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation; either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - */ - -#include -#include - -/* Convenience makro, note: only within this C file. */ -#define LOG(level, fmt, args...) \ - LOGP(DGTPHUB, level, fmt, ##args) - -int gtphub_write(const struct osmo_fd *to, - const struct osmo_sockaddr *to_addr, - const uint8_t *buf, size_t buf_len) -{ - errno = 0; - ssize_t sent = sendto(to->fd, buf, buf_len, 0, - (struct sockaddr*)&to_addr->a, to_addr->l); - LOG(LOGL_DEBUG, "to %s\n", osmo_sockaddr_to_str(to_addr)); - - if (sent == -1) { - LOG(LOGL_ERROR, "error: %s\n", strerror(errno)); - return -EINVAL; - } - - if (sent != buf_len) - LOG(LOGL_ERROR, "sent(%d) != data_len(%d)\n", - (int)sent, (int)buf_len); - else - LOG(LOGL_DEBUG, "Sent %d: %s%s\n", - (int)sent, - osmo_hexdump(buf, sent > 1000? 1000 : sent), - sent > 1000 ? "..." : ""); - - return 0; -} - diff --git a/openbsc/src/gprs/gtphub_vty.c b/openbsc/src/gprs/gtphub_vty.c deleted file mode 100644 index a30ad2a5..00000000 --- a/openbsc/src/gprs/gtphub_vty.c +++ /dev/null @@ -1,613 +0,0 @@ -/* (C) 2015 by sysmocom s.f.m.c. GmbH - * All Rights Reserved - * - * Author: Neels Hofmeyr - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation; either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - * - */ - -#include -#include - -#include -#include -#include -#include - -#include -#include -#include - -#include -#include - -/* TODO split GRX ares from sgsn into a separate struct and allow use without - * globals. */ -#include -extern struct sgsn_instance *sgsn; - -static struct gtphub *g_hub = 0; -static struct gtphub_cfg *g_cfg = 0; - -static struct cmd_node gtphub_node = { - GTPHUB_NODE, - "%s(config-gtphub)# ", - 1, -}; - -#define GTPH_DEFAULT_CONTROL_PORT 2123 -#define GTPH_DEFAULT_USER_PORT 2152 - -static void write_addrs(struct vty *vty, const char *name, - struct gtphub_cfg_addr *c, struct gtphub_cfg_addr *u) -{ - if ((c->port == GTPH_DEFAULT_CONTROL_PORT) - && (u->port == GTPH_DEFAULT_USER_PORT) - && (strcmp(c->addr_str, u->addr_str) == 0)) { - /* Default port numbers and same IP address: write "short" - * variant. */ - vty_out(vty, " %s %s%s", - name, - c->addr_str, - VTY_NEWLINE); - return; - } - - vty_out(vty, " %s ctrl %s %d user %s %d%s", - name, - c->addr_str, (int)c->port, - u->addr_str, (int)u->port, - VTY_NEWLINE); - - struct ares_addr_node *server; - for (server = sgsn->ares_servers; server; server = server->next) - vty_out(vty, " grx-dns-add %s%s", inet_ntoa(server->addr.addr4), VTY_NEWLINE); -} - -static int config_write_gtphub(struct vty *vty) -{ - vty_out(vty, "gtphub%s", VTY_NEWLINE); - - write_addrs(vty, "bind-to-sgsns", - &g_cfg->to_gsns[GTPH_SIDE_SGSN][GTPH_PLANE_CTRL].bind, - &g_cfg->to_gsns[GTPH_SIDE_SGSN][GTPH_PLANE_USER].bind); - - write_addrs(vty, "bind-to-ggsns", - &g_cfg->to_gsns[GTPH_SIDE_GGSN][GTPH_PLANE_CTRL].bind, - &g_cfg->to_gsns[GTPH_SIDE_GGSN][GTPH_PLANE_USER].bind); - - if (g_cfg->sgsn_use_sender) { - vty_out(vty, "sgsn-use-sender%s", VTY_NEWLINE); - } - - if (g_cfg->proxy[GTPH_SIDE_SGSN][GTPH_PLANE_CTRL].addr_str) { - write_addrs(vty, "sgsn-proxy", - &g_cfg->proxy[GTPH_SIDE_SGSN][GTPH_PLANE_CTRL], - &g_cfg->proxy[GTPH_SIDE_SGSN][GTPH_PLANE_USER]); - } - - if (g_cfg->proxy[GTPH_SIDE_GGSN][GTPH_PLANE_CTRL].addr_str) { - write_addrs(vty, "ggsn-proxy", - &g_cfg->proxy[GTPH_SIDE_GGSN][GTPH_PLANE_CTRL], - &g_cfg->proxy[GTPH_SIDE_GGSN][GTPH_PLANE_USER]); - } - - return CMD_SUCCESS; -} - -DEFUN(cfg_gtphub, cfg_gtphub_cmd, - "gtphub", - "Configure the GTP hub\n") -{ - vty->node = GTPHUB_NODE; - return CMD_SUCCESS; -} - -#define BIND_ARGS "ctrl ADDR <0-65535> user ADDR <0-65535>" -#define BIND_DOCS \ - "Set GTP-C bind\n" \ - "GTP-C local IP address (v4 or v6)\n" \ - "GTP-C local port\n" \ - "Set GTP-U bind\n" \ - "GTP-U local IP address (v4 or v6)\n" \ - "GTP-U local port\n" - - -DEFUN(cfg_gtphub_bind_to_sgsns_short, cfg_gtphub_bind_to_sgsns_short_cmd, - "bind-to-sgsns ADDR", - "GTP Hub Parameters\n" - "Set the local bind address to listen for SGSNs, for both GTP-C and GTP-U\n" - "Local IP address (v4 or v6)\n" - ) -{ - int i; - for_each_plane(i) - g_cfg->to_gsns[GTPH_SIDE_SGSN][i].bind.addr_str = talloc_strdup(tall_vty_ctx, argv[0]); - g_cfg->to_gsns[GTPH_SIDE_SGSN][GTPH_PLANE_CTRL].bind.port = GTPH_DEFAULT_CONTROL_PORT; - g_cfg->to_gsns[GTPH_SIDE_SGSN][GTPH_PLANE_USER].bind.port = GTPH_DEFAULT_USER_PORT; - return CMD_SUCCESS; -} - -DEFUN(cfg_gtphub_bind_to_ggsns_short, cfg_gtphub_bind_to_ggsns_short_cmd, - "bind-to-ggsns ADDR", - "GTP Hub Parameters\n" - "Set the local bind address to listen for GGSNs, for both GTP-C and GTP-U\n" - "Local IP address (v4 or v6)\n" - ) -{ - int i; - for_each_plane(i) - g_cfg->to_gsns[GTPH_SIDE_GGSN][i].bind.addr_str = talloc_strdup(tall_vty_ctx, argv[0]); - g_cfg->to_gsns[GTPH_SIDE_GGSN][GTPH_PLANE_CTRL].bind.port = GTPH_DEFAULT_CONTROL_PORT; - g_cfg->to_gsns[GTPH_SIDE_GGSN][GTPH_PLANE_USER].bind.port = GTPH_DEFAULT_USER_PORT; - return CMD_SUCCESS; -} - - -static int handle_binds(struct gtphub_cfg_bind *b, const char **argv) -{ - b[GTPH_PLANE_CTRL].bind.addr_str = talloc_strdup(tall_vty_ctx, argv[0]); - b[GTPH_PLANE_CTRL].bind.port = atoi(argv[1]); - b[GTPH_PLANE_USER].bind.addr_str = talloc_strdup(tall_vty_ctx, argv[2]); - b[GTPH_PLANE_USER].bind.port = atoi(argv[3]); - return CMD_SUCCESS; -} - -DEFUN(cfg_gtphub_bind_to_sgsns, cfg_gtphub_bind_to_sgsns_cmd, - "bind-to-sgsns " BIND_ARGS, - "GTP Hub Parameters\n" - "Set the local bind addresses and ports to listen for SGSNs\n" - BIND_DOCS - ) -{ - return handle_binds(g_cfg->to_gsns[GTPH_SIDE_SGSN], argv); -} - -DEFUN(cfg_gtphub_bind_to_ggsns, cfg_gtphub_bind_to_ggsns_cmd, - "bind-to-ggsns " BIND_ARGS, - "GTP Hub Parameters\n" - "Set the local bind addresses and ports to listen for GGSNs\n" - BIND_DOCS - ) -{ - return handle_binds(g_cfg->to_gsns[GTPH_SIDE_GGSN], argv); -} - -DEFUN(cfg_gtphub_ggsn_proxy_short, cfg_gtphub_ggsn_proxy_short_cmd, - "ggsn-proxy ADDR", - "GTP Hub Parameters\n" - "Redirect all GGSN bound traffic to default ports on this address (another gtphub)\n" - "Remote IP address (v4 or v6)\n" - ) -{ - g_cfg->proxy[GTPH_SIDE_GGSN][GTPH_PLANE_CTRL].addr_str = talloc_strdup(tall_vty_ctx, argv[0]); - g_cfg->proxy[GTPH_SIDE_GGSN][GTPH_PLANE_CTRL].port = GTPH_DEFAULT_CONTROL_PORT; - g_cfg->proxy[GTPH_SIDE_GGSN][GTPH_PLANE_USER].addr_str = talloc_strdup(tall_vty_ctx, argv[0]); - g_cfg->proxy[GTPH_SIDE_GGSN][GTPH_PLANE_USER].port = GTPH_DEFAULT_USER_PORT; - return CMD_SUCCESS; -} - -DEFUN(cfg_gtphub_ggsn_proxy, cfg_gtphub_ggsn_proxy_cmd, - "ggsn-proxy " BIND_ARGS, - "GTP Hub Parameters\n" - "Redirect all GGSN bound traffic to these addresses and ports (another gtphub)\n" - BIND_DOCS - ) -{ - g_cfg->proxy[GTPH_SIDE_GGSN][GTPH_PLANE_CTRL].addr_str = talloc_strdup(tall_vty_ctx, argv[0]); - g_cfg->proxy[GTPH_SIDE_GGSN][GTPH_PLANE_CTRL].port = atoi(argv[1]); - g_cfg->proxy[GTPH_SIDE_GGSN][GTPH_PLANE_USER].addr_str = talloc_strdup(tall_vty_ctx, argv[2]); - g_cfg->proxy[GTPH_SIDE_GGSN][GTPH_PLANE_USER].port = atoi(argv[3]); - return CMD_SUCCESS; -} - -DEFUN(cfg_gtphub_sgsn_proxy_short, cfg_gtphub_sgsn_proxy_short_cmd, - "sgsn-proxy ADDR", - "GTP Hub Parameters\n" - "Redirect all SGSN bound traffic to default ports on this address (another gtphub)\n" - "Remote IP address (v4 or v6)\n" - ) -{ - g_cfg->proxy[GTPH_SIDE_SGSN][GTPH_PLANE_CTRL].addr_str = talloc_strdup(tall_vty_ctx, argv[0]); - g_cfg->proxy[GTPH_SIDE_SGSN][GTPH_PLANE_CTRL].port = GTPH_DEFAULT_CONTROL_PORT; - g_cfg->proxy[GTPH_SIDE_SGSN][GTPH_PLANE_USER].addr_str = talloc_strdup(tall_vty_ctx, argv[0]); - g_cfg->proxy[GTPH_SIDE_SGSN][GTPH_PLANE_USER].port = GTPH_DEFAULT_USER_PORT; - return CMD_SUCCESS; -} - -DEFUN(cfg_gtphub_sgsn_proxy, cfg_gtphub_sgsn_proxy_cmd, - "sgsn-proxy " BIND_ARGS, - "GTP Hub Parameters\n" - "Redirect all SGSN bound traffic to these addresses and ports (another gtphub)\n" - BIND_DOCS - ) -{ - g_cfg->proxy[GTPH_SIDE_SGSN][GTPH_PLANE_CTRL].addr_str = talloc_strdup(tall_vty_ctx, argv[0]); - g_cfg->proxy[GTPH_SIDE_SGSN][GTPH_PLANE_CTRL].port = atoi(argv[1]); - g_cfg->proxy[GTPH_SIDE_SGSN][GTPH_PLANE_USER].addr_str = talloc_strdup(tall_vty_ctx, argv[2]); - g_cfg->proxy[GTPH_SIDE_SGSN][GTPH_PLANE_USER].port = atoi(argv[3]); - return CMD_SUCCESS; -} - - -#define SGSN_USE_SENDER_STR \ - "Ignore SGSN's Address IEs, use sender address and port (useful over NAT)\n" - -DEFUN(cfg_gtphub_sgsn_use_sender, - cfg_gtphub_sgsn_use_sender_cmd, - "sgsn-use-sender", - SGSN_USE_SENDER_STR) -{ - g_cfg->sgsn_use_sender = 1; - return CMD_SUCCESS; -} - -DEFUN(cfg_gtphub_no_sgsn_use_sender, - cfg_gtphub_no_sgsn_use_sender_cmd, - "no sgsn-use-sender", - NO_STR SGSN_USE_SENDER_STR) -{ - g_cfg->sgsn_use_sender = 0; - return CMD_SUCCESS; -} - - -/* Copied from sgsn_vty.h */ -DEFUN(cfg_grx_ggsn, cfg_grx_ggsn_cmd, - "grx-dns-add A.B.C.D", - "Add DNS server\nIPv4 address\n") -{ - struct ares_addr_node *node = talloc_zero(tall_bsc_ctx, struct ares_addr_node); - node->family = AF_INET; - inet_aton(argv[0], &node->addr.addr4); - - node->next = sgsn->ares_servers; - sgsn->ares_servers = node; - return CMD_SUCCESS; -} - - -static void show_bind_stats_all(struct vty *vty) -{ - int plane_idx; - for_each_plane(plane_idx) { - vty_out(vty, "- %s Plane:%s", - gtphub_plane_idx_names[plane_idx], VTY_NEWLINE); - - int side_idx; - for_each_side(side_idx) { - struct gtphub_bind *b = &g_hub->to_gsns[side_idx][plane_idx]; - vty_out(vty, " - local addr to/from %ss: %s port %d%s", - gtphub_side_idx_names[side_idx], - gsn_addr_to_str(&b->local_addr), (int)b->local_port, - VTY_NEWLINE); - vty_out_rate_ctr_group(vty, " ", b->counters_io); - } - } -} - -static void show_tunnel_stats(struct vty *vty, struct gtphub_tunnel *tun) -{ - int plane_idx; - for_each_plane(plane_idx) { - vty_out(vty, "- %s Plane:%s", - gtphub_plane_idx_names[plane_idx], VTY_NEWLINE); - - int side_idx; - for_each_side(side_idx) { - struct gtphub_tunnel_endpoint *te = &tun->endpoint[side_idx][plane_idx]; - vty_out(vty, " - to/from %s:%s", - gtphub_side_idx_names[side_idx], - VTY_NEWLINE); - vty_out_rate_ctr_group(vty, " ", te->counters_io); - } - } -} - -static void show_peer_summary(struct vty *vty, const char *prefix, - int side_idx, int plane_idx, - struct gtphub_peer *p, int with_io_stats) -{ - struct gtphub_peer_addr *pa; - int p2l = strlen(prefix) + 4 + 1; - char prefix2[p2l]; - memset(prefix2, ' ', p2l - 1); - prefix2[p2l - 1] = '\0'; - - if (with_io_stats) { - llist_for_each_entry(pa, &p->addresses, entry) { - vty_out(vty, "%s- %s %s %s%s", prefix, - gtphub_side_idx_names[side_idx], - gtphub_plane_idx_names[plane_idx], - gsn_addr_to_str(&pa->addr), - VTY_NEWLINE); - - - struct gtphub_peer_port *pp; - llist_for_each_entry(pp, &pa->ports, entry) { - vty_out(vty, "%s Port %" PRIu16 "%s", prefix, pp->port, VTY_NEWLINE); - vty_out_rate_ctr_group(vty, prefix2, pp->counters_io); - } - } - } else { - llist_for_each_entry(pa, &p->addresses, entry) { - vty_out(vty, "%s- %s %s %s", prefix, - gtphub_side_idx_names[side_idx], - gtphub_plane_idx_names[plane_idx], - gsn_addr_to_str(&pa->addr)); - struct gtphub_peer_port *pp; - llist_for_each_entry(pp, &pa->ports, entry) { - vty_out(vty, ":%" PRIu16, pp->port); - } - vty_out(vty, VTY_NEWLINE); - } - } -} - -static void show_peers_summary(struct vty *vty) -{ - int side_idx; - int plane_idx; - - int count[GTPH_SIDE_N][GTPH_PLANE_N] = {{0}}; - - for_each_side(side_idx) { - for_each_plane(plane_idx) { - struct gtphub_peer *p; - llist_for_each_entry(p, &g_hub->to_gsns[side_idx][plane_idx].peers, entry) { - count[side_idx][plane_idx] ++; - } - } - } - - vty_out(vty, "Peers Count:%s", VTY_NEWLINE); - for_each_side_and_plane(side_idx, plane_idx) { - vty_out(vty, " %s %s peers: %d%s", - gtphub_side_idx_names[side_idx], - gtphub_plane_idx_names[plane_idx], - count[side_idx][plane_idx], - VTY_NEWLINE); - } -} - -static void show_peers_all(struct vty *vty, int with_io_stats) -{ - int side_idx; - int plane_idx; - - int count[GTPH_SIDE_N][GTPH_PLANE_N] = {{0}}; - - vty_out(vty, "All Peers%s%s", - with_io_stats? " with I/O stats" : "", - VTY_NEWLINE); - for_each_side(side_idx) { - vty_out(vty, "- %s%s", gtphub_side_idx_names[side_idx], VTY_NEWLINE); - for_each_plane(plane_idx) { - struct gtphub_peer *p; - llist_for_each_entry(p, &g_hub->to_gsns[side_idx][plane_idx].peers, entry) { - count[side_idx][plane_idx] ++; - show_peer_summary(vty, " ", side_idx, plane_idx, p, with_io_stats); - } - } - } - for_each_side_and_plane(side_idx, plane_idx) { - vty_out(vty, "%s %s peers: %d%s", - gtphub_side_idx_names[side_idx], - gtphub_plane_idx_names[plane_idx], - count[side_idx][plane_idx], - VTY_NEWLINE); - } -} - - -static void show_tunnels_summary(struct vty *vty) -{ - time_t now = gtphub_now(); - - const int w = 36; - int max_expiry = g_hub->expire_slowly.expiry_in_seconds; - float seconds_per_step = ((float)max_expiry) / w; - - /* Print TEI mapping expiry in an ASCII histogram, like: - TEI map summary - Legend: '_'=0 '.'<=1% ':'<=2% '|'<=10% '#'>10% (10.0 m/step) - CTRL: 30 mappings, valid for 360m[# :. | . : . ]1m - USER: 30 mappings, valid for 360m[# :. | . : . ]1m - 4 TEI mappings in total, last expiry in 359.4 min - */ - vty_out(vty, - "Tunnels summary%s" - " Legend: ' '=0 '.'<=1%% ':'<=2%% '|'<=10%% '#'>10%% (%.1f m/step)%s", - VTY_NEWLINE, - seconds_per_step / 60., - VTY_NEWLINE); - - int last_expiry = 0; - - unsigned int count = 0; - - int histogram[w]; - memset(histogram, 0, sizeof(histogram)); - - struct gtphub_tunnel *t; - llist_for_each_entry(t, &g_hub->tunnels, entry) { - count ++; - int expiry = t->expiry_entry.expiry - now; - last_expiry = (last_expiry > expiry) ? last_expiry : expiry; - - int hi = ((float)expiry) / seconds_per_step; - if (hi < 0) - hi = 0; - if (hi > (w - 1)) - hi = w - 1; - histogram[hi] ++; - } - - vty_out(vty, - " %u tunnels, valid for %dm[", - count, max_expiry / 60); - - int i; - for (i = w - 1; i >= 0; i--) { - char c; - int val = histogram[i]; - int percent = 100. * val / count; - if (!val) - c = ' '; - else if (percent <= 1) - c = '.'; - else if (percent <= 2) - c = ':'; - else if (percent <= 10) - c = '|'; - else c = '#'; - vty_out(vty, "%c", c); - } - vty_out(vty, "]1m%s", VTY_NEWLINE); - - vty_out(vty, " last expiry in %.1f min%s", - ((float)last_expiry) / 60., - VTY_NEWLINE); -} - -static void show_tunnels_all(struct vty *vty, int with_io_stats) -{ - time_t now = gtphub_now(); - - vty_out(vty, "All tunnels%s:%s" - "Legend: TEI=: SGSN <-> GGSN (expiry in minutes), with each:%s" - " [/] (TEI C= U=)%s", - with_io_stats? "with I/O stats" : "", - VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE); - - unsigned int count = 0; - unsigned int incomplete = 0; - struct gtphub_tunnel *tun; - llist_for_each_entry(tun, &g_hub->tunnels, entry) { - vty_out(vty, - "%s (expiry in %dm)%s", - gtphub_tunnel_str(tun), - (int)((tun->expiry_entry.expiry - now) / 60), - VTY_NEWLINE); - count ++; - if (!gtphub_tunnel_complete(tun)) - incomplete ++; - if (with_io_stats) - show_tunnel_stats(vty, tun); - } - vty_out(vty, "Total: %u tunnels (of which %u incomplete)%s", - count, incomplete, VTY_NEWLINE); -} - -#define SHOW_GTPHUB_STRS SHOW_STR "Show info on running GTP hub\n" -#define SHOW_GTPHUB_PEERS_STRS SHOW_GTPHUB_STRS "Active peers\n" -#define SHOW_GTPHUB_TUNS_STRS SHOW_GTPHUB_STRS "Active tunnels\n" - -DEFUN(show_gtphub_peers_summary, show_gtphub_peers_summary_cmd, "show gtphub peers summary", - SHOW_GTPHUB_PEERS_STRS "Summary of all peers\n") -{ - show_peers_summary(vty); - return CMD_SUCCESS; -} - -DEFUN(show_gtphub_peers_list, show_gtphub_peers_list_cmd, "show gtphub peers list", - SHOW_GTPHUB_PEERS_STRS "List all peers\n") -{ - show_peers_all(vty, 0); - return CMD_SUCCESS; -} - -DEFUN(show_gtphub_peers_stats, show_gtphub_peers_stats_cmd, "show gtphub peers stats", - SHOW_GTPHUB_PEERS_STRS "List all peers with I/O stats\n") -{ - show_peers_all(vty, 1); - return CMD_SUCCESS; -} - -DEFUN(show_gtphub_tunnels_summary, show_gtphub_tunnels_summary_cmd, "show gtphub tunnels summary", - SHOW_GTPHUB_TUNS_STRS "Summary of all tunnels\n") -{ - show_tunnels_summary(vty); - return CMD_SUCCESS; -} - -DEFUN(show_gtphub_tunnels_list, show_gtphub_tunnels_list_cmd, "show gtphub tunnels list", - SHOW_GTPHUB_TUNS_STRS "List all tunnels\n") -{ - show_tunnels_all(vty, 0); - return CMD_SUCCESS; -} - -DEFUN(show_gtphub_tunnels_stats, show_gtphub_tunnels_stats_cmd, "show gtphub tunnels stats", - SHOW_GTPHUB_TUNS_STRS "List all tunnels with I/O stats\n") -{ - show_tunnels_all(vty, 1); - return CMD_SUCCESS; -} - -DEFUN(show_gtphub, show_gtphub_cmd, "show gtphub all", - SHOW_GTPHUB_STRS "Summarize everything about the GTP hub\n") -{ - show_bind_stats_all(vty); - show_peers_summary(vty); - show_tunnels_summary(vty); - return CMD_SUCCESS; -} - - -int gtphub_vty_init(struct gtphub *global_hub, struct gtphub_cfg *global_cfg) -{ - g_hub = global_hub; - g_cfg = global_cfg; - - install_element_ve(&show_gtphub_cmd); - install_element_ve(&show_gtphub_peers_summary_cmd); - install_element_ve(&show_gtphub_peers_list_cmd); - install_element_ve(&show_gtphub_peers_stats_cmd); - install_element_ve(&show_gtphub_tunnels_summary_cmd); - install_element_ve(&show_gtphub_tunnels_list_cmd); - install_element_ve(&show_gtphub_tunnels_stats_cmd); - - install_element(CONFIG_NODE, &cfg_gtphub_cmd); - install_node(>phub_node, config_write_gtphub); - vty_install_default(GTPHUB_NODE); - - install_element(GTPHUB_NODE, &cfg_gtphub_bind_to_sgsns_short_cmd); - install_element(GTPHUB_NODE, &cfg_gtphub_bind_to_sgsns_cmd); - install_element(GTPHUB_NODE, &cfg_gtphub_bind_to_ggsns_short_cmd); - install_element(GTPHUB_NODE, &cfg_gtphub_bind_to_ggsns_cmd); - install_element(GTPHUB_NODE, &cfg_gtphub_ggsn_proxy_short_cmd); - install_element(GTPHUB_NODE, &cfg_gtphub_ggsn_proxy_cmd); - install_element(GTPHUB_NODE, &cfg_gtphub_sgsn_proxy_short_cmd); - install_element(GTPHUB_NODE, &cfg_gtphub_sgsn_proxy_cmd); - install_element(GTPHUB_NODE, &cfg_gtphub_sgsn_use_sender_cmd); - install_element(GTPHUB_NODE, &cfg_gtphub_no_sgsn_use_sender_cmd); - install_element(GTPHUB_NODE, &cfg_grx_ggsn_cmd); - - return 0; -} - -int gtphub_cfg_read(struct gtphub_cfg *cfg, const char *config_file) -{ - int rc; - - rc = vty_read_config_file(config_file, NULL); - if (rc < 0) { - fprintf(stderr, "Failed to parse the config file: '%s'\n", config_file); - return rc; - } - - return 0; -} diff --git a/openbsc/src/gprs/osmo_sgsn.cfg b/openbsc/src/gprs/osmo_sgsn.cfg deleted file mode 100644 index c4c9ec1c..00000000 --- a/openbsc/src/gprs/osmo_sgsn.cfg +++ /dev/null @@ -1,23 +0,0 @@ -! -! Osmocom SGSN (0.9.0.474-0ede2) configuration saved from vty -!! -! -line vty - no login -! -sgsn - gtp local-ip 192.168.100.11 - ggsn 0 remote-ip 192.168.100.239 - ggsn 0 gtp-version 1 -ns - timer tns-block 3 - timer tns-block-retries 3 - timer tns-reset 3 - timer tns-reset-retries 3 - timer tns-test 30 - timer tns-alive 3 - timer tns-alive-retries 10 - encapsulation udp local-ip 192.168.100.11 - encapsulation udp local-port 23000 - encapsulation framerelay-gre enabled 0 -bssgp diff --git a/openbsc/src/gprs/sgsn_ares.c b/openbsc/src/gprs/sgsn_ares.c deleted file mode 100644 index d94d184a..00000000 --- a/openbsc/src/gprs/sgsn_ares.c +++ /dev/null @@ -1,173 +0,0 @@ -/* C-ARES DNS resolver integration */ - -/* - * (C) 2015 by Holger Hans Peter Freyther - * All Rights Reserved - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation; either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - * - */ - -#include -#include - -#include - -struct cares_event_fd { - struct llist_head head; - struct osmo_fd fd; -}; - -struct cares_cb_data { - ares_host_callback cb; - void *data; -}; - -static void osmo_ares_reschedule(struct sgsn_instance *sgsn); -static void ares_cb(void *_arg, int status, int timeouts, struct hostent *hostent) -{ - struct cares_cb_data *arg = _arg; - - arg->cb(arg->data, status, timeouts, hostent); - osmo_ares_reschedule(sgsn); - talloc_free(arg); -} - -static int ares_osmo_fd_cb(struct osmo_fd *fd, unsigned int what) -{ - LOGP(DGPRS, LOGL_DEBUG, "C-ares fd(%d) ready(%d)\n", fd->fd, what); - - ares_process_fd(sgsn->ares_channel, - (what & BSC_FD_READ) ? fd->fd : ARES_SOCKET_BAD, - (what & BSC_FD_WRITE) ? fd->fd : ARES_SOCKET_BAD); - osmo_ares_reschedule(sgsn); - return 0; -} - -static void ares_timeout_cb(void *data) -{ - struct sgsn_instance *sgsn = data; - - LOGP(DGPRS, LOGL_DEBUG, "C-ares triggering timeout\n"); - ares_process_fd(sgsn->ares_channel, ARES_SOCKET_BAD, ARES_SOCKET_BAD); - osmo_ares_reschedule(sgsn); -} - -static void osmo_ares_reschedule(struct sgsn_instance *sgsn) -{ - struct timeval *timeout, tv; - - osmo_timer_del(&sgsn->ares_timer); - timeout = ares_timeout(sgsn->ares_channel, NULL, &tv); - if (timeout) { - LOGP(DGPRS, LOGL_DEBUG, "C-ares scheduling timeout %llu.%llu\n", - (unsigned long long) tv.tv_sec, - (unsigned long long) tv.tv_usec); - osmo_timer_setup(&sgsn->ares_timer, ares_timeout_cb, sgsn); - osmo_timer_schedule(&sgsn->ares_timer, tv.tv_sec, tv.tv_usec); - } -} - -static void setup_ares_osmo_fd(void *data, int fd, int read, int write) -{ - struct cares_event_fd *ufd, *tmp; - - /* delete the entry */ - if (read == 0 && write == 0) { - llist_for_each_entry_safe(ufd, tmp, &sgsn->ares_fds, head) { - if (ufd->fd.fd != fd) - continue; - - LOGP(DGPRS, LOGL_DEBUG, - "Removing C-ares watched fd (%d)\n", fd); - osmo_fd_unregister(&ufd->fd); - llist_del(&ufd->head); - talloc_free(ufd); - return; - } - } - - /* Search for the fd or create a new one */ - llist_for_each_entry(ufd, &sgsn->ares_fds, head) { - if (ufd->fd.fd != fd) - continue; - - LOGP(DGPRS, LOGL_DEBUG, "Updating C-ares fd (%d)\n", fd); - goto update_fd; - } - - LOGP(DGPRS, LOGL_DEBUG, "Registering C-ares fd (%d)\n", fd); - ufd = talloc_zero(tall_bsc_ctx, struct cares_event_fd); - ufd->fd.fd = fd; - ufd->fd.cb = ares_osmo_fd_cb; - ufd->fd.data = data; - if (osmo_fd_register(&ufd->fd) != 0) - LOGP(DGPRS, LOGL_ERROR, "Failed to register C-ares fd (%d)\n", fd); - llist_add(&ufd->head, &sgsn->ares_fds); - -update_fd: - if (read) - ufd->fd.when |= BSC_FD_READ; - else - ufd->fd.when &= ~BSC_FD_READ; - - if (write) - ufd->fd.when |= BSC_FD_WRITE; - else - ufd->fd.when &= ~BSC_FD_WRITE; - - osmo_ares_reschedule(sgsn); -} - -int sgsn_ares_query(struct sgsn_instance *sgsn, const char *name, - ares_host_callback cb, void *data) -{ - struct cares_cb_data *cb_data; - - cb_data = talloc_zero(tall_bsc_ctx, struct cares_cb_data); - cb_data->cb = cb; - cb_data->data = data; - ares_gethostbyname(sgsn->ares_channel, name, AF_INET, ares_cb, cb_data); - osmo_ares_reschedule(sgsn); - return 0; -} - -int sgsn_ares_init(struct sgsn_instance *sgsn) -{ - struct ares_options options; - int optmask; - int rc; - - INIT_LLIST_HEAD(&sgsn->ares_fds); - memset(&options, 0, sizeof(options)); - options.sock_state_cb = setup_ares_osmo_fd; - options.sock_state_cb_data = sgsn; - - optmask = ARES_OPT_FLAGS | ARES_OPT_SOCK_STATE_CB | ARES_OPT_DOMAINS; - - if (sgsn->ares_servers) - optmask |= ARES_OPT_SERVERS; - - ares_library_init(ARES_LIB_INIT_ALL); - rc = ares_init_options(&sgsn->ares_channel, &options, optmask); - if (rc != ARES_SUCCESS) - return rc; - - if (sgsn->ares_servers) - rc = ares_set_servers(sgsn->ares_channel, sgsn->ares_servers); - - return rc; -} - -osmo_static_assert(ARES_SUCCESS == 0, ares_success_zero); diff --git a/openbsc/src/gprs/sgsn_auth.c b/openbsc/src/gprs/sgsn_auth.c deleted file mode 100644 index a64339c3..00000000 --- a/openbsc/src/gprs/sgsn_auth.c +++ /dev/null @@ -1,312 +0,0 @@ -/* MS authorization and subscriber data handling */ - -/* (C) 2009-2010 by Harald Welte - * - * All Rights Reserved - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation; either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public 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 - -const struct value_string auth_state_names[] = { - { SGSN_AUTH_ACCEPTED, "accepted"}, - { SGSN_AUTH_REJECTED, "rejected"}, - { SGSN_AUTH_UNKNOWN, "unknown"}, - { SGSN_AUTH_AUTHENTICATE, "authenticate" }, - { SGSN_AUTH_UMTS_RESYNC, "UMTS-resync" }, - { 0, NULL } -}; - -const struct value_string *sgsn_auth_state_names = auth_state_names; - -void sgsn_auth_init(void) -{ - INIT_LLIST_HEAD(&sgsn->cfg.imsi_acl); -} - -/* temporary IMSI ACL hack */ -struct imsi_acl_entry *sgsn_acl_lookup(const char *imsi, struct sgsn_config *cfg) -{ - struct imsi_acl_entry *acl; - llist_for_each_entry(acl, &cfg->imsi_acl, list) { - if (!strcmp(imsi, acl->imsi)) - return acl; - } - return NULL; -} - -int sgsn_acl_add(const char *imsi, struct sgsn_config *cfg) -{ - struct imsi_acl_entry *acl; - - if (sgsn_acl_lookup(imsi, cfg)) - return -EEXIST; - - acl = talloc_zero(NULL, struct imsi_acl_entry); - if (!acl) - return -ENOMEM; - osmo_strlcpy(acl->imsi, imsi, sizeof(acl->imsi)); - - llist_add(&acl->list, &cfg->imsi_acl); - - return 0; -} - -int sgsn_acl_del(const char *imsi, struct sgsn_config *cfg) -{ - struct imsi_acl_entry *acl; - - acl = sgsn_acl_lookup(imsi, cfg); - if (!acl) - return -ENODEV; - - llist_del(&acl->list); - talloc_free(acl); - - return 0; -} - -enum sgsn_auth_state sgsn_auth_state(struct sgsn_mm_ctx *mmctx) -{ - char mccmnc[16]; - int check_net = 0; - int check_acl = 0; - - OSMO_ASSERT(mmctx); - - switch (sgsn->cfg.auth_policy) { - case SGSN_AUTH_POLICY_OPEN: - return SGSN_AUTH_ACCEPTED; - - case SGSN_AUTH_POLICY_CLOSED: - check_net = 1; - check_acl = 1; - break; - - case SGSN_AUTH_POLICY_ACL_ONLY: - check_acl = 1; - break; - - case SGSN_AUTH_POLICY_REMOTE: - if (!mmctx->subscr) - return mmctx->auth_state; - - if (mmctx->subscr->flags & GPRS_SUBSCRIBER_UPDATE_PENDING_MASK) - return mmctx->auth_state; - - if (sgsn->cfg.require_authentication && - (!mmctx->is_authenticated || - mmctx->subscr->sgsn_data->auth_triplets_updated)) - return SGSN_AUTH_AUTHENTICATE; - - if (mmctx->subscr->authorized) - return SGSN_AUTH_ACCEPTED; - - return SGSN_AUTH_REJECTED; - } - - if (!strlen(mmctx->imsi)) { - LOGMMCTXP(LOGL_NOTICE, mmctx, - "Missing IMSI, authorization state not known\n"); - return SGSN_AUTH_UNKNOWN; - } - - if (check_net) { - /* We simply assume that the IMSI exists, as long as it is part - * of 'our' network */ - snprintf(mccmnc, sizeof(mccmnc), "%03d%02d", - mmctx->ra.mcc, mmctx->ra.mnc); - if (strncmp(mccmnc, mmctx->imsi, 5) == 0) - return SGSN_AUTH_ACCEPTED; - } - - if (check_acl && sgsn_acl_lookup(mmctx->imsi, &sgsn->cfg)) - return SGSN_AUTH_ACCEPTED; - - return SGSN_AUTH_REJECTED; -} - -/* - * This function is directly called by e.g. the GMM layer. It returns either - * after calling sgsn_auth_update directly or after triggering an asynchronous - * procedure which will call sgsn_auth_update later on. - */ -int sgsn_auth_request(struct sgsn_mm_ctx *mmctx) -{ - struct gprs_subscr *subscr; - struct gsm_auth_tuple *at; - int need_update_location; - int rc; - - LOGMMCTXP(LOGL_DEBUG, mmctx, "Requesting authorization\n"); - - if (sgsn->cfg.auth_policy != SGSN_AUTH_POLICY_REMOTE) { - sgsn_auth_update(mmctx); - return 0; - } - - need_update_location = sgsn->cfg.require_update_location && - (mmctx->subscr == NULL || - mmctx->pending_req == GSM48_MT_GMM_ATTACH_REQ); - - /* This has the side effect of registering the subscr with the mmctx */ - subscr = gprs_subscr_get_or_create_by_mmctx(mmctx); - gprs_subscr_put(subscr); - - OSMO_ASSERT(mmctx->subscr != NULL); - - if (sgsn->cfg.require_authentication && !mmctx->is_authenticated) { - /* Find next tuple */ - at = sgsn_auth_get_tuple(mmctx, mmctx->auth_triplet.key_seq); - - if (!at) { - /* No valid tuple found, request fresh ones */ - mmctx->auth_triplet.key_seq = GSM_KEY_SEQ_INVAL; - LOGMMCTXP(LOGL_INFO, mmctx, - "Requesting authentication tuples\n"); - rc = gprs_subscr_request_auth_info(mmctx, NULL, NULL); - if (rc >= 0) - return 0; - - return rc; - } - - mmctx->auth_triplet = *at; - } else if (need_update_location) { - LOGMMCTXP(LOGL_INFO, mmctx, - "Missing information, requesting subscriber data\n"); - rc = gprs_subscr_request_update_location(mmctx); - if (rc >= 0) - return 0; - - return rc; - } - - sgsn_auth_update(mmctx); - return 0; -} - -void sgsn_auth_update(struct sgsn_mm_ctx *mmctx) -{ - enum sgsn_auth_state auth_state; - struct gprs_subscr *subscr = mmctx->subscr; - struct gsm_auth_tuple *at; - int gmm_cause; - - auth_state = sgsn_auth_state(mmctx); - - LOGMMCTXP(LOGL_DEBUG, mmctx, "Updating authorization (%s -> %s)\n", - get_value_string(sgsn_auth_state_names, mmctx->auth_state), - get_value_string(sgsn_auth_state_names, auth_state)); - - if (auth_state == SGSN_AUTH_UNKNOWN && subscr && - !(subscr->flags & GPRS_SUBSCRIBER_UPDATE_PENDING_MASK)) { - /* Reject requests if gprs_subscr_request_update_location fails */ - LOGMMCTXP(LOGL_ERROR, mmctx, - "Missing information, authorization not possible\n"); - auth_state = SGSN_AUTH_REJECTED; - } - - if (auth_state == SGSN_AUTH_AUTHENTICATE && - mmctx->auth_triplet.key_seq == GSM_KEY_SEQ_INVAL) { - /* The current tuple is not valid, but we are possibly called - * because new auth tuples have been received */ - at = sgsn_auth_get_tuple(mmctx, mmctx->auth_triplet.key_seq); - if (!at) { - LOGMMCTXP(LOGL_ERROR, mmctx, - "Missing auth tuples, authorization not possible\n"); - auth_state = SGSN_AUTH_REJECTED; - } else { - mmctx->auth_triplet = *at; - } - } - - if (mmctx->auth_state == auth_state) - return; - - LOGMMCTXP(LOGL_INFO, mmctx, "Got authorization update: state %s -> %s\n", - get_value_string(sgsn_auth_state_names, mmctx->auth_state), - get_value_string(sgsn_auth_state_names, auth_state)); - - mmctx->auth_state = auth_state; - - switch (auth_state) { - case SGSN_AUTH_AUTHENTICATE: - if (subscr) - subscr->sgsn_data->auth_triplets_updated = 0; - - gsm0408_gprs_authenticate(mmctx); - break; - case SGSN_AUTH_ACCEPTED: - gsm0408_gprs_access_granted(mmctx); - break; - case SGSN_AUTH_REJECTED: - gmm_cause = - subscr ? subscr->sgsn_data->error_cause : - SGSN_ERROR_CAUSE_NONE; - - if (subscr && (subscr->flags & GPRS_SUBSCRIBER_CANCELLED) != 0) - gsm0408_gprs_access_cancelled(mmctx, gmm_cause); - else - gsm0408_gprs_access_denied(mmctx, gmm_cause); - break; - default: - break; - } -} - -struct gsm_auth_tuple *sgsn_auth_get_tuple(struct sgsn_mm_ctx *mmctx, - unsigned key_seq) -{ - unsigned count; - unsigned idx; - struct gsm_auth_tuple *at = NULL; - - struct sgsn_subscriber_data *sdata; - - if (!mmctx->subscr) - return NULL; - - if (key_seq == GSM_KEY_SEQ_INVAL) - /* Start with 0 after increment module array size */ - idx = ARRAY_SIZE(sdata->auth_triplets) - 1; - else - idx = key_seq; - - sdata = mmctx->subscr->sgsn_data; - - /* Find next tuple */ - for (count = ARRAY_SIZE(sdata->auth_triplets); count > 0; count--) { - idx = (idx + 1) % ARRAY_SIZE(sdata->auth_triplets); - - if (sdata->auth_triplets[idx].key_seq == GSM_KEY_SEQ_INVAL) - continue; - - if (sdata->auth_triplets[idx].use_count == 0) { - at = &sdata->auth_triplets[idx]; - at->use_count = 1; - return at; - } - } - - return NULL; -} diff --git a/openbsc/src/gprs/sgsn_cdr.c b/openbsc/src/gprs/sgsn_cdr.c deleted file mode 100644 index 09108961..00000000 --- a/openbsc/src/gprs/sgsn_cdr.c +++ /dev/null @@ -1,258 +0,0 @@ -/* GPRS SGSN CDR dumper */ - -/* (C) 2015 by Holger Hans Peter Freyther - * All Rights Reserved - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation; either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - * - */ - -#include -#include -#include -#include - -#include - -#include -#include - -#include - -#include - -#include -#include - -/* TODO...avoid going through a global */ -extern struct sgsn_instance *sgsn; - -/** - * The CDR module will generate an entry like: - * - * IMSI, # Subscriber IMSI - * IMEI, # Subscriber IMEI - * MSISDN, # Subscriber MISDN - * Charging_Timestamp, # Event start Time - * Charging_UTC, # Time zone of event start time - * Duration, # Session DURATION - * Cell_Id, # CELL_ID - * Location_Area, # LAC - * GGSN_ADDR, # GGSN_ADDR - * SGSN_ADDR, # SGSN_ADDR - * APNI, # APNI - * PDP_ADDR, # PDP_ADDR - * VOL_IN, # VOL_IN in Bytes - * VOL_OUT, # VOL_OUT in Bytes - * CAUSE_FOR_TERM, # CAUSE_FOR_TERM - */ - - -static void maybe_print_header(FILE *cdr_file) -{ - if (ftell(cdr_file) != 0) - return; - - fprintf(cdr_file, "timestamp,imsi,imei,msisdn,cell_id,lac,hlr,event,pdp_duration,ggsn_addr,sgsn_addr,apni,eua_addr,vol_in,vol_out,charging_id\n"); -} - -static void cdr_log_mm(struct sgsn_instance *inst, const char *ev, - struct sgsn_mm_ctx *mmctx) -{ - FILE *cdr_file; - struct tm tm; - struct timeval tv; - - if (!inst->cfg.cdr.filename) - return; - - cdr_file = fopen(inst->cfg.cdr.filename, "a"); - if (!cdr_file) { - LOGP(DGPRS, LOGL_ERROR, "Failed to open %s\n", - inst->cfg.cdr.filename); - return; - } - - maybe_print_header(cdr_file); - gettimeofday(&tv, NULL); - gmtime_r(&tv.tv_sec, &tm); - fprintf(cdr_file, "%04d%02d%02d%02d%02d%02d%03d,%s,%s,%s,%d,%d,%s,%s\n", - tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday, - tm.tm_hour, tm.tm_min, tm.tm_sec, - (int)(tv.tv_usec / 1000), - mmctx->imsi, - mmctx->imei, - mmctx->msisdn, - mmctx->gb.cell_id, - mmctx->ra.lac, - mmctx->hlr, - ev); - - fclose(cdr_file); -} - -static void extract_eua(struct ul66_t *eua, char *eua_addr) -{ - if (eua->l < 2) - return; - - /* there is no addr for ETSI/PPP */ - if ((eua->v[0] & 0x0F) != 1) { - strcpy(eua_addr, "ETSI"); - return; - } - - if (eua->v[1] == 0x21 && eua->l == 6) - inet_ntop(AF_INET, &eua->v[2], eua_addr, INET_ADDRSTRLEN); - else if (eua->v[1] == 0x57 && eua->l == 18) - inet_ntop(AF_INET6, &eua->v[2], eua_addr, INET6_ADDRSTRLEN); - else { - /* e.g. both IPv4 and IPv6 */ - strcpy(eua_addr, "Unknown address"); - } -} - -static void cdr_log_pdp(struct sgsn_instance *inst, const char *ev, - struct sgsn_pdp_ctx *pdp) -{ - FILE *cdr_file; - char apni[(pdp->lib ? pdp->lib->apn_use.l : 0) + 1]; - char ggsn_addr[INET_ADDRSTRLEN + 1]; - char sgsn_addr[INET_ADDRSTRLEN + 1]; - char eua_addr[INET6_ADDRSTRLEN + 1]; - struct tm tm; - struct timeval tv; - time_t duration; - struct timespec tp; - - if (!inst->cfg.cdr.filename) - return; - - memset(apni, 0, sizeof(apni)); - memset(ggsn_addr, 0, sizeof(ggsn_addr)); - memset(eua_addr, 0, sizeof(eua_addr)); - - - if (pdp->lib) { - gprs_apn_to_str(apni, pdp->lib->apn_use.v, pdp->lib->apn_use.l); - inet_ntop(AF_INET, &pdp->lib->hisaddr0.s_addr, ggsn_addr, sizeof(ggsn_addr)); - extract_eua(&pdp->lib->eua, eua_addr); - } - - if (pdp->ggsn) - inet_ntop(AF_INET, &pdp->ggsn->gsn->gsnc.s_addr, sgsn_addr, sizeof(sgsn_addr)); - - cdr_file = fopen(inst->cfg.cdr.filename, "a"); - if (!cdr_file) { - LOGP(DGPRS, LOGL_ERROR, "Failed to open %s\n", - inst->cfg.cdr.filename); - return; - } - - maybe_print_header(cdr_file); - - clock_gettime(CLOCK_MONOTONIC, &tp); - gettimeofday(&tv, NULL); - - /* convert the timestamp to UTC */ - gmtime_r(&tv.tv_sec, &tm); - - /* Check the duration of the PDP context */ - duration = tp.tv_sec - pdp->cdr_start.tv_sec; - - fprintf(cdr_file, - "%04d%02d%02d%02d%02d%02d%03d,%s,%s,%s,%d,%d,%s,%s,%ld,%s,%s,%s,%s,%" PRIu64 ",%" PRIu64 ",%u\n", - tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday, - tm.tm_hour, tm.tm_min, tm.tm_sec, - (int)(tv.tv_usec / 1000), - pdp->mm ? pdp->mm->imsi : "N/A", - pdp->mm ? pdp->mm->imei : "N/A", - pdp->mm ? pdp->mm->msisdn : "N/A", - pdp->mm ? pdp->mm->gb.cell_id : -1, - pdp->mm ? pdp->mm->ra.lac : -1, - pdp->mm ? pdp->mm->hlr : "N/A", - ev, - (unsigned long ) duration, - ggsn_addr, - sgsn_addr, - apni, - eua_addr, - pdp->cdr_bytes_in, - pdp->cdr_bytes_out, - pdp->cdr_charging_id); - fclose(cdr_file); -} - -static void cdr_pdp_timeout(void *_data) -{ - struct sgsn_pdp_ctx *pdp = _data; - cdr_log_pdp(sgsn, "pdp-periodic", pdp); - osmo_timer_schedule(&pdp->cdr_timer, sgsn->cfg.cdr.interval, 0); -} - -static int handle_sgsn_sig(unsigned int subsys, unsigned int signal, - void *handler_data, void *_signal_data) -{ - struct sgsn_signal_data *signal_data = _signal_data; - struct sgsn_instance *inst = handler_data; - - if (subsys != SS_SGSN) - return 0; - - switch (signal) { - case S_SGSN_ATTACH: - cdr_log_mm(inst, "attach", signal_data->mm); - break; - case S_SGSN_UPDATE: - cdr_log_mm(inst, "update", signal_data->mm); - break; - case S_SGSN_DETACH: - cdr_log_mm(inst, "detach", signal_data->mm); - break; - case S_SGSN_MM_FREE: - cdr_log_mm(inst, "free", signal_data->mm); - break; - case S_SGSN_PDP_ACT: - clock_gettime(CLOCK_MONOTONIC, &signal_data->pdp->cdr_start); - signal_data->pdp->cdr_charging_id = signal_data->pdp->lib->cid; - cdr_log_pdp(inst, "pdp-act", signal_data->pdp); - osmo_timer_setup(&signal_data->pdp->cdr_timer, cdr_pdp_timeout, - signal_data->pdp); - osmo_timer_schedule(&signal_data->pdp->cdr_timer, inst->cfg.cdr.interval, 0); - break; - case S_SGSN_PDP_DEACT: - cdr_log_pdp(inst, "pdp-deact", signal_data->pdp); - osmo_timer_del(&signal_data->pdp->cdr_timer); - break; - case S_SGSN_PDP_TERMINATE: - cdr_log_pdp(inst, "pdp-terminate", signal_data->pdp); - osmo_timer_del(&signal_data->pdp->cdr_timer); - break; - case S_SGSN_PDP_FREE: - cdr_log_pdp(inst, "pdp-free", signal_data->pdp); - osmo_timer_del(&signal_data->pdp->cdr_timer); - break; - } - - return 0; -} - -int sgsn_cdr_init(struct sgsn_instance *sgsn) -{ - /* register for CDR related events */ - sgsn->cfg.cdr.interval = 10 * 60; - osmo_signal_register_handler(SS_SGSN, handle_sgsn_sig, sgsn); - - return 0; -} diff --git a/openbsc/src/gprs/sgsn_ctrl.c b/openbsc/src/gprs/sgsn_ctrl.c deleted file mode 100644 index 31ac74f1..00000000 --- a/openbsc/src/gprs/sgsn_ctrl.c +++ /dev/null @@ -1,69 +0,0 @@ -/* Control Interface Implementation for the SGSN */ -/* - * (C) 2014 by Holger Hans Peter Freyther - * (C) 2014 by sysmocom s.f.m.c. GmbH - * All Rights Reserved - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation; either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - * - */ - -#include -#include -#include -#include -#include -#include - -#include - -extern vector ctrl_node_vec; - -static int get_subscriber_list(struct ctrl_cmd *cmd, void *d) -{ - struct sgsn_mm_ctx *mm; - - cmd->reply = talloc_strdup(cmd, ""); - llist_for_each_entry(mm, &sgsn_mm_ctxts, list) { - char *addr = NULL; - struct sgsn_pdp_ctx *pdp; - - if (strlen(mm->imsi) == 0) - continue; - - llist_for_each_entry(pdp, &mm->pdp_list, list) - addr = gprs_pdpaddr2str(pdp->lib->eua.v, - pdp->lib->eua.l); - - cmd->reply = talloc_asprintf_append( - cmd->reply, - "%s,%s\n", mm->imsi, addr ? addr : ""); - } - - return CTRL_CMD_REPLY; -} -CTRL_CMD_DEFINE_RO(subscriber_list, "subscriber-list-active-v1"); - -int sgsn_ctrl_cmds_install(void) -{ - int rc = 0; - rc |= ctrl_cmd_install(CTRL_NODE_ROOT, &cmd_subscriber_list); - return rc; -} - -struct ctrl_handle *sgsn_controlif_setup(struct gsm_network *net, - const char *bind_addr, uint16_t port) -{ - return ctrl_interface_setup_dynip(net, bind_addr, port, NULL); -} diff --git a/openbsc/src/gprs/sgsn_libgtp.c b/openbsc/src/gprs/sgsn_libgtp.c deleted file mode 100644 index 001e6114..00000000 --- a/openbsc/src/gprs/sgsn_libgtp.c +++ /dev/null @@ -1,860 +0,0 @@ -/* GPRS SGSN integration with libgtp of OpenGGSN */ -/* libgtp implements the GPRS Tunelling Protocol GTP per TS 09.60 / 29.060 */ - -/* (C) 2010 by Harald Welte - * (C) 2010 by On-Waves - * (C) 2015 by Holger Hans Peter Freyther - * All Rights Reserved - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation; either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - * - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "bscconfig.h" - -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include - -#ifdef BUILD_IU -#include -#include -#endif - -#include -#include - -/* TS 23.003: The MSISDN shall take the dummy MSISDN value composed of - * 15 digits set to 0 (encoded as an E.164 international number) when - * the MSISDN is not available in messages in which the presence of the - * MSISDN parameter */ -static const uint8_t dummy_msisdn[] = - { 0x91, /* No extension, international, E.164 */ - 0, 0, 0, 0, 0, 0, 0, /* 14 digits of zeroes */ - 0xF0 /* 15th digit of zero + padding */ }; - -const struct value_string gtp_cause_strs[] = { - { GTPCAUSE_REQ_IMSI, "Request IMSI" }, - { GTPCAUSE_REQ_IMEI, "Request IMEI" }, - { GTPCAUSE_REQ_IMSI_IMEI, "Request IMSI and IMEI" }, - { GTPCAUSE_NO_ID_NEEDED, "No identity needed" }, - { GTPCAUSE_MS_REFUSES_X, "MS refuses" }, - { GTPCAUSE_MS_NOT_RESP_X, "MS is not GPRS responding" }, - { GTPCAUSE_ACC_REQ, "Request accepted" }, - { GTPCAUSE_NON_EXIST, "Non-existent" }, - { GTPCAUSE_INVALID_MESSAGE, "Invalid message format" }, - { GTPCAUSE_IMSI_NOT_KNOWN, "IMSI not known" }, - { GTPCAUSE_MS_DETACHED, "MS is GPRS detached" }, - { GTPCAUSE_MS_NOT_RESP, "MS is not GPRS responding" }, - { GTPCAUSE_MS_REFUSES, "MS refuses" }, - { GTPCAUSE_NO_RESOURCES, "No resources available" }, - { GTPCAUSE_NOT_SUPPORTED, "Service not supported" }, - { GTPCAUSE_MAN_IE_INCORRECT, "Mandatory IE incorrect" }, - { GTPCAUSE_MAN_IE_MISSING, "Mandatory IE missing" }, - { GTPCAUSE_OPT_IE_INCORRECT, "Optional IE incorrect" }, - { GTPCAUSE_SYS_FAIL, "System failure" }, - { GTPCAUSE_ROAMING_REST, "Roaming restrictions" }, - { GTPCAUSE_PTIMSI_MISMATCH, "P-TMSI Signature mismatch" }, - { GTPCAUSE_CONN_SUSP, "GPRS connection suspended" }, - { GTPCAUSE_AUTH_FAIL, "Authentication failure" }, - { GTPCAUSE_USER_AUTH_FAIL, "User authentication failed" }, - { GTPCAUSE_CONTEXT_NOT_FOUND, "Context not found" }, - { GTPCAUSE_ADDR_OCCUPIED, "All dynamic PDP addresses occupied" }, - { GTPCAUSE_NO_MEMORY, "No memory is available" }, - { GTPCAUSE_RELOC_FAIL, "Relocation failure" }, - { GTPCAUSE_UNKNOWN_MAN_EXTHEADER, "Unknown mandatory ext. header" }, - { GTPCAUSE_SEM_ERR_TFT, "Semantic error in TFT operation" }, - { GTPCAUSE_SYN_ERR_TFT, "Syntactic error in TFT operation" }, - { GTPCAUSE_SEM_ERR_FILTER, "Semantic errors in packet filter" }, - { GTPCAUSE_SYN_ERR_FILTER, "Syntactic errors in packet filter" }, - { GTPCAUSE_MISSING_APN, "Missing or unknown APN" }, - { GTPCAUSE_UNKNOWN_PDP, "Unknown PDP address or PDP type" }, - { 0, NULL } -}; - -/* Generate the GTP IMSI IE according to 09.60 Section 7.9.2 */ -static uint64_t imsi_str2gtp(char *str) -{ - uint64_t imsi64 = 0; - unsigned int n; - unsigned int imsi_len = strlen(str); - - if (imsi_len > 16) { - LOGP(DGPRS, LOGL_NOTICE, "IMSI length > 16 not supported!\n"); - return 0; - } - - for (n = 0; n < 16; n++) { - uint64_t val; - if (n < imsi_len) - val = (str[n]-'0') & 0xf; - else - val = 0xf; - imsi64 |= (val << (n*4)); - } - return imsi64; -} - -/* generate a PDP context based on the IE's from the 04.08 message, - * and send the GTP create pdp context request to the GGSN */ -struct sgsn_pdp_ctx *sgsn_create_pdp_ctx(struct sgsn_ggsn_ctx *ggsn, - struct sgsn_mm_ctx *mmctx, - uint16_t nsapi, - struct tlv_parsed *tp) -{ - struct gprs_ra_id raid; - struct sgsn_pdp_ctx *pctx; - struct pdp_t *pdp; - uint64_t imsi_ui64; - size_t qos_len; - const uint8_t *qos; - int rc; - - LOGP(DGPRS, LOGL_ERROR, "Create PDP Context\n"); - pctx = sgsn_pdp_ctx_alloc(mmctx, nsapi); - if (!pctx) { - LOGP(DGPRS, LOGL_ERROR, "Couldn't allocate PDP Ctx\n"); - return NULL; - } - - imsi_ui64 = imsi_str2gtp(mmctx->imsi); - - rc = pdp_newpdp(&pdp, imsi_ui64, nsapi, NULL); - if (rc) { - LOGP(DGPRS, LOGL_ERROR, "Out of libgtp PDP Contexts\n"); - return NULL; - } - pdp->priv = pctx; - pctx->lib = pdp; - pctx->ggsn = ggsn; - - //pdp->peer = /* sockaddr_in of GGSN (receive) */ - //pdp->ipif = /* not used by library */ - pdp->version = ggsn->gtp_version; - pdp->hisaddr0 = ggsn->remote_addr; - pdp->hisaddr1 = ggsn->remote_addr; - //pdp->cch_pdp = 512; /* Charging Flat Rate */ - - /* MS provided APN, subscription was verified by the caller */ - pdp->selmode = 0xFC | 0x00; - - /* IMSI, TEID/TEIC, FLLU/FLLC, TID, NSAPI set in pdp_newpdp */ - - /* Put the MSISDN in case we have it */ - if (mmctx->subscr && mmctx->subscr->sgsn_data->msisdn_len) { - pdp->msisdn.l = mmctx->subscr->sgsn_data->msisdn_len; - if (pdp->msisdn.l > sizeof(pdp->msisdn.v)) - pdp->msisdn.l = sizeof(pdp->msisdn.v); - memcpy(pdp->msisdn.v, mmctx->subscr->sgsn_data->msisdn, - pdp->msisdn.l); - } else { - /* use the dummy 15-digits-zero MSISDN value */ - pdp->msisdn.l = sizeof(dummy_msisdn); - memcpy(pdp->msisdn.v, dummy_msisdn, pdp->msisdn.l); - } - - /* End User Address from GMM requested PDP address */ - pdp->eua.l = TLVP_LEN(tp, OSMO_IE_GSM_REQ_PDP_ADDR); - if (pdp->eua.l > sizeof(pdp->eua.v)) - pdp->eua.l = sizeof(pdp->eua.v); - memcpy(pdp->eua.v, TLVP_VAL(tp, OSMO_IE_GSM_REQ_PDP_ADDR), - pdp->eua.l); - /* Highest 4 bits of first byte need to be set to 1, otherwise - * the IE is identical with the 04.08 PDP Address IE */ - pdp->eua.v[0] |= 0xf0; - - /* APN name from GMM */ - pdp->apn_use.l = TLVP_LEN(tp, GSM48_IE_GSM_APN); - if (pdp->apn_use.l > sizeof(pdp->apn_use.v)) - pdp->apn_use.l = sizeof(pdp->apn_use.v); - memcpy(pdp->apn_use.v, TLVP_VAL(tp, GSM48_IE_GSM_APN), - pdp->apn_use.l); - - /* Protocol Configuration Options from GMM */ - pdp->pco_req.l = TLVP_LEN(tp, GSM48_IE_GSM_PROTO_CONF_OPT); - if (pdp->pco_req.l > sizeof(pdp->pco_req.v)) - pdp->pco_req.l = sizeof(pdp->pco_req.v); - memcpy(pdp->pco_req.v, TLVP_VAL(tp, GSM48_IE_GSM_PROTO_CONF_OPT), - pdp->pco_req.l); - - /* QoS options from GMM or remote */ - if (TLVP_LEN(tp, OSMO_IE_GSM_SUB_QOS) > 0) { - qos_len = TLVP_LEN(tp, OSMO_IE_GSM_SUB_QOS); - qos = TLVP_VAL(tp, OSMO_IE_GSM_SUB_QOS); - } else { - qos_len = TLVP_LEN(tp, OSMO_IE_GSM_REQ_QOS); - qos = TLVP_VAL(tp, OSMO_IE_GSM_REQ_QOS); - } - - if (qos_len <= 3) { - pdp->qos_req.l = qos_len + 1; - if (pdp->qos_req.l > sizeof(pdp->qos_req.v)) - pdp->qos_req.l = sizeof(pdp->qos_req.v); - pdp->qos_req.v[0] = 0; /* Allocation/Retention policy */ - memcpy(&pdp->qos_req.v[1], qos, pdp->qos_req.l - 1); - } else { - pdp->qos_req.l = qos_len; - if (pdp->qos_req.l > sizeof(pdp->qos_req.v)) - pdp->qos_req.l = sizeof(pdp->qos_req.v); - memcpy(pdp->qos_req.v, qos, pdp->qos_req.l); - } - - /* SGSN address for control plane */ - pdp->gsnlc.l = sizeof(sgsn->cfg.gtp_listenaddr.sin_addr); - memcpy(pdp->gsnlc.v, &sgsn->cfg.gtp_listenaddr.sin_addr, - sizeof(sgsn->cfg.gtp_listenaddr.sin_addr)); - - /* SGSN address for user plane - * Default to the control plane addr for now. If we are connected to a - * hnbgw via IuPS we'll need to send a PDP context update with the - * correct IP address after the RAB Assignment is complete */ - pdp->gsnlu.l = sizeof(sgsn->cfg.gtp_listenaddr.sin_addr); - memcpy(pdp->gsnlu.v, &sgsn->cfg.gtp_listenaddr.sin_addr, - sizeof(sgsn->cfg.gtp_listenaddr.sin_addr)); - - /* Assume we are a GERAN system */ - pdp->rattype.l = 1; - pdp->rattype.v[0] = 2; - pdp->rattype_given = 1; - - /* Include RAI and ULI all the time */ - pdp->rai_given = 1; - pdp->rai.l = 6; - raid = mmctx->ra; - raid.lac = 0xFFFE; - raid.rac = 0xFF; - gsm48_construct_ra(pdp->rai.v, &raid); - - pdp->userloc_given = 1; - pdp->userloc.l = 8; - pdp->userloc.v[0] = 0; /* CGI for GERAN */ - bssgp_create_cell_id(&pdp->userloc.v[1], &mmctx->ra, mmctx->gb.cell_id); - - /* include the IMEI(SV) */ - pdp->imeisv_given = 1; - gsm48_encode_bcd_number(&pdp->imeisv.v[0], 8, 0, mmctx->imei); - pdp->imeisv.l = pdp->imeisv.v[0]; - memmove(&pdp->imeisv.v[0], &pdp->imeisv.v[1], 8); - - /* change pdp state to 'requested' */ - pctx->state = PDP_STATE_CR_REQ; - - rc = gtp_create_context_req(ggsn->gsn, pdp, pctx); - /* FIXME */ - - return pctx; -} - -/* SGSN wants to delete a PDP context */ -int sgsn_delete_pdp_ctx(struct sgsn_pdp_ctx *pctx) -{ - LOGPDPCTXP(LOGL_ERROR, pctx, "Delete PDP Context\n"); - - /* FIXME: decide if we need teardown or not ! */ - return gtp_delete_context_req(pctx->ggsn->gsn, pctx->lib, pctx, 1); -} - -struct cause_map { - uint8_t cause_in; - uint8_t cause_out; -}; - -static uint8_t cause_map(const struct cause_map *map, uint8_t in, uint8_t deflt) -{ - const struct cause_map *m; - - for (m = map; m->cause_in && m->cause_out; m++) { - if (m->cause_in == in) - return m->cause_out; - } - return deflt; -} - -/* how do we map from gtp cause to SM cause */ -static const struct cause_map gtp2sm_cause_map[] = { - { GTPCAUSE_NO_RESOURCES, GSM_CAUSE_INSUFF_RSRC }, - { GTPCAUSE_NOT_SUPPORTED, GSM_CAUSE_SERV_OPT_NOTSUPP }, - { GTPCAUSE_MAN_IE_INCORRECT, GSM_CAUSE_INV_MAND_INFO }, - { GTPCAUSE_MAN_IE_MISSING, GSM_CAUSE_INV_MAND_INFO }, - { GTPCAUSE_OPT_IE_INCORRECT, GSM_CAUSE_PROTO_ERR_UNSPEC }, - { GTPCAUSE_SYS_FAIL, GSM_CAUSE_NET_FAIL }, - { GTPCAUSE_ROAMING_REST, GSM_CAUSE_REQ_SERV_OPT_NOTSUB }, - { GTPCAUSE_PTIMSI_MISMATCH, GSM_CAUSE_PROTO_ERR_UNSPEC }, - { GTPCAUSE_CONN_SUSP, GSM_CAUSE_PROTO_ERR_UNSPEC }, - { GTPCAUSE_AUTH_FAIL, GSM_CAUSE_AUTH_FAILED }, - { GTPCAUSE_USER_AUTH_FAIL, GSM_CAUSE_ACT_REJ_GGSN }, - { GTPCAUSE_CONTEXT_NOT_FOUND, GSM_CAUSE_PROTO_ERR_UNSPEC }, - { GTPCAUSE_ADDR_OCCUPIED, GSM_CAUSE_INSUFF_RSRC }, - { GTPCAUSE_NO_MEMORY, GSM_CAUSE_INSUFF_RSRC }, - { GTPCAUSE_RELOC_FAIL, GSM_CAUSE_PROTO_ERR_UNSPEC }, - { GTPCAUSE_UNKNOWN_MAN_EXTHEADER, GSM_CAUSE_PROTO_ERR_UNSPEC }, - { GTPCAUSE_MISSING_APN, GSM_CAUSE_MISSING_APN }, - { GTPCAUSE_UNKNOWN_PDP, GSM_CAUSE_UNKNOWN_PDP }, - { 0, 0 } -}; - -static int send_act_pdp_cont_acc(struct sgsn_pdp_ctx *pctx) -{ - struct sgsn_signal_data sig_data; - int rc; - struct gprs_llc_lle *lle; - - /* Inform others about it */ - memset(&sig_data, 0, sizeof(sig_data)); - sig_data.pdp = pctx; - osmo_signal_dispatch(SS_SGSN, S_SGSN_PDP_ACT, &sig_data); - - /* Send PDP CTX ACT to MS */ - rc = gsm48_tx_gsm_act_pdp_acc(pctx); - if (rc < 0) - return rc; - - if (pctx->mm->ran_type == MM_CTX_T_GERAN_Gb) { - /* Send SNDCP XID to MS */ - lle = &pctx->mm->gb.llme->lle[pctx->sapi]; - rc = sndcp_sn_xid_req(lle,pctx->nsapi); - if (rc < 0) - return rc; - } - - return 0; -} - -/* The GGSN has confirmed the creation of a PDP Context */ -static int create_pdp_conf(struct pdp_t *pdp, void *cbp, int cause) -{ - struct sgsn_pdp_ctx *pctx = cbp; - uint8_t reject_cause; - - LOGPDPCTXP(LOGL_INFO, pctx, "Received CREATE PDP CTX CONF, cause=%d(%s)\n", - cause, get_value_string(gtp_cause_strs, cause)); - - if (!pctx->mm) { - LOGP(DGPRS, LOGL_INFO, - "No MM context, aborting CREATE PDP CTX CONF\n"); - return -EIO; - } - - /* Check for cause value if it was really successful */ - if (cause < 0) { - LOGP(DGPRS, LOGL_NOTICE, "Create PDP ctx req timed out\n"); - if (pdp && pdp->version == 1) { - pdp->version = 0; - gtp_create_context_req(sgsn->gsn, pdp, cbp); - return 0; - } else { - reject_cause = GSM_CAUSE_NET_FAIL; - goto reject; - } - } - - /* Check for cause value if it was really successful */ - if (cause != GTPCAUSE_ACC_REQ) { - reject_cause = cause_map(gtp2sm_cause_map, cause, - GSM_CAUSE_ACT_REJ_GGSN); - goto reject; - } - - if (pctx->mm->ran_type == MM_CTX_T_GERAN_Gb) { - /* Activate the SNDCP layer */ - sndcp_sm_activate_ind(&pctx->mm->gb.llme->lle[pctx->sapi], pctx->nsapi); - return send_act_pdp_cont_acc(pctx); - } else if (pctx->mm->ran_type == MM_CTX_T_UTRAN_Iu) { -#ifdef BUILD_IU - /* Activate a radio bearer */ - iu_rab_act_ps(pdp->nsapi, pctx, 1); - return 0; -#else - return -ENOTSUP; -#endif - } - - LOGP(DGPRS, LOGL_ERROR, "Unknown ran_type %d\n", - pctx->mm->ran_type); - reject_cause = GSM_CAUSE_PROTO_ERR_UNSPEC; - -reject: - /* - * In case of a timeout pdp will be NULL but we have a valid pointer - * in pctx->lib. For other rejects pctx->lib and pdp might be the - * same. - */ - pctx->state = PDP_STATE_NONE; - if (pctx->lib && pctx->lib != pdp) - pdp_freepdp(pctx->lib); - pctx->lib = NULL; - - if (pdp) - pdp_freepdp(pdp); - /* Send PDP CTX ACT REJ to MS */ - gsm48_tx_gsm_act_pdp_rej(pctx->mm, pctx->ti, reject_cause, - 0, NULL); - sgsn_pdp_ctx_free(pctx); - - return EOF; -} - -void sgsn_pdp_upd_gtp_u(struct sgsn_pdp_ctx *pdp, void *addr, size_t alen) -{ - pdp->lib->gsnlu.l = alen; - memcpy(pdp->lib->gsnlu.v, addr, alen); - gtp_update_context(pdp->ggsn->gsn, pdp->lib, pdp, &pdp->lib->hisaddr0); -} - -#ifdef BUILD_IU -/* Callback for RAB assignment response */ -int sgsn_ranap_rab_ass_resp(struct sgsn_mm_ctx *ctx, RANAP_RAB_SetupOrModifiedItemIEs_t *setup_ies) -{ - uint8_t rab_id; - bool require_pdp_update = false; - struct sgsn_pdp_ctx *pdp = NULL; - RANAP_RAB_SetupOrModifiedItem_t *item = &setup_ies->raB_SetupOrModifiedItem; - - rab_id = item->rAB_ID.buf[0]; - - pdp = sgsn_pdp_ctx_by_nsapi(ctx, rab_id); - if (!pdp) { - LOGP(DRANAP, LOGL_ERROR, "RAB Assignment Response for unknown RAB/NSAPI=%u\n", rab_id); - return -1; - } - - if (item->transportLayerAddress) { - LOGPC(DRANAP, LOGL_INFO, " Setup: (%u/%s)", rab_id, osmo_hexdump(item->transportLayerAddress->buf, - item->transportLayerAddress->size)); - switch (item->transportLayerAddress->size) { - case 7: - /* It must be IPv4 inside a X213 NSAP */ - memcpy(pdp->lib->gsnlu.v, &item->transportLayerAddress->buf[3], 4); - break; - case 4: - /* It must be a raw IPv4 address */ - memcpy(pdp->lib->gsnlu.v, item->transportLayerAddress->buf, 4); - break; - case 16: - /* TODO: It must be a raw IPv6 address */ - case 19: - /* TODO: It must be IPv6 inside a X213 NSAP */ - default: - LOGP(DRANAP, LOGL_ERROR, "RAB Assignment Resp: Unknown " - "transport layer address size %u\n", - item->transportLayerAddress->size); - return -1; - } - require_pdp_update = true; - } - - /* The TEI on the RNC side might have changed, too */ - if (item->iuTransportAssociation && - item->iuTransportAssociation->present == RANAP_IuTransportAssociation_PR_gTP_TEI && - item->iuTransportAssociation->choice.gTP_TEI.buf && - item->iuTransportAssociation->choice.gTP_TEI.size >= 4) { - uint32_t tei = osmo_load32be(item->iuTransportAssociation->choice.gTP_TEI.buf); - LOGP(DRANAP, LOGL_DEBUG, "Updating TEID on RNC side from 0x%08x to 0x%08x\n", - pdp->lib->teid_own, tei); - pdp->lib->teid_own = tei; - require_pdp_update = true; - } - - if (require_pdp_update) - gtp_update_context(pdp->ggsn->gsn, pdp->lib, pdp, &pdp->lib->hisaddr0); - - if (pdp->state != PDP_STATE_CR_CONF) { - send_act_pdp_cont_acc(pdp); - pdp->state = PDP_STATE_CR_CONF; - } - return 0; - -} -#endif - -/* Confirmation of a PDP Context Delete */ -static int delete_pdp_conf(struct pdp_t *pdp, void *cbp, int cause) -{ - struct sgsn_signal_data sig_data; - struct sgsn_pdp_ctx *pctx = cbp; - int rc = 0; - - LOGPDPCTXP(LOGL_INFO, pctx, "Received DELETE PDP CTX CONF, cause=%d(%s)\n", - cause, get_value_string(gtp_cause_strs, cause)); - - memset(&sig_data, 0, sizeof(sig_data)); - sig_data.pdp = pctx; - osmo_signal_dispatch(SS_SGSN, S_SGSN_PDP_DEACT, &sig_data); - - if (pctx->mm) { - if (pctx->mm->ran_type == MM_CTX_T_GERAN_Gb) { - /* Deactivate the SNDCP layer */ - sndcp_sm_deactivate_ind(&pctx->mm->gb.llme->lle[pctx->sapi], pctx->nsapi); - } else { -#ifdef BUILD_IU - /* Deactivate radio bearer */ - iu_rab_deact(pctx->mm->iu.ue_ctx, 1); -#else - return -ENOTSUP; -#endif - } - - /* Confirm deactivation of PDP context to MS */ - rc = gsm48_tx_gsm_deact_pdp_acc(pctx); - } else { - LOGPDPCTXP(LOGL_NOTICE, pctx, - "Not deactivating SNDCP layer since the MM context " - "is not available\n"); - } - - /* unlink the now non-existing library handle from the pdp - * context */ - pctx->lib = NULL; - - sgsn_pdp_ctx_free(pctx); - - return rc; -} - -/* Confirmation of an GTP ECHO request */ -static int echo_conf(struct pdp_t *pdp, void *cbp, int recovery) -{ - if (recovery < 0) { - LOGP(DGPRS, LOGL_NOTICE, "GTP Echo Request timed out\n"); - /* FIXME: if version == 1, retry with version 0 */ - } else { - DEBUGP(DGPRS, "GTP Rx Echo Response\n"); - } - return 0; -} - -/* Any message received by GGSN contains a recovery IE */ -static int cb_recovery(struct sockaddr_in *peer, uint8_t recovery) -{ - struct sgsn_ggsn_ctx *ggsn; - - ggsn = sgsn_ggsn_ctx_by_addr(&peer->sin_addr); - if (!ggsn) { - LOGP(DGPRS, LOGL_NOTICE, "Received Recovery IE for unknown GGSN\n"); - return -EINVAL; - } - - if (ggsn->remote_restart_ctr == -1) { - /* First received ECHO RESPONSE, note the restart ctr */ - ggsn->remote_restart_ctr = recovery; - } else if (ggsn->remote_restart_ctr != recovery) { - /* counter has changed (GGSN restart): release all PDP */ - LOGP(DGPRS, LOGL_NOTICE, "GGSN recovery (%u->%u), " - "releasing all PDP contexts\n", - ggsn->remote_restart_ctr, recovery); - ggsn->remote_restart_ctr = recovery; - drop_all_pdp_for_ggsn(ggsn); - } - return 0; -} - -/* libgtp callback for confirmations */ -static int cb_conf(int type, int cause, struct pdp_t *pdp, void *cbp) -{ - DEBUGP(DGPRS, "libgtp cb_conf(type=%d, cause=%d, pdp=%p, cbp=%p)\n", - type, cause, pdp, cbp); - - if (cause == EOF) - LOGP(DGPRS, LOGL_ERROR, "libgtp EOF (type=%u, pdp=%p, cbp=%p)\n", - type, pdp, cbp); - - switch (type) { - case GTP_ECHO_REQ: - /* libgtp hands us the RECOVERY number instead of a cause */ - return echo_conf(pdp, cbp, cause); - case GTP_CREATE_PDP_REQ: - return create_pdp_conf(pdp, cbp, cause); - case GTP_DELETE_PDP_REQ: - return delete_pdp_conf(pdp, cbp, cause); - default: - break; - } - return 0; -} - -/* Called whenever a PDP context is deleted for any reason */ -static int cb_delete_context(struct pdp_t *pdp) -{ - LOGP(DGPRS, LOGL_INFO, "PDP Context was deleted\n"); - return 0; -} - -/* Called when we receive a Version Not Supported message */ -static int cb_unsup_ind(struct sockaddr_in *peer) -{ - LOGP(DGPRS, LOGL_INFO, "GTP Version not supported Indication " - "from %s:%u\n", inet_ntoa(peer->sin_addr), - ntohs(peer->sin_port)); - return 0; -} - -/* Called when we receive a Supported Ext Headers Notification */ -static int cb_extheader_ind(struct sockaddr_in *peer) -{ - LOGP(DGPRS, LOGL_INFO, "GTP Supported Ext Headers Noficiation " - "from %s:%u\n", inet_ntoa(peer->sin_addr), - ntohs(peer->sin_port)); - return 0; -} - -/* Called whenever we recive a DATA packet */ -static int cb_data_ind(struct pdp_t *lib, void *packet, unsigned int len) -{ - struct bssgp_paging_info pinfo; - struct sgsn_pdp_ctx *pdp; - struct sgsn_mm_ctx *mm; - struct msgb *msg; - uint8_t *ud; - - pdp = lib->priv; - if (!pdp) { - LOGP(DGPRS, LOGL_NOTICE, - "GTP DATA IND from GGSN for unknown PDP\n"); - return -EIO; - } - mm = pdp->mm; - if (!mm) { - LOGP(DGPRS, LOGL_ERROR, - "PDP context (address=%u) without MM context!\n", - pdp->address); - return -EIO; - } - - DEBUGP(DGPRS, "GTP DATA IND from GGSN for %s, length=%u\n", mm->imsi, - len); - - if (mm->ran_type == MM_CTX_T_UTRAN_Iu) { -#ifdef BUILD_IU - /* Ignore the packet for now and page the UE to get the RAB - * reestablished */ - iu_page_ps(mm->imsi, &mm->p_tmsi, mm->ra.lac, mm->ra.rac); - - return 0; -#else - return -ENOTSUP; -#endif - } - - msg = msgb_alloc_headroom(len+256, 128, "GTP->SNDCP"); - ud = msgb_put(msg, len); - memcpy(ud, packet, len); - - msgb_tlli(msg) = mm->gb.tlli; - msgb_bvci(msg) = mm->gb.bvci; - msgb_nsei(msg) = mm->gb.nsei; - - switch (mm->gmm_state) { - case GMM_REGISTERED_SUSPENDED: - /* initiate PS PAGING procedure */ - memset(&pinfo, 0, sizeof(pinfo)); - pinfo.mode = BSSGP_PAGING_PS; - pinfo.scope = BSSGP_PAGING_BVCI; - pinfo.bvci = mm->gb.bvci; - pinfo.imsi = mm->imsi; - pinfo.ptmsi = &mm->p_tmsi; - pinfo.drx_params = mm->drx_parms; - pinfo.qos[0] = 0; // FIXME - bssgp_tx_paging(mm->gb.nsei, 0, &pinfo); - rate_ctr_inc(&mm->ctrg->ctr[GMM_CTR_PAGING_PS]); - /* FIXME: queue the packet we received from GTP */ - break; - case GMM_REGISTERED_NORMAL: - break; - default: - LOGP(DGPRS, LOGL_ERROR, "GTP DATA IND for TLLI %08X in state " - "%u\n", mm->gb.tlli, mm->gmm_state); - msgb_free(msg); - return -1; - } - - rate_ctr_inc(&pdp->ctrg->ctr[PDP_CTR_PKTS_UDATA_OUT]); - rate_ctr_add(&pdp->ctrg->ctr[PDP_CTR_BYTES_UDATA_OUT], len); - rate_ctr_inc(&mm->ctrg->ctr[GMM_CTR_PKTS_UDATA_OUT]); - rate_ctr_add(&mm->ctrg->ctr[GMM_CTR_BYTES_UDATA_OUT], len); - - /* It is easier to have a global count */ - pdp->cdr_bytes_out += len; - - return sndcp_unitdata_req(msg, &mm->gb.llme->lle[pdp->sapi], - pdp->nsapi, mm); -} - -/* Called by SNDCP when it has received/re-assembled a N-PDU */ -int sgsn_rx_sndcp_ud_ind(struct gprs_ra_id *ra_id, int32_t tlli, uint8_t nsapi, - struct msgb *msg, uint32_t npdu_len, uint8_t *npdu) -{ - struct sgsn_mm_ctx *mmctx; - struct sgsn_pdp_ctx *pdp; - - /* look-up the MM context for this message */ - mmctx = sgsn_mm_ctx_by_tlli(tlli, ra_id); - if (!mmctx) { - LOGP(DGPRS, LOGL_ERROR, - "Cannot find MM CTX for TLLI %08x\n", tlli); - return -EIO; - } - /* look-up the PDP context for this message */ - pdp = sgsn_pdp_ctx_by_nsapi(mmctx, nsapi); - if (!pdp) { - LOGP(DGPRS, LOGL_ERROR, "Cannot find PDP CTX for " - "TLLI=%08x, NSAPI=%u\n", tlli, nsapi); - return -EIO; - } - if (!pdp->lib) { - LOGP(DGPRS, LOGL_ERROR, "PDP CTX without libgtp\n"); - return -EIO; - } - - rate_ctr_inc(&pdp->ctrg->ctr[PDP_CTR_PKTS_UDATA_IN]); - rate_ctr_add(&pdp->ctrg->ctr[PDP_CTR_BYTES_UDATA_IN], npdu_len); - rate_ctr_inc(&mmctx->ctrg->ctr[GMM_CTR_PKTS_UDATA_IN]); - rate_ctr_add(&mmctx->ctrg->ctr[GMM_CTR_BYTES_UDATA_IN], npdu_len); - - /* It is easier to have a global count */ - pdp->cdr_bytes_in += npdu_len; - - return gtp_data_req(pdp->ggsn->gsn, pdp->lib, npdu, npdu_len); -} - -/* libgtp select loop integration */ -static int sgsn_gtp_fd_cb(struct osmo_fd *fd, unsigned int what) -{ - struct sgsn_instance *sgi = fd->data; - int rc; - - if (!(what & BSC_FD_READ)) - return 0; - - switch (fd->priv_nr) { - case 0: - rc = gtp_decaps0(sgi->gsn); - break; - case 1: - rc = gtp_decaps1c(sgi->gsn); - break; - case 2: - rc = gtp_decaps1u(sgi->gsn); - break; - default: - rc = -EINVAL; - break; - } - return rc; -} - -static void sgsn_gtp_tmr_start(struct sgsn_instance *sgi) -{ - struct timeval next; - - /* Retrieve next retransmission as struct timeval */ - gtp_retranstimeout(sgi->gsn, &next); - - /* re-schedule the timer */ - osmo_timer_schedule(&sgi->gtp_timer, next.tv_sec, next.tv_usec/1000); -} - -/* timer callback for libgtp retransmissions and ping */ -static void sgsn_gtp_tmr_cb(void *data) -{ - struct sgsn_instance *sgi = data; - - /* Do all the retransmissions as needed */ - gtp_retrans(sgi->gsn); - - sgsn_gtp_tmr_start(sgi); -} - -int sgsn_gtp_init(struct sgsn_instance *sgi) -{ - int rc; - struct gsn_t *gsn; - - rc = gtp_new(&sgi->gsn, sgi->cfg.gtp_statedir, - &sgi->cfg.gtp_listenaddr.sin_addr, GTP_MODE_SGSN); - if (rc) { - LOGP(DGPRS, LOGL_ERROR, "Failed to create GTP: %d\n", rc); - return rc; - } - gsn = sgi->gsn; - - sgi->gtp_fd0.fd = gsn->fd0; - sgi->gtp_fd0.priv_nr = 0; - sgi->gtp_fd0.data = sgi; - sgi->gtp_fd0.when = BSC_FD_READ; - sgi->gtp_fd0.cb = sgsn_gtp_fd_cb; - rc = osmo_fd_register(&sgi->gtp_fd0); - if (rc < 0) - return rc; - - sgi->gtp_fd1c.fd = gsn->fd1c; - sgi->gtp_fd1c.priv_nr = 1; - sgi->gtp_fd1c.data = sgi; - sgi->gtp_fd1c.when = BSC_FD_READ; - sgi->gtp_fd1c.cb = sgsn_gtp_fd_cb; - rc = osmo_fd_register(&sgi->gtp_fd1c); - if (rc < 0) { - osmo_fd_unregister(&sgi->gtp_fd0); - return rc; - } - - sgi->gtp_fd1u.fd = gsn->fd1u; - sgi->gtp_fd1u.priv_nr = 2; - sgi->gtp_fd1u.data = sgi; - sgi->gtp_fd1u.when = BSC_FD_READ; - sgi->gtp_fd1u.cb = sgsn_gtp_fd_cb; - rc = osmo_fd_register(&sgi->gtp_fd1u); - if (rc < 0) { - osmo_fd_unregister(&sgi->gtp_fd0); - osmo_fd_unregister(&sgi->gtp_fd1c); - return rc; - } - - /* Start GTP re-transmission timer */ - osmo_timer_setup(&sgi->gtp_timer, sgsn_gtp_tmr_cb, sgi); - sgsn_gtp_tmr_start(sgi); - - /* Register callbackcs with libgtp */ - gtp_set_cb_delete_context(gsn, cb_delete_context); - gtp_set_cb_conf(gsn, cb_conf); - gtp_set_cb_recovery(gsn, cb_recovery); - gtp_set_cb_data_ind(gsn, cb_data_ind); - gtp_set_cb_unsup_ind(gsn, cb_unsup_ind); - gtp_set_cb_extheader_ind(gsn, cb_extheader_ind); - - return 0; -} diff --git a/openbsc/src/gprs/sgsn_main.c b/openbsc/src/gprs/sgsn_main.c deleted file mode 100644 index 04f2825f..00000000 --- a/openbsc/src/gprs/sgsn_main.c +++ /dev/null @@ -1,462 +0,0 @@ -/* GPRS SGSN Implementation */ - -/* (C) 2010 by Harald Welte - * (C) 2010 by On-Waves - * All Rights Reserved - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation; either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - * - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include - -#include -#include - -#include -#include -#include -#include - -#include - -#include -#include -#include -#include -#include -#include -#include - -#include -#include - -#include - -#include "../../bscconfig.h" - -#define _GNU_SOURCE -#include - -void *tall_bsc_ctx; - -struct gprs_ns_inst *sgsn_nsi; -static int daemonize = 0; -const char *openbsc_copyright = - "Copyright (C) 2010 Harald Welte and On-Waves\r\n" - "License AGPLv3+: GNU AGPL version 3 or later \r\n" - "This is free software: you are free to change and redistribute it.\r\n" - "There is NO WARRANTY, to the extent permitted by law.\r\n"; - -static struct sgsn_instance sgsn_inst = { - .config_file = "osmo_sgsn.cfg", - .cfg = { - .gtp_statedir = "./", - .auth_policy = SGSN_AUTH_POLICY_CLOSED, - }, -}; -struct sgsn_instance *sgsn = &sgsn_inst; - -/* call-back function for the NS protocol */ -static int sgsn_ns_cb(enum gprs_ns_evt event, struct gprs_nsvc *nsvc, - struct msgb *msg, uint16_t bvci) -{ - int rc = 0; - - switch (event) { - case GPRS_NS_EVT_UNIT_DATA: - /* hand the message into the BSSGP implementation */ - rc = bssgp_rcvmsg(msg); - break; - default: - LOGP(DGPRS, LOGL_ERROR, "SGSN: Unknown event %u from NS\n", event); - if (msg) - msgb_free(msg); - rc = -EIO; - break; - } - return rc; -} - -/* call-back function for the BSSGP protocol */ -int bssgp_prim_cb(struct osmo_prim_hdr *oph, void *ctx) -{ - struct osmo_bssgp_prim *bp; - bp = container_of(oph, struct osmo_bssgp_prim, oph); - - switch (oph->sap) { - case SAP_BSSGP_LL: - switch (oph->primitive) { - case PRIM_BSSGP_UL_UD: - return gprs_llc_rcvmsg(oph->msg, bp->tp); - } - break; - case SAP_BSSGP_GMM: - switch (oph->primitive) { - case PRIM_BSSGP_GMM_SUSPEND: - return gprs_gmm_rx_suspend(bp->ra_id, bp->tlli); - case PRIM_BSSGP_GMM_RESUME: - return gprs_gmm_rx_resume(bp->ra_id, bp->tlli, - bp->u.resume.suspend_ref); - } - break; - case SAP_BSSGP_NM: - break; - } - return 0; -} - -static void signal_handler(int signal) -{ - fprintf(stdout, "signal %u received\n", signal); - - switch (signal) { - case SIGINT: - osmo_signal_dispatch(SS_L_GLOBAL, S_L_GLOBAL_SHUTDOWN, NULL); - sleep(1); - exit(0); - break; - case SIGABRT: - /* in case of abort, we want to obtain a talloc report - * and then return to the caller, who will abort the process */ - case SIGUSR1: - talloc_report(tall_vty_ctx, stderr); - talloc_report_full(tall_bsc_ctx, stderr); - break; - case SIGUSR2: - talloc_report_full(tall_vty_ctx, stderr); - break; - default: - break; - } -} - -/* NSI that BSSGP uses when transmitting on NS */ -extern struct gprs_ns_inst *bssgp_nsi; - -extern int bsc_vty_go_parent(struct vty *vty); - -static struct vty_app_info vty_info = { - .name = "OsmoSGSN", - .version = PACKAGE_VERSION, - .go_parent_cb = bsc_vty_go_parent, - .is_config_node = bsc_vty_is_config_node, -}; - -static void print_help(void) -{ - printf("Some useful help...\n"); - printf(" -h --help\tthis text\n"); - printf(" -D --daemonize\tFork the process into a background daemon\n"); - printf(" -d option --debug\tenable Debugging\n"); - printf(" -s --disable-color\n"); - printf(" -c --config-file\tThe config file to use [%s]\n", sgsn->config_file); - printf(" -e --log-level number\tSet a global log level\n"); -} - -static void handle_options(int argc, char **argv) -{ - while (1) { - int option_index = 0, c; - static struct option long_options[] = { - {"help", 0, 0, 'h'}, - {"debug", 1, 0, 'd'}, - {"daemonize", 0, 0, 'D'}, - {"config-file", 1, 0, 'c'}, - {"disable-color", 0, 0, 's'}, - {"timestamp", 0, 0, 'T'}, - { "version", 0, 0, 'V' }, - {"log-level", 1, 0, 'e'}, - {NULL, 0, 0, 0} - }; - - c = getopt_long(argc, argv, "hd:Dc:sTVe:", - long_options, &option_index); - if (c == -1) - break; - - switch (c) { - case 'h': - //print_usage(); - print_help(); - exit(0); - case 's': - log_set_use_color(osmo_stderr_target, 0); - break; - case 'd': - log_parse_category_mask(osmo_stderr_target, optarg); - break; - case 'D': - daemonize = 1; - break; - case 'c': - sgsn_inst.config_file = strdup(optarg); - break; - case 'T': - log_set_print_timestamp(osmo_stderr_target, 1); - break; - case 'V': - print_version(1); - exit(0); - break; - case 'e': - log_set_log_level(osmo_stderr_target, atoi(optarg)); - break; - default: - /* ignore */ - break; - } - } -} - -/* default categories */ -static struct log_info_cat gprs_categories[] = { - [DMM] = { - .name = "DMM", - .description = "Layer3 Mobility Management (MM)", - .color = "\033[1;33m", - .enabled = 1, .loglevel = LOGL_NOTICE, - }, - [DPAG] = { - .name = "DPAG", - .description = "Paging Subsystem", - .color = "\033[1;38m", - .enabled = 1, .loglevel = LOGL_NOTICE, - }, - [DMEAS] = { - .name = "DMEAS", - .description = "Radio Measurement Processing", - .enabled = 0, .loglevel = LOGL_NOTICE, - }, - [DREF] = { - .name = "DREF", - .description = "Reference Counting", - .enabled = 0, .loglevel = LOGL_NOTICE, - }, - [DGPRS] = { - .name = "DGPRS", - .description = "GPRS Packet Service", - .enabled = 1, .loglevel = LOGL_DEBUG, - }, - [DNS] = { - .name = "DNS", - .description = "GPRS Network Service (NS)", - .enabled = 1, .loglevel = LOGL_INFO, - }, - [DBSSGP] = { - .name = "DBSSGP", - .description = "GPRS BSS Gateway Protocol (BSSGP)", - .enabled = 1, .loglevel = LOGL_DEBUG, - }, - [DLLC] = { - .name = "DLLC", - .description = "GPRS Logical Link Control Protocol (LLC)", - .enabled = 1, .loglevel = LOGL_DEBUG, - }, - [DSNDCP] = { - .name = "DSNDCP", - .description = "GPRS Sub-Network Dependent Control Protocol (SNDCP)", - .enabled = 1, .loglevel = LOGL_DEBUG, - }, - [DRANAP] = { - .name = "DRANAP", - .description = "RAN Application Part (RANAP)", - .enabled = 1, .loglevel = LOGL_DEBUG, - }, - [DSUA] = { - .name = "DSUA", - .description = "SCCP User Adaptation (SUA)", - .enabled = 1, .loglevel = LOGL_DEBUG, - }, - [DSLHC] = { - .name = "DSLHC", - .description = "RFC1144 TCP/IP Header compression (SLHC)", - .enabled = 1, .loglevel = LOGL_DEBUG, - }, - [DV42BIS] = { - .name = "DV42BIS", - .description = "V.42bis data compression (SNDCP)", - .enabled = 1, .loglevel = LOGL_DEBUG, - } -}; - -static const struct log_info gprs_log_info = { - .filter_fn = gprs_log_filter_fn, - .cat = gprs_categories, - .num_cat = ARRAY_SIZE(gprs_categories), -}; - -/* Implement the extern asn_debug from libasn1c to indicate whether the ASN.1 - * binary code decoded and encoded during Iu communication should be logged to - * stderr. See osmocom's libasn1c, asn_internal.h, at "if (asn_debug)": - * http://git.osmocom.org/libasn1c/tree/include/asn1c/asn_internal.h */ -int asn_debug = 0; - -int sgsn_ranap_iu_event(struct ue_conn_ctx *ctx, enum iu_event_type type, void *data); - -int main(int argc, char **argv) -{ - struct ctrl_handle *ctrl; - struct gsm_network dummy_network; - int rc; - - srand(time(NULL)); - tall_bsc_ctx = talloc_named_const(NULL, 0, "osmo_sgsn"); - msgb_talloc_ctx_init(tall_bsc_ctx, 0); - - signal(SIGINT, &signal_handler); - signal(SIGABRT, &signal_handler); - signal(SIGUSR1, &signal_handler); - signal(SIGUSR2, &signal_handler); - - osmo_init_ignore_signals(); - osmo_init_logging(&gprs_log_info); - osmo_stats_init(tall_bsc_ctx); - - vty_info.copyright = openbsc_copyright; - vty_init(&vty_info); - logging_vty_add_cmds(NULL); - osmo_stats_vty_add_cmds(&gprs_log_info); - sgsn_vty_init(); - ctrl_vty_init(tall_bsc_ctx); -#ifdef BUILD_IU - iu_vty_init(&asn_debug); -#endif - - handle_options(argc, argv); - - rate_ctr_init(tall_bsc_ctx); - - gprs_ns_set_log_ss(DNS); - bssgp_set_log_ss(DBSSGP); - - sgsn_nsi = gprs_ns_instantiate(&sgsn_ns_cb, tall_bsc_ctx); - if (!sgsn_nsi) { - LOGP(DGPRS, LOGL_ERROR, "Unable to instantiate NS\n"); - exit(1); - } - bssgp_nsi = sgsn_inst.cfg.nsi = sgsn_nsi; - - gprs_llc_init("/usr/local/lib/osmocom/crypt/"); - sgsn_rate_ctr_init(); - sgsn_inst_init(); - - gprs_ns_vty_init(bssgp_nsi); - bssgp_vty_init(); - gprs_llc_vty_init(); - gprs_sndcp_vty_init(); - sgsn_auth_init(); - sgsn_cdr_init(&sgsn_inst); - /* FIXME: register signal handler for SS_L_NS */ - - rc = sgsn_parse_config(sgsn_inst.config_file, &sgsn_inst.cfg); - if (rc < 0) { - LOGP(DGPRS, LOGL_FATAL, "Error in config file\n"); - exit(2); - } - - /* start telnet after reading config for vty_get_bind_addr() */ - rc = telnet_init_dynif(tall_bsc_ctx, &dummy_network, - vty_get_bind_addr(), OSMO_VTY_PORT_SGSN); - if (rc < 0) - exit(1); - - /* start control interface after reading config for - * ctrl_vty_get_bind_addr() */ - ctrl = sgsn_controlif_setup(NULL, ctrl_vty_get_bind_addr(), - OSMO_CTRL_PORT_SGSN); - if (!ctrl) { - LOGP(DGPRS, LOGL_ERROR, "Failed to create CTRL interface.\n"); - exit(1); - } - - if (sgsn_ctrl_cmds_install() != 0) { - LOGP(DGPRS, LOGL_ERROR, "Failed to install CTRL commands.\n"); - exit(1); - } - - - rc = sgsn_gtp_init(&sgsn_inst); - if (rc) { - LOGP(DGPRS, LOGL_FATAL, "Cannot bind/listen on GTP socket\n"); - exit(2); - } - - rc = gprs_subscr_init(&sgsn_inst); - if (rc < 0) { - LOGP(DGPRS, LOGL_FATAL, "Cannot set up subscriber management\n"); - exit(2); - } - - rc = gprs_ns_nsip_listen(sgsn_nsi); - if (rc < 0) { - LOGP(DGPRS, LOGL_FATAL, "Cannot bind/listen on NSIP socket\n"); - exit(2); - } - - rc = gprs_ns_frgre_listen(sgsn_nsi); - if (rc < 0) { - LOGP(DGPRS, LOGL_FATAL, "Cannot bind/listen GRE " - "socket. Do you have CAP_NET_RAW?\n"); - exit(2); - } - - if (sgsn->cfg.dynamic_lookup) { - if (sgsn_ares_init(sgsn) != 0) { - LOGP(DGPRS, LOGL_FATAL, - "Failed to initialize c-ares(%d)\n", rc); - exit(4); - } - } - -#ifdef BUILD_IU - iu_init(tall_bsc_ctx, "127.0.0.2", 14001, gsm0408_gprs_rcvmsg_iu, sgsn_ranap_iu_event); -#endif - - if (daemonize) { - rc = osmo_daemonize(); - if (rc < 0) { - perror("Error during daemonize"); - exit(1); - } - } - - while (1) { - rc = osmo_select_main(0); - if (rc < 0) - exit(3); - } - - /* not reached */ - exit(0); -} diff --git a/openbsc/src/gprs/sgsn_vty.c b/openbsc/src/gprs/sgsn_vty.c deleted file mode 100644 index e09a0296..00000000 --- a/openbsc/src/gprs/sgsn_vty.c +++ /dev/null @@ -1,1323 +0,0 @@ -/* - * (C) 2010-2016 by Harald Welte - * (C) 2010 by On-Waves - * (C) 2015 by Holger Hans Peter Freyther - * All Rights Reserved - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation; either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - * - */ - -#include -#include -#include -#include - -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include - -#include - -static struct sgsn_config *g_cfg = NULL; - -const struct value_string sgsn_auth_pol_strs[] = { - { SGSN_AUTH_POLICY_OPEN, "accept-all" }, - { SGSN_AUTH_POLICY_CLOSED, "closed" }, - { SGSN_AUTH_POLICY_ACL_ONLY, "acl-only" }, - { SGSN_AUTH_POLICY_REMOTE, "remote" }, - { 0, NULL } -}; - -/* Section 11.2.2 / Table 11.3a GPRS Mobility management timers – MS side */ -#define GSM0408_T3312_SECS (10*60) /* periodic RAU interval, default 54min */ - -/* Section 11.2.2 / Table 11.4 MM timers netwokr side */ -#define GSM0408_T3322_SECS 6 /* DETACH_REQ -> DETACH_ACC */ -#define GSM0408_T3350_SECS 6 /* waiting for ATT/RAU/TMSI COMPL */ -#define GSM0408_T3360_SECS 6 /* waiting for AUTH/CIPH RESP */ -#define GSM0408_T3370_SECS 6 /* waiting for ID RESP */ - -/* Section 11.2.2 / Table 11.4a MM timers network side */ -#define GSM0408_T3313_SECS 30 /* waiting for paging response */ -#define GSM0408_T3314_SECS 44 /* force to STBY on expiry, Ready timer */ -#define GSM0408_T3316_SECS 44 - -/* Section 11.3 / Table 11.2d Timers of Session Management - network side */ -#define GSM0408_T3385_SECS 8 /* wait for ACT PDP CTX REQ */ -#define GSM0408_T3386_SECS 8 /* wait for MODIFY PDP CTX ACK */ -#define GSM0408_T3395_SECS 8 /* wait for DEACT PDP CTX ACK */ -#define GSM0408_T3397_SECS 8 /* wait for DEACT AA PDP CTX ACK */ - -#define DECLARE_TIMER(number, doc) \ - DEFUN(cfg_sgsn_T##number, \ - cfg_sgsn_T##number##_cmd, \ - "timer t" #number " <0-65535>", \ - "Configure GPRS Timers\n" \ - doc "\nTimer Value in seconds\n") \ -{ \ - int value = atoi(argv[0]); \ - \ - if (value < 0 || value > 65535) { \ - vty_out(vty, "Timer value %s out of range.%s", \ - argv[0], VTY_NEWLINE); \ - return CMD_WARNING; \ - } \ - \ - g_cfg->timers.T##number = value; \ - return CMD_SUCCESS; \ -} - -DECLARE_TIMER(3312, "Periodic RA Update timer (s)") -DECLARE_TIMER(3322, "Detach request -> accept timer (s)") -DECLARE_TIMER(3350, "Waiting for ATT/RAU/TMSI_COMPL timer (s)") -DECLARE_TIMER(3360, "Waiting for AUTH/CIPH response timer (s)") -DECLARE_TIMER(3370, "Waiting for IDENTITY response timer (s)") - -DECLARE_TIMER(3313, "Waiting for paging response timer (s)") -DECLARE_TIMER(3314, "Force to STANDBY on expiry timer (s)") -DECLARE_TIMER(3316, "AA-Ready timer (s)") - -DECLARE_TIMER(3385, "Wait for ACT PDP CTX REQ timer (s)") -DECLARE_TIMER(3386, "Wait for MODIFY PDP CTX ACK timer (s)") -DECLARE_TIMER(3395, "Wait for DEACT PDP CTX ACK timer (s)") -DECLARE_TIMER(3397, "Wait for DEACT AA PDP CTX ACK timer (s)") - - -#define GSM48_MAX_APN_LEN 102 /* 10.5.6.1 */ -/* TODO: consolidate with gprs_apn_to_str(). */ -/** Copy apn to a static buffer, replacing the length octets in apn_enc with '.' - * and terminating with a '\0'. Return the static buffer. - * len: the length of the encoded APN (which has no terminating zero). - */ -static char *gprs_apn2str(uint8_t *apn, unsigned int len) -{ - static char apnbuf[GSM48_MAX_APN_LEN+1]; - unsigned int i = 0; - - if (!apn) - return ""; - - if (len > sizeof(apnbuf)-1) - len = sizeof(apnbuf)-1; - - memcpy(apnbuf, apn, len); - apnbuf[len] = '\0'; - - /* replace the domain name step sizes with dots */ - while (i < len) { - unsigned int step = apnbuf[i]; - apnbuf[i] = '.'; - i += step+1; - } - - return apnbuf+1; -} - -char *gprs_pdpaddr2str(uint8_t *pdpa, uint8_t len) -{ - static char str[INET6_ADDRSTRLEN + 10]; - - if (!pdpa || len < 2) - return "none"; - - switch (pdpa[0] & 0x0f) { - case PDP_TYPE_ORG_IETF: - switch (pdpa[1]) { - case PDP_TYPE_N_IETF_IPv4: - if (len < 2 + 4) - break; - strcpy(str, "IPv4 "); - inet_ntop(AF_INET, pdpa+2, str+5, sizeof(str)-5); - return str; - case PDP_TYPE_N_IETF_IPv6: - if (len < 2 + 8) - break; - strcpy(str, "IPv6 "); - inet_ntop(AF_INET6, pdpa+2, str+5, sizeof(str)-5); - return str; - default: - break; - } - break; - case PDP_TYPE_ORG_ETSI: - if (pdpa[1] == PDP_TYPE_N_ETSI_PPP) - return "PPP"; - break; - default: - break; - } - - return "invalid"; -} - -static struct cmd_node sgsn_node = { - SGSN_NODE, - "%s(config-sgsn)# ", - 1, -}; - -static int config_write_sgsn(struct vty *vty) -{ - struct sgsn_ggsn_ctx *gctx; - struct imsi_acl_entry *acl; - struct apn_ctx *actx; - struct ares_addr_node *server; - - vty_out(vty, "sgsn%s", VTY_NEWLINE); - - vty_out(vty, " gtp local-ip %s%s", - inet_ntoa(g_cfg->gtp_listenaddr.sin_addr), VTY_NEWLINE); - - llist_for_each_entry(gctx, &sgsn_ggsn_ctxts, list) { - if (gctx->id == UINT32_MAX) - continue; - - vty_out(vty, " ggsn %u remote-ip %s%s", gctx->id, - inet_ntoa(gctx->remote_addr), VTY_NEWLINE); - vty_out(vty, " ggsn %u gtp-version %u%s", gctx->id, - gctx->gtp_version, VTY_NEWLINE); - } - - if (sgsn->cfg.dynamic_lookup) - vty_out(vty, " ggsn dynamic%s", VTY_NEWLINE); - - for (server = sgsn->ares_servers; server; server = server->next) - vty_out(vty, " grx-dns-add %s%s", inet_ntoa(server->addr.addr4), VTY_NEWLINE); - - if (g_cfg->cipher != GPRS_ALGO_GEA0) - vty_out(vty, " encryption %s%s", - get_value_string(gprs_cipher_names, g_cfg->cipher), - VTY_NEWLINE); - if (g_cfg->gsup_server_addr.sin_addr.s_addr) - vty_out(vty, " gsup remote-ip %s%s", - inet_ntoa(g_cfg->gsup_server_addr.sin_addr), VTY_NEWLINE); - if (g_cfg->gsup_server_port) - vty_out(vty, " gsup remote-port %d%s", - g_cfg->gsup_server_port, VTY_NEWLINE); - vty_out(vty, " auth-policy %s%s", - get_value_string(sgsn_auth_pol_strs, g_cfg->auth_policy), - VTY_NEWLINE); - - vty_out(vty, " gsup oap-id %d%s", - (int)g_cfg->oap.client_id, VTY_NEWLINE); - if (g_cfg->oap.secret_k_present != 0) - vty_out(vty, " gsup oap-k %s%s", - osmo_hexdump_nospc(g_cfg->oap.secret_k, sizeof(g_cfg->oap.secret_k)), - VTY_NEWLINE); - if (g_cfg->oap.secret_opc_present != 0) - vty_out(vty, " gsup oap-opc %s%s", - osmo_hexdump_nospc(g_cfg->oap.secret_opc, sizeof(g_cfg->oap.secret_opc)), - VTY_NEWLINE); - - llist_for_each_entry(acl, &g_cfg->imsi_acl, list) - vty_out(vty, " imsi-acl add %s%s", acl->imsi, VTY_NEWLINE); - - if (llist_empty(&sgsn_apn_ctxts)) - vty_out(vty, " ! apn * ggsn 0%s", VTY_NEWLINE); - llist_for_each_entry(actx, &sgsn_apn_ctxts, list) { - if (strlen(actx->imsi_prefix) > 0) - vty_out(vty, " apn %s imsi-prefix %s ggsn %u%s", - actx->name, actx->imsi_prefix, actx->ggsn->id, - VTY_NEWLINE); - else - vty_out(vty, " apn %s ggsn %u%s", actx->name, - actx->ggsn->id, VTY_NEWLINE); - } - - if (g_cfg->cdr.filename) - vty_out(vty, " cdr filename %s%s", g_cfg->cdr.filename, VTY_NEWLINE); - else - vty_out(vty, " no cdr filename%s", VTY_NEWLINE); - vty_out(vty, " cdr interval %d%s", g_cfg->cdr.interval, VTY_NEWLINE); - - vty_out(vty, " timer t3312 %d%s", g_cfg->timers.T3312, VTY_NEWLINE); - vty_out(vty, " timer t3322 %d%s", g_cfg->timers.T3322, VTY_NEWLINE); - vty_out(vty, " timer t3350 %d%s", g_cfg->timers.T3350, VTY_NEWLINE); - vty_out(vty, " timer t3360 %d%s", g_cfg->timers.T3360, VTY_NEWLINE); - vty_out(vty, " timer t3370 %d%s", g_cfg->timers.T3370, VTY_NEWLINE); - vty_out(vty, " timer t3313 %d%s", g_cfg->timers.T3313, VTY_NEWLINE); - vty_out(vty, " timer t3314 %d%s", g_cfg->timers.T3314, VTY_NEWLINE); - vty_out(vty, " timer t3316 %d%s", g_cfg->timers.T3316, VTY_NEWLINE); - vty_out(vty, " timer t3385 %d%s", g_cfg->timers.T3385, VTY_NEWLINE); - vty_out(vty, " timer t3386 %d%s", g_cfg->timers.T3386, VTY_NEWLINE); - vty_out(vty, " timer t3395 %d%s", g_cfg->timers.T3395, VTY_NEWLINE); - vty_out(vty, " timer t3397 %d%s", g_cfg->timers.T3397, VTY_NEWLINE); - - if (g_cfg->pcomp_rfc1144.active) { - vty_out(vty, " compression rfc1144 active slots %d%s", - g_cfg->pcomp_rfc1144.s01 + 1, VTY_NEWLINE); - } else if (g_cfg->pcomp_rfc1144.passive) { - vty_out(vty, " compression rfc1144 passive%s", VTY_NEWLINE); - } else - vty_out(vty, " no compression rfc1144%s", VTY_NEWLINE); - - if (g_cfg->dcomp_v42bis.active && g_cfg->dcomp_v42bis.p0 == 1) { - vty_out(vty, - " compression v42bis active direction sgsn codewords %d strlen %d%s", - g_cfg->dcomp_v42bis.p1, g_cfg->dcomp_v42bis.p2, - VTY_NEWLINE); - } else if (g_cfg->dcomp_v42bis.active && g_cfg->dcomp_v42bis.p0 == 2) { - vty_out(vty, - " compression v42bis active direction ms codewords %d strlen %d%s", - g_cfg->dcomp_v42bis.p1, g_cfg->dcomp_v42bis.p2, - VTY_NEWLINE); - } else if (g_cfg->dcomp_v42bis.active && g_cfg->dcomp_v42bis.p0 == 3) { - vty_out(vty, - " compression v42bis active direction both codewords %d strlen %d%s", - g_cfg->dcomp_v42bis.p1, g_cfg->dcomp_v42bis.p2, - VTY_NEWLINE); - } else if (g_cfg->dcomp_v42bis.passive) { - vty_out(vty, " compression v42bis passive%s", VTY_NEWLINE); - } else - vty_out(vty, " no compression v42bis%s", VTY_NEWLINE); - - return CMD_SUCCESS; -} - -#define SGSN_STR "Configure the SGSN\n" -#define GGSN_STR "Configure the GGSN information\n" - -DEFUN(cfg_sgsn, cfg_sgsn_cmd, - "sgsn", - SGSN_STR) -{ - vty->node = SGSN_NODE; - return CMD_SUCCESS; -} - -DEFUN(cfg_sgsn_bind_addr, cfg_sgsn_bind_addr_cmd, - "gtp local-ip A.B.C.D", - "GTP Parameters\n" - "Set the IP address for the local GTP bind\n" - "IPv4 Address\n") -{ - inet_aton(argv[0], &g_cfg->gtp_listenaddr.sin_addr); - - return CMD_SUCCESS; -} - -DEFUN(cfg_ggsn_remote_ip, cfg_ggsn_remote_ip_cmd, - "ggsn <0-255> remote-ip A.B.C.D", - GGSN_STR "GGSN Number\n" IP_STR "IPv4 Address\n") -{ - uint32_t id = atoi(argv[0]); - struct sgsn_ggsn_ctx *ggc = sgsn_ggsn_ctx_find_alloc(id); - - inet_aton(argv[1], &ggc->remote_addr); - - return CMD_SUCCESS; -} - -#if 0 -DEFUN(cfg_ggsn_remote_port, cfg_ggsn_remote_port_cmd, - "ggsn <0-255> remote-port <0-65535>", - "") -{ - uint32_t id = atoi(argv[0]); - struct sgsn_ggsn_ctx *ggc = sgsn_ggsn_ctx_find_alloc(id); - uint16_t port = atoi(argv[1]); - -} -#endif - -DEFUN(cfg_ggsn_gtp_version, cfg_ggsn_gtp_version_cmd, - "ggsn <0-255> gtp-version (0|1)", - GGSN_STR "GGSN Number\n" "GTP Version\n" - "Version 0\n" "Version 1\n") -{ - uint32_t id = atoi(argv[0]); - struct sgsn_ggsn_ctx *ggc = sgsn_ggsn_ctx_find_alloc(id); - - if (atoi(argv[1])) - ggc->gtp_version = 1; - else - ggc->gtp_version = 0; - - return CMD_SUCCESS; -} - -DEFUN(cfg_ggsn_dynamic_lookup, cfg_ggsn_dynamic_lookup_cmd, - "ggsn dynamic", - GGSN_STR "Enable dynamic GRX based look-up (requires restart)\n") -{ - sgsn->cfg.dynamic_lookup = 1; - return CMD_SUCCESS; -} - -DEFUN(cfg_grx_ggsn, cfg_grx_ggsn_cmd, - "grx-dns-add A.B.C.D", - "Add DNS server\nIPv4 address\n") -{ - struct ares_addr_node *node = talloc_zero(tall_bsc_ctx, struct ares_addr_node); - node->family = AF_INET; - inet_aton(argv[0], &node->addr.addr4); - - node->next = sgsn->ares_servers; - sgsn->ares_servers = node; - return CMD_SUCCESS; -} - -#define APN_STR "Configure the information per APN\n" -#define APN_GW_STR "The APN gateway name optionally prefixed by '*' (wildcard)\n" - -static int add_apn_ggsn_mapping(struct vty *vty, const char *apn_str, - const char *imsi_prefix, int ggsn_id) -{ - struct apn_ctx *actx; - struct sgsn_ggsn_ctx *ggsn; - - ggsn = sgsn_ggsn_ctx_by_id(ggsn_id); - if (ggsn == NULL) { - vty_out(vty, "%% a GGSN with id %d has not been defined%s", - ggsn_id, VTY_NEWLINE); - return CMD_WARNING; - } - - actx = sgsn_apn_ctx_find_alloc(apn_str, imsi_prefix); - if (!actx) { - vty_out(vty, "%% unable to create APN context for %s/%s%s", - apn_str, imsi_prefix, VTY_NEWLINE); - return CMD_WARNING; - } - - actx->ggsn = ggsn; - - return CMD_SUCCESS; -} - -DEFUN(cfg_apn_ggsn, cfg_apn_ggsn_cmd, - "apn APNAME ggsn <0-255>", - APN_STR APN_GW_STR - "Select the GGSN to use when the APN gateway prefix matches\n" - "The GGSN id") -{ - - return add_apn_ggsn_mapping(vty, argv[0], "", atoi(argv[1])); -} - -DEFUN(cfg_apn_imsi_ggsn, cfg_apn_imsi_ggsn_cmd, - "apn APNAME imsi-prefix IMSIPRE ggsn <0-255>", - APN_STR APN_GW_STR - "Restrict rule to a certain IMSI prefix\n" - "An IMSI prefix\n" - "Select the GGSN to use when APN gateway and IMSI prefix match\n" - "The GGSN id") -{ - - return add_apn_ggsn_mapping(vty, argv[0], argv[1], atoi(argv[2])); -} - -const struct value_string gprs_mm_st_strs[] = { - { GMM_DEREGISTERED, "DEREGISTERED" }, - { GMM_COMMON_PROC_INIT, "COMMON PROCEDURE (INIT)" }, - { GMM_REGISTERED_NORMAL, "REGISTERED (NORMAL)" }, - { GMM_REGISTERED_SUSPENDED, "REGISTERED (SUSPENDED)" }, - { GMM_DEREGISTERED_INIT, "DEREGISTERED (INIT)" }, - { 0, NULL } -}; - -static char *gtp_ntoa(struct ul16_t *ul) -{ - if (ul->l == 4) { - struct in_addr *ia = (struct in_addr *) ul; - return inet_ntoa(*ia); - } else { - return "UNKNOWN"; - } -} - -static void vty_dump_pdp(struct vty *vty, const char *pfx, - struct sgsn_pdp_ctx *pdp) -{ - const char *imsi = pdp->mm ? pdp->mm->imsi : "(detaching)"; - vty_out(vty, "%sPDP Context IMSI: %s, SAPI: %u, NSAPI: %u, TI: %u%s", - pfx, imsi, pdp->sapi, pdp->nsapi, pdp->ti, VTY_NEWLINE); - vty_out(vty, "%s APN: %s%s", pfx, - gprs_apn2str(pdp->lib->apn_use.v, pdp->lib->apn_use.l), - VTY_NEWLINE); - vty_out(vty, "%s PDP Address: %s%s", pfx, - gprs_pdpaddr2str(pdp->lib->eua.v, pdp->lib->eua.l), - VTY_NEWLINE); - vty_out(vty, "%s GTP Local Control(%s / TEIC: 0x%08x) ", pfx, - gtp_ntoa(&pdp->lib->gsnlc), pdp->lib->teic_own); - vty_out(vty, "Data(%s / TEID: 0x%08x)%s", - gtp_ntoa(&pdp->lib->gsnlu), pdp->lib->teid_own, VTY_NEWLINE); - vty_out(vty, "%s GTP Remote Control(%s / TEIC: 0x%08x) ", pfx, - gtp_ntoa(&pdp->lib->gsnrc), pdp->lib->teic_gn); - vty_out(vty, "Data(%s / TEID: 0x%08x)%s", - gtp_ntoa(&pdp->lib->gsnru), pdp->lib->teid_gn, VTY_NEWLINE); - - vty_out_rate_ctr_group(vty, " ", pdp->ctrg); -} - -static void vty_dump_mmctx(struct vty *vty, const char *pfx, - struct sgsn_mm_ctx *mm, int pdp) -{ - vty_out(vty, "%sMM Context for IMSI %s, IMEI %s, P-TMSI %08x%s", - pfx, mm->imsi, mm->imei, mm->p_tmsi, VTY_NEWLINE); - vty_out(vty, "%s MSISDN: %s, TLLI: %08x%s HLR: %s", - pfx, mm->msisdn, mm->gb.tlli, mm->hlr, VTY_NEWLINE); - vty_out(vty, "%s MM State: %s, Routeing Area: %u-%u-%u-%u, " - "Cell ID: %u%s", pfx, - get_value_string(gprs_mm_st_strs, mm->gmm_state), - mm->ra.mcc, mm->ra.mnc, mm->ra.lac, mm->ra.rac, - mm->gb.cell_id, VTY_NEWLINE); - - vty_out_rate_ctr_group(vty, " ", mm->ctrg); - - if (pdp) { - struct sgsn_pdp_ctx *pdp; - - llist_for_each_entry(pdp, &mm->pdp_list, list) - vty_dump_pdp(vty, " ", pdp); - } -} - -DEFUN(show_sgsn, show_sgsn_cmd, "show sgsn", - SHOW_STR "Display information about the SGSN") -{ - if (sgsn->gsup_client) { - struct ipa_client_conn *link = sgsn->gsup_client->link; - vty_out(vty, - " Remote authorization: %sconnected to %s:%d via GSUP%s", - sgsn->gsup_client->is_connected ? "" : "not ", - link->addr, link->port, - VTY_NEWLINE); - } - /* FIXME: statistics */ - return CMD_SUCCESS; -} - -#define MMCTX_STR "MM Context\n" -#define INCLUDE_PDP_STR "Include PDP Context Information\n" - -#if 0 -DEFUN(show_mmctx_tlli, show_mmctx_tlli_cmd, - "show mm-context tlli HEX [pdp]", - SHOW_STR MMCTX_STR "Identify by TLLI\n" "TLLI\n" INCLUDE_PDP_STR) -{ - uint32_t tlli; - struct sgsn_mm_ctx *mm; - - tlli = strtoul(argv[0], NULL, 16); - mm = sgsn_mm_ctx_by_tlli(tlli); - if (!mm) { - vty_out(vty, "No MM context for TLLI %08x%s", - tlli, VTY_NEWLINE); - return CMD_WARNING; - } - vty_dump_mmctx(vty, "", mm, argv[1] ? 1 : 0); - return CMD_SUCCESS; -} -#endif - -DEFUN(swow_mmctx_imsi, show_mmctx_imsi_cmd, - "show mm-context imsi IMSI [pdp]", - SHOW_STR MMCTX_STR "Identify by IMSI\n" "IMSI of the MM Context\n" - INCLUDE_PDP_STR) -{ - struct sgsn_mm_ctx *mm; - - mm = sgsn_mm_ctx_by_imsi(argv[0]); - if (!mm) { - vty_out(vty, "No MM context for IMSI %s%s", - argv[0], VTY_NEWLINE); - return CMD_WARNING; - } - vty_dump_mmctx(vty, "", mm, argv[1] ? 1 : 0); - return CMD_SUCCESS; -} - -DEFUN(swow_mmctx_all, show_mmctx_all_cmd, - "show mm-context all [pdp]", - SHOW_STR MMCTX_STR "All MM Contexts\n" INCLUDE_PDP_STR) -{ - struct sgsn_mm_ctx *mm; - - llist_for_each_entry(mm, &sgsn_mm_ctxts, list) - vty_dump_mmctx(vty, "", mm, argv[0] ? 1 : 0); - - return CMD_SUCCESS; -} - -DEFUN(show_pdpctx_all, show_pdpctx_all_cmd, - "show pdp-context all", - SHOW_STR "Display information on PDP Context\n" "Show everything\n") -{ - struct sgsn_pdp_ctx *pdp; - - llist_for_each_entry(pdp, &sgsn_pdp_ctxts, g_list) - vty_dump_pdp(vty, "", pdp); - - return CMD_SUCCESS; -} - - -DEFUN(imsi_acl, cfg_imsi_acl_cmd, - "imsi-acl (add|del) IMSI", - "Access Control List of foreign IMSIs\n" - "Add IMSI to ACL\n" - "Remove IMSI from ACL\n" - "IMSI of subscriber\n") -{ - char imsi_sanitized[GSM23003_IMSI_MAX_DIGITS+1]; - const char *op = argv[0]; - const char *imsi = imsi_sanitized; - int rc; - - /* Sanitize IMSI */ - if (strlen(argv[1]) > GSM23003_IMSI_MAX_DIGITS) { - vty_out(vty, "%% IMSI (%s) too long -- ignored!%s", - argv[1], VTY_NEWLINE); - return CMD_WARNING; - } - memset(imsi_sanitized, '0', sizeof(imsi_sanitized)); - strcpy(imsi_sanitized+GSM23003_IMSI_MAX_DIGITS-strlen(argv[1]),argv[1]); - - if (!strcmp(op, "add")) - rc = sgsn_acl_add(imsi, g_cfg); - else - rc = sgsn_acl_del(imsi, g_cfg); - - if (rc < 0) { - vty_out(vty, "%% unable to %s ACL%s", op, VTY_NEWLINE); - return CMD_WARNING; - } - - return CMD_SUCCESS; -} - -DEFUN(cfg_encrypt, cfg_encrypt_cmd, - "encryption (GEA0|GEA1|GEA2|GEA3|GEA4)", - "Set encryption algorithm for SGSN\n" - "Use GEA0 (no encryption)\n" - "Use GEA1\nUse GEA2\nUse GEA3\nUse GEA4\n") -{ - enum gprs_ciph_algo c = get_string_value(gprs_cipher_names, argv[0]); - if (c != GPRS_ALGO_GEA0) { - if (!gprs_cipher_supported(c)) { - vty_out(vty, "%% cipher %s is unsupported in current version%s", argv[0], VTY_NEWLINE); - return CMD_WARNING; - } - - if (!g_cfg->require_authentication) { - vty_out(vty, "%% unable to use encryption %s without authentication: please adjust auth-policy%s", - argv[0], VTY_NEWLINE); - return CMD_WARNING; - } - } - - g_cfg->cipher = c; - - return CMD_SUCCESS; -} - -DEFUN(cfg_auth_policy, cfg_auth_policy_cmd, - "auth-policy (accept-all|closed|acl-only|remote)", - "Autorization Policy of SGSN\n" - "Accept all IMSIs (DANGEROUS)\n" - "Accept only home network subscribers or those in the ACL\n" - "Accept only subscribers in the ACL\n" - "Use remote subscription data only (HLR)\n") -{ - int val = get_string_value(sgsn_auth_pol_strs, argv[0]); - OSMO_ASSERT(val >= SGSN_AUTH_POLICY_OPEN && val <= SGSN_AUTH_POLICY_REMOTE); - g_cfg->auth_policy = val; - g_cfg->require_authentication = (val == SGSN_AUTH_POLICY_REMOTE); - g_cfg->require_update_location = (val == SGSN_AUTH_POLICY_REMOTE); - - return CMD_SUCCESS; -} - -/* Subscriber */ -#include - -static void subscr_dump_full_vty(struct vty *vty, struct gprs_subscr *gsub, int pending) -{ -#if 0 - char expire_time[200]; -#endif - struct gsm_auth_tuple *at; - int at_idx; - struct sgsn_subscriber_pdp_data *pdp; - - vty_out(vty, " Authorized: %d%s", - gsub->authorized, VTY_NEWLINE); - vty_out(vty, " LAC: %d/0x%x%s", - gsub->lac, gsub->lac, VTY_NEWLINE); - vty_out(vty, " IMSI: %s%s", gsub->imsi, VTY_NEWLINE); - if (gsub->tmsi != GSM_RESERVED_TMSI) - vty_out(vty, " TMSI: %08X%s", gsub->tmsi, - VTY_NEWLINE); - if (gsub->sgsn_data->msisdn_len > 0) - vty_out(vty, " MSISDN (BCD): %s%s", - osmo_hexdump(gsub->sgsn_data->msisdn, - gsub->sgsn_data->msisdn_len), - VTY_NEWLINE); - - if (strlen(gsub->imei) > 0) - vty_out(vty, " IMEI: %s%s", gsub->imei, VTY_NEWLINE); - - for (at_idx = 0; at_idx < ARRAY_SIZE(gsub->sgsn_data->auth_triplets); - at_idx++) { - at = &gsub->sgsn_data->auth_triplets[at_idx]; - if (at->key_seq == GSM_KEY_SEQ_INVAL) - continue; - - vty_out(vty, " A3A8 tuple (used %d times): ", - at->use_count); - vty_out(vty, " CKSN: %d, ", - at->key_seq); - if (at->vec.auth_types & OSMO_AUTH_TYPE_GSM) { - vty_out(vty, "RAND: %s, ", - osmo_hexdump(at->vec.rand, - sizeof(at->vec.rand))); - vty_out(vty, "SRES: %s, ", - osmo_hexdump(at->vec.sres, - sizeof(at->vec.sres))); - vty_out(vty, "Kc: %s%s", - osmo_hexdump(at->vec.kc, - sizeof(at->vec.kc)), VTY_NEWLINE); - } - if (at->vec.auth_types & OSMO_AUTH_TYPE_UMTS) { - vty_out(vty, " AUTN: %s, ", - osmo_hexdump(at->vec.autn, - sizeof(at->vec.autn))); - vty_out(vty, "RES: %s, ", - osmo_hexdump(at->vec.res, at->vec.res_len)); - vty_out(vty, "IK: %s, ", - osmo_hexdump(at->vec.ik, sizeof(at->vec.ik))); - vty_out(vty, "CK: %s, ", - osmo_hexdump(at->vec.ck, sizeof(at->vec.ck))); - } - } - - llist_for_each_entry(pdp, &gsub->sgsn_data->pdp_list, list) { - vty_out(vty, " PDP info: Id: %d, Type: 0x%04x, APN: '%s' QoS: %s%s", - pdp->context_id, pdp->pdp_type, pdp->apn_str, - osmo_hexdump(pdp->qos_subscribed, pdp->qos_subscribed_len), - VTY_NEWLINE); - } - -#if 0 - /* print the expiration time of a subscriber */ - if (gsub->expire_lu) { - strftime(expire_time, sizeof(expire_time), - "%a, %d %b %Y %T %z", localtime(&gsub->expire_lu)); - expire_time[sizeof(expire_time) - 1] = '\0'; - vty_out(vty, " Expiration Time: %s%s", expire_time, VTY_NEWLINE); - } -#endif - - if (gsub->flags) - vty_out(vty, " Flags: %s%s%s%s%s%s", - gsub->flags & GPRS_SUBSCRIBER_FIRST_CONTACT ? - "FIRST_CONTACT " : "", - gsub->flags & GPRS_SUBSCRIBER_CANCELLED ? - "CANCELLED " : "", - gsub->flags & GPRS_SUBSCRIBER_UPDATE_LOCATION_PENDING ? - "UPDATE_LOCATION_PENDING " : "", - gsub->flags & GPRS_SUBSCRIBER_UPDATE_AUTH_INFO_PENDING ? - "AUTH_INFO_PENDING " : "", - gsub->flags & GPRS_SUBSCRIBER_ENABLE_PURGE ? - "ENABLE_PURGE " : "", - VTY_NEWLINE); - - vty_out(vty, " Use count: %u%s", gsub->use_count, VTY_NEWLINE); -} - -DEFUN(show_subscr_cache, - show_subscr_cache_cmd, - "show subscriber cache", - SHOW_STR "Show information about subscribers\n" - "Display contents of subscriber cache\n") -{ - struct gprs_subscr *subscr; - - llist_for_each_entry(subscr, gprs_subscribers, entry) { - vty_out(vty, " Subscriber:%s", VTY_NEWLINE); - subscr_dump_full_vty(vty, subscr, 0); - } - - return CMD_SUCCESS; -} - -#define UPDATE_SUBSCR_STR "update-subscriber imsi IMSI " -#define UPDATE_SUBSCR_HELP "Update subscriber list\n" \ - "Use the IMSI to select the subscriber\n" \ - "The IMSI\n" - -#define UPDATE_SUBSCR_INSERT_HELP "Insert data into the subscriber record\n" - -DEFUN(update_subscr_insert_auth_triplet, update_subscr_insert_auth_triplet_cmd, - UPDATE_SUBSCR_STR "insert auth-triplet <1-5> sres SRES rand RAND kc KC", - UPDATE_SUBSCR_HELP - UPDATE_SUBSCR_INSERT_HELP - "Update authentication triplet\n" - "Triplet index\n" - "Set SRES value\nSRES value (4 byte) in hex\n" - "Set RAND value\nRAND value (16 byte) in hex\n" - "Set Kc value\nKc value (8 byte) in hex\n") -{ - const char *imsi = argv[0]; - const int cksn = atoi(argv[1]) - 1; - const char *sres_str = argv[2]; - const char *rand_str = argv[3]; - const char *kc_str = argv[4]; - struct gsm_auth_tuple at = {0,}; - - struct gprs_subscr *subscr; - - subscr = gprs_subscr_get_by_imsi(imsi); - if (!subscr) { - vty_out(vty, "%% unable get subscriber record for %s%s", - imsi, VTY_NEWLINE); - return CMD_WARNING; - } - - OSMO_ASSERT(subscr->sgsn_data); - - if (osmo_hexparse(sres_str, &at.vec.sres[0], sizeof(at.vec.sres)) < 0) { - vty_out(vty, "%% invalid SRES value '%s'%s", - sres_str, VTY_NEWLINE); - goto failed; - } - if (osmo_hexparse(rand_str, &at.vec.rand[0], sizeof(at.vec.rand)) < 0) { - vty_out(vty, "%% invalid RAND value '%s'%s", - rand_str, VTY_NEWLINE); - goto failed; - } - if (osmo_hexparse(kc_str, &at.vec.kc[0], sizeof(at.vec.kc)) < 0) { - vty_out(vty, "%% invalid Kc value '%s'%s", - kc_str, VTY_NEWLINE); - goto failed; - } - at.key_seq = cksn; - - subscr->sgsn_data->auth_triplets[cksn] = at; - subscr->sgsn_data->auth_triplets_updated = 1; - - gprs_subscr_put(subscr); - - return CMD_SUCCESS; - -failed: - gprs_subscr_put(subscr); - return CMD_SUCCESS; -} - -DEFUN(update_subscr_cancel, update_subscr_cancel_cmd, - UPDATE_SUBSCR_STR "cancel (update-procedure|subscription-withdraw)", - UPDATE_SUBSCR_HELP - "Cancel (remove) subscriber record\n" - "The MS moved to another SGSN\n" - "The subscription is no longer valid\n") -{ - const char *imsi = argv[0]; - const char *cancel_type = argv[1]; - - struct gprs_subscr *subscr; - - subscr = gprs_subscr_get_by_imsi(imsi); - if (!subscr) { - vty_out(vty, "%% no subscriber record for %s%s", - imsi, VTY_NEWLINE); - return CMD_WARNING; - } - - if (strcmp(cancel_type, "update-procedure") == 0) - subscr->sgsn_data->error_cause = SGSN_ERROR_CAUSE_NONE; - else - subscr->sgsn_data->error_cause = GMM_CAUSE_IMPL_DETACHED; - - gprs_subscr_cancel(subscr); - gprs_subscr_put(subscr); - - return CMD_SUCCESS; -} - -DEFUN(update_subscr_create, update_subscr_create_cmd, - UPDATE_SUBSCR_STR "create", - UPDATE_SUBSCR_HELP - "Create a subscriber entry\n") -{ - const char *imsi = argv[0]; - - struct gprs_subscr *subscr; - - subscr = gprs_subscr_get_by_imsi(imsi); - if (subscr) { - vty_out(vty, "%% subscriber record already exists for %s%s", - imsi, VTY_NEWLINE); - return CMD_WARNING; - } - - subscr = gprs_subscr_get_or_create(imsi); - subscr->keep_in_ram = 1; - gprs_subscr_put(subscr); - - return CMD_SUCCESS; -} - -DEFUN(update_subscr_destroy, update_subscr_destroy_cmd, - UPDATE_SUBSCR_STR "destroy", - UPDATE_SUBSCR_HELP - "Destroy a subscriber entry\n") -{ - const char *imsi = argv[0]; - - struct gprs_subscr *subscr; - - subscr = gprs_subscr_get_by_imsi(imsi); - if (!subscr) { - vty_out(vty, "%% subscriber record does not exist for %s%s", - imsi, VTY_NEWLINE); - return CMD_WARNING; - } - - subscr->keep_in_ram = 0; - subscr->sgsn_data->error_cause = SGSN_ERROR_CAUSE_NONE; - gprs_subscr_cancel(subscr); - if (subscr->use_count > 1) - vty_out(vty, "%% subscriber is still in use%s", - VTY_NEWLINE); - gprs_subscr_put(subscr); - - return CMD_SUCCESS; -} - -#define UL_ERR_STR "system-failure|data-missing|unexpected-data-value|" \ - "unknown-subscriber|roaming-not-allowed" - -#define UL_ERR_HELP \ - "Force error code SystemFailure\n" \ - "Force error code DataMissing\n" \ - "Force error code UnexpectedDataValue\n" \ - "Force error code UnknownSubscriber\n" \ - "Force error code RoamingNotAllowed\n" - -DEFUN(update_subscr_update_location_result, update_subscr_update_location_result_cmd, - UPDATE_SUBSCR_STR "update-location-result (ok|" UL_ERR_STR ")", - UPDATE_SUBSCR_HELP - "Complete the update location procedure\n" - "The update location request succeeded\n" - UL_ERR_HELP) -{ - const char *imsi = argv[0]; - const char *ret_code_str = argv[1]; - - struct gprs_subscr *subscr; - - const struct value_string cause_mapping[] = { - { GMM_CAUSE_NET_FAIL, "system-failure" }, - { GMM_CAUSE_INV_MAND_INFO, "data-missing" }, - { GMM_CAUSE_PROTO_ERR_UNSPEC, "unexpected-data-value" }, - { GMM_CAUSE_IMSI_UNKNOWN, "unknown-subscriber" }, - { GMM_CAUSE_GPRS_NOTALLOWED, "roaming-not-allowed" }, - { 0, NULL } - }; - - subscr = gprs_subscr_get_by_imsi(imsi); - if (!subscr) { - vty_out(vty, "%% unable to get subscriber record for %s%s", - imsi, VTY_NEWLINE); - return CMD_WARNING; - } - - if (strcmp(ret_code_str, "ok") == 0) { - subscr->sgsn_data->error_cause = SGSN_ERROR_CAUSE_NONE; - subscr->authorized = 1; - } else { - subscr->sgsn_data->error_cause = - get_string_value(cause_mapping, ret_code_str); - subscr->authorized = 0; - } - - gprs_subscr_update(subscr); - - gprs_subscr_put(subscr); - - return CMD_SUCCESS; -} - -DEFUN(update_subscr_update_auth_info, update_subscr_update_auth_info_cmd, - UPDATE_SUBSCR_STR "update-auth-info", - UPDATE_SUBSCR_HELP - "Complete the send authentication info procedure\n") -{ - const char *imsi = argv[0]; - - struct gprs_subscr *subscr; - - subscr = gprs_subscr_get_by_imsi(imsi); - if (!subscr) { - vty_out(vty, "%% unable to get subscriber record for %s%s", - imsi, VTY_NEWLINE); - return CMD_WARNING; - } - - gprs_subscr_update_auth_info(subscr); - - gprs_subscr_put(subscr); - - return CMD_SUCCESS; -} - -DEFUN(cfg_gsup_remote_ip, cfg_gsup_remote_ip_cmd, - "gsup remote-ip A.B.C.D", - "GSUP Parameters\n" - "Set the IP address of the remote GSUP server\n" - "IPv4 Address\n") -{ - inet_aton(argv[0], &g_cfg->gsup_server_addr.sin_addr); - - return CMD_SUCCESS; -} - -DEFUN(cfg_gsup_remote_port, cfg_gsup_remote_port_cmd, - "gsup remote-port <0-65535>", - "GSUP Parameters\n" - "Set the TCP port of the remote GSUP server\n" - "Remote TCP port\n") -{ - g_cfg->gsup_server_port = atoi(argv[0]); - - return CMD_SUCCESS; -} - -DEFUN(cfg_gsup_oap_id, cfg_gsup_oap_id_cmd, - "gsup oap-id <0-65535>", - "GSUP Parameters\n" - "Set the SGSN's OAP client ID\nOAP client ID (0 == disabled)\n") -{ - /* VTY ensures range */ - g_cfg->oap.client_id = (uint16_t)atoi(argv[0]); - return CMD_SUCCESS; -} - -DEFUN(cfg_gsup_oap_k, cfg_gsup_oap_k_cmd, - "gsup oap-k K", - "GSUP Parameters\n" - "Set the OAP shared secret K\nK value (16 byte) hex\n") -{ - const char *k = argv[0]; - - g_cfg->oap.secret_k_present = 0; - - if ((!k) || (strlen(k) == 0)) - goto disable; - - int k_len = osmo_hexparse(k, - g_cfg->oap.secret_k, - sizeof(g_cfg->oap.secret_k)); - if (k_len != 16) { - vty_out(vty, "%% need exactly 16 octets for oap-k, got %d.%s", - k_len, VTY_NEWLINE); - goto disable; - } - - g_cfg->oap.secret_k_present = 1; - return CMD_SUCCESS; - -disable: - if (g_cfg->oap.client_id > 0) { - vty_out(vty, "%% OAP client ID set, but invalid oap-k value disables OAP.%s", - VTY_NEWLINE); - return CMD_WARNING; - } - return CMD_SUCCESS; -} - -DEFUN(cfg_gsup_oap_opc, cfg_gsup_oap_opc_cmd, - "gsup oap-opc OPC", - "GSUP Parameters\n" - "Set the OAP shared secret OPC\nOPC value (16 byte) hex\n") -{ - const char *opc = argv[0]; - - g_cfg->oap.secret_opc_present = 0; - - if ((!opc) || (strlen(opc) == 0)) - goto disable; - - int opc_len = osmo_hexparse(opc, - g_cfg->oap.secret_opc, - sizeof(g_cfg->oap.secret_opc)); - if (opc_len != 16) { - vty_out(vty, "%% need exactly 16 octets for oap-opc, got %d.%s", - opc_len, VTY_NEWLINE); - goto disable; - } - - g_cfg->oap.secret_opc_present = 1; - return CMD_SUCCESS; - -disable: - if (g_cfg->oap.client_id > 0) { - vty_out(vty, "%% OAP client ID set, but invalid oap-opc value disables OAP.%s", - VTY_NEWLINE); - return CMD_WARNING; - } - return CMD_SUCCESS; -} - -DEFUN(cfg_apn_name, cfg_apn_name_cmd, - "access-point-name NAME", - "Configure a global list of allowed APNs\n" - "Add this NAME to the list\n") -{ - return add_apn_ggsn_mapping(vty, argv[0], "", 0); -} - -DEFUN(cfg_no_apn_name, cfg_no_apn_name_cmd, - "no access-point-name NAME", - NO_STR "Configure a global list of allowed APNs\n" - "Remove entry with NAME\n") -{ - struct apn_ctx *apn_ctx = sgsn_apn_ctx_by_name(argv[0], ""); - if (!apn_ctx) - return CMD_SUCCESS; - - sgsn_apn_ctx_free(apn_ctx); - return CMD_SUCCESS; -} - -DEFUN(cfg_cdr_filename, cfg_cdr_filename_cmd, - "cdr filename NAME", - "CDR\nSet filename\nname\n") -{ - talloc_free(g_cfg->cdr.filename); - g_cfg->cdr.filename = talloc_strdup(tall_vty_ctx, argv[0]); - return CMD_SUCCESS; -} - -DEFUN(cfg_no_cdr_filename, cfg_no_cdr_filename_cmd, - "no cdr filename", - NO_STR "CDR\nDisable CDR generation\n") -{ - talloc_free(g_cfg->cdr.filename); - g_cfg->cdr.filename = NULL; - return CMD_SUCCESS; -} - -DEFUN(cfg_cdr_interval, cfg_cdr_interval_cmd, - "cdr interval <1-2147483647>", - "CDR\nPDP periodic log interval\nSeconds\n") -{ - g_cfg->cdr.interval = atoi(argv[0]); - return CMD_SUCCESS; -} - -#define COMPRESSION_STR "Configure compression\n" -DEFUN(cfg_no_comp_rfc1144, cfg_no_comp_rfc1144_cmd, - "no compression rfc1144", - NO_STR COMPRESSION_STR "disable rfc1144 TCP/IP header compression\n") -{ - g_cfg->pcomp_rfc1144.active = 0; - g_cfg->pcomp_rfc1144.passive = 0; - return CMD_SUCCESS; -} - -DEFUN(cfg_comp_rfc1144, cfg_comp_rfc1144_cmd, - "compression rfc1144 active slots <1-256>", - COMPRESSION_STR - "RFC1144 Header compresion scheme\n" - "Compression is actively proposed\n" - "Number of compression state slots\n" - "Number of compression state slots\n") -{ - g_cfg->pcomp_rfc1144.active = 1; - g_cfg->pcomp_rfc1144.passive = 1; - g_cfg->pcomp_rfc1144.s01 = atoi(argv[0]) - 1; - return CMD_SUCCESS; -} - -DEFUN(cfg_comp_rfc1144p, cfg_comp_rfc1144p_cmd, - "compression rfc1144 passive", - COMPRESSION_STR - "RFC1144 Header compresion scheme\n" - "Compression is available on request\n") -{ - g_cfg->pcomp_rfc1144.active = 0; - g_cfg->pcomp_rfc1144.passive = 1; - return CMD_SUCCESS; -} - -DEFUN(cfg_no_comp_v42bis, cfg_no_comp_v42bis_cmd, - "no compression v42bis", - NO_STR COMPRESSION_STR "disable V.42bis data compression\n") -{ - g_cfg->dcomp_v42bis.active = 0; - g_cfg->dcomp_v42bis.passive = 0; - return CMD_SUCCESS; -} - -DEFUN(cfg_comp_v42bis, cfg_comp_v42bis_cmd, - "compression v42bis active direction (ms|sgsn|both) codewords <512-65535> strlen <6-250>", - COMPRESSION_STR - "V.42bis data compresion scheme\n" - "Compression is actively proposed\n" - "Direction in which the compression shall be active (p0)\n" - "Compress ms->sgsn direction only\n" - "Compress sgsn->ms direction only\n" - "Both directions\n" - "Number of codewords (p1)\n" - "Number of codewords\n" - "Maximum string length (p2)\n" "Maximum string length\n") -{ - g_cfg->dcomp_v42bis.active = 1; - g_cfg->dcomp_v42bis.passive = 1; - - switch (argv[0][0]) { - case 'm': - g_cfg->dcomp_v42bis.p0 = 1; - break; - case 's': - g_cfg->dcomp_v42bis.p0 = 2; - break; - case 'b': - g_cfg->dcomp_v42bis.p0 = 3; - break; - } - - g_cfg->dcomp_v42bis.p1 = atoi(argv[1]); - g_cfg->dcomp_v42bis.p2 = atoi(argv[2]); - return CMD_SUCCESS; -} - -DEFUN(cfg_comp_v42bisp, cfg_comp_v42bisp_cmd, - "compression v42bis passive", - COMPRESSION_STR - "V.42bis data compresion scheme\n" - "Compression is available on request\n") -{ - g_cfg->dcomp_v42bis.active = 0; - g_cfg->dcomp_v42bis.passive = 1; - return CMD_SUCCESS; -} - -int sgsn_vty_init(void) -{ - install_element_ve(&show_sgsn_cmd); - //install_element_ve(&show_mmctx_tlli_cmd); - install_element_ve(&show_mmctx_imsi_cmd); - install_element_ve(&show_mmctx_all_cmd); - install_element_ve(&show_pdpctx_all_cmd); - install_element_ve(&show_subscr_cache_cmd); - - install_element(ENABLE_NODE, &update_subscr_insert_auth_triplet_cmd); - install_element(ENABLE_NODE, &update_subscr_create_cmd); - install_element(ENABLE_NODE, &update_subscr_destroy_cmd); - install_element(ENABLE_NODE, &update_subscr_cancel_cmd); - install_element(ENABLE_NODE, &update_subscr_update_location_result_cmd); - install_element(ENABLE_NODE, &update_subscr_update_auth_info_cmd); - - install_element(CONFIG_NODE, &cfg_sgsn_cmd); - install_node(&sgsn_node, config_write_sgsn); - vty_install_default(SGSN_NODE); - install_element(SGSN_NODE, &cfg_sgsn_bind_addr_cmd); - install_element(SGSN_NODE, &cfg_ggsn_remote_ip_cmd); - //install_element(SGSN_NODE, &cfg_ggsn_remote_port_cmd); - install_element(SGSN_NODE, &cfg_ggsn_gtp_version_cmd); - install_element(SGSN_NODE, &cfg_imsi_acl_cmd); - install_element(SGSN_NODE, &cfg_auth_policy_cmd); - install_element(SGSN_NODE, &cfg_encrypt_cmd); - install_element(SGSN_NODE, &cfg_gsup_remote_ip_cmd); - install_element(SGSN_NODE, &cfg_gsup_remote_port_cmd); - install_element(SGSN_NODE, &cfg_gsup_oap_id_cmd); - install_element(SGSN_NODE, &cfg_gsup_oap_k_cmd); - install_element(SGSN_NODE, &cfg_gsup_oap_opc_cmd); - install_element(SGSN_NODE, &cfg_apn_ggsn_cmd); - install_element(SGSN_NODE, &cfg_apn_imsi_ggsn_cmd); - install_element(SGSN_NODE, &cfg_apn_name_cmd); - install_element(SGSN_NODE, &cfg_no_apn_name_cmd); - install_element(SGSN_NODE, &cfg_cdr_filename_cmd); - install_element(SGSN_NODE, &cfg_no_cdr_filename_cmd); - install_element(SGSN_NODE, &cfg_cdr_interval_cmd); - install_element(SGSN_NODE, &cfg_ggsn_dynamic_lookup_cmd); - install_element(SGSN_NODE, &cfg_grx_ggsn_cmd); - - install_element(SGSN_NODE, &cfg_sgsn_T3312_cmd); - install_element(SGSN_NODE, &cfg_sgsn_T3322_cmd); - install_element(SGSN_NODE, &cfg_sgsn_T3350_cmd); - install_element(SGSN_NODE, &cfg_sgsn_T3360_cmd); - install_element(SGSN_NODE, &cfg_sgsn_T3370_cmd); - install_element(SGSN_NODE, &cfg_sgsn_T3313_cmd); - install_element(SGSN_NODE, &cfg_sgsn_T3314_cmd); - install_element(SGSN_NODE, &cfg_sgsn_T3316_cmd); - install_element(SGSN_NODE, &cfg_sgsn_T3385_cmd); - install_element(SGSN_NODE, &cfg_sgsn_T3386_cmd); - install_element(SGSN_NODE, &cfg_sgsn_T3395_cmd); - install_element(SGSN_NODE, &cfg_sgsn_T3397_cmd); - - install_element(SGSN_NODE, &cfg_no_comp_rfc1144_cmd); - install_element(SGSN_NODE, &cfg_comp_rfc1144_cmd); - install_element(SGSN_NODE, &cfg_comp_rfc1144p_cmd); - install_element(SGSN_NODE, &cfg_no_comp_v42bis_cmd); - install_element(SGSN_NODE, &cfg_comp_v42bis_cmd); - install_element(SGSN_NODE, &cfg_comp_v42bisp_cmd); - return 0; -} - -int sgsn_parse_config(const char *config_file, struct sgsn_config *cfg) -{ - int rc; - - g_cfg = cfg; - - g_cfg->timers.T3312 = GSM0408_T3312_SECS; - g_cfg->timers.T3322 = GSM0408_T3322_SECS; - g_cfg->timers.T3350 = GSM0408_T3350_SECS; - g_cfg->timers.T3360 = GSM0408_T3360_SECS; - g_cfg->timers.T3370 = GSM0408_T3370_SECS; - g_cfg->timers.T3313 = GSM0408_T3313_SECS; - g_cfg->timers.T3314 = GSM0408_T3314_SECS; - g_cfg->timers.T3316 = GSM0408_T3316_SECS; - g_cfg->timers.T3385 = GSM0408_T3385_SECS; - g_cfg->timers.T3386 = GSM0408_T3386_SECS; - g_cfg->timers.T3395 = GSM0408_T3395_SECS; - g_cfg->timers.T3397 = GSM0408_T3397_SECS; - - rc = vty_read_config_file(config_file, NULL); - if (rc < 0) { - fprintf(stderr, "Failed to parse the config file: '%s'\n", config_file); - return rc; - } - - if (g_cfg->auth_policy == SGSN_AUTH_POLICY_REMOTE - && !(g_cfg->gsup_server_addr.sin_addr.s_addr - && g_cfg->gsup_server_port)) { - fprintf(stderr, "Configuration error:" - " 'auth-policy remote' requires both" - " 'gsup remote-ip' and 'gsup remote-port'\n"); - return -EINVAL; - } - - return 0; -} diff --git a/openbsc/src/gprs/slhc.c b/openbsc/src/gprs/slhc.c deleted file mode 100644 index cbdf8dbd..00000000 --- a/openbsc/src/gprs/slhc.c +++ /dev/null @@ -1,813 +0,0 @@ -/* - * Routines to compress and uncompress tcp packets (for transmission - * over low speed serial lines). - * - * Copyright (c) 1989 Regents of the University of California. - * All rights reserved. - * - * Redistribution and use in source and binary forms are permitted - * provided that the above copyright notice and this paragraph are - * duplicated in all such forms and that any documentation, - * advertising materials, and other materials related to such - * distribution and use acknowledge that the software was developed - * by the University of California, Berkeley. The name of the - * University may not be used to endorse or promote products derived - * from this software without specific prior written permission. - * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR - * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. - * - * Van Jacobson (van@helios.ee.lbl.gov), Dec 31, 1989: - * - Initial distribution. - * - * - * modified for KA9Q Internet Software Package by - * Katie Stevens (dkstevens@ucdavis.edu) - * University of California, Davis - * Computing Services - * - 01-31-90 initial adaptation (from 1.19) - * PPP.05 02-15-90 [ks] - * PPP.08 05-02-90 [ks] use PPP protocol field to signal compression - * PPP.15 09-90 [ks] improve mbuf handling - * PPP.16 11-02 [karn] substantially rewritten to use NOS facilities - * - * - Feb 1991 Bill_Simpson@um.cc.umich.edu - * variable number of conversation slots - * allow zero or one slots - * separate routines - * status display - * - Jul 1994 Dmitry Gorodchanin - * Fixes for memory leaks. - * - Oct 1994 Dmitry Gorodchanin - * Modularization. - * - Jan 1995 Bjorn Ekwall - * Use ip_fast_csum from ip.h - * - July 1995 Christos A. Polyzols - * Spotted bug in tcp option checking - * - * - * This module is a difficult issue. It's clearly inet code but it's also clearly - * driver code belonging close to PPP and SLIP - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#define ERR_PTR(x) x - - -static unsigned char *encode(unsigned char *cp, unsigned short n); -static long decode(unsigned char **cpp); -static unsigned char * put16(unsigned char *cp, unsigned short x); -static unsigned short pull16(unsigned char **cpp); - -/* Replacement for kernel space function ip_fast_csum() */ -static uint16_t ip_fast_csum(uint8_t *iph, int ihl) -{ - int i; - uint16_t temp; - uint32_t accumulator = 0xFFFF; - - for(i=0;i0xFFFF) - { - accumulator++; - accumulator&=0xFFFF; - } - } - - return (uint16_t)(htons(~accumulator)&0xFFFF); -} - -/* Replacement for kernel space function put_unaligned() */ -static void put_unaligned(uint16_t val, void *ptr) -{ - memcpy(ptr,&val,sizeof(val)); -} - - -/* Allocate compression data structure - * slots must be in range 0 to 255 (zero meaning no compression) - * Returns pointer to structure or ERR_PTR() on error. - */ -struct slcompress * -slhc_init(const void *ctx, int rslots, int tslots) -{ - register short i; - register struct cstate *ts; - struct slcompress *comp; - - if (rslots < 0 || rslots > 255 || tslots < 0 || tslots > 255) - return NULL; - - comp = (struct slcompress *)talloc_zero_size(ctx,sizeof(struct slcompress)); - if (! comp) - goto out_fail; - - if (rslots > 0) { - size_t rsize = rslots * sizeof(struct cstate); - comp->rstate = (struct cstate *) talloc_zero_size(ctx, rsize); - if (! comp->rstate) - goto out_free; - comp->rslot_limit = rslots - 1; - } - - if (tslots > 0) { - size_t tsize = tslots * sizeof(struct cstate); - comp->tstate = (struct cstate *) talloc_zero_size(ctx, tsize); - if (! comp->tstate) - goto out_free2; - comp->tslot_limit = tslots - 1; - } - - comp->xmit_oldest = 0; - comp->xmit_current = 255; - comp->recv_current = 255; - /* - * don't accept any packets with implicit index until we get - * one with an explicit index. Otherwise the uncompress code - * will try to use connection 255, which is almost certainly - * out of range - */ - comp->flags |= SLF_TOSS; - - if ( tslots > 0 ) { - ts = comp->tstate; - for(i = comp->tslot_limit; i > 0; --i){ - ts[i].cs_this = i; - ts[i].next = &(ts[i - 1]); - } - ts[0].next = &(ts[comp->tslot_limit]); - ts[0].cs_this = 0; - } - return comp; - -out_free2: - talloc_free(comp->rstate); -out_free: - talloc_free(comp); -out_fail: - return NULL; -} - - -/* Free a compression data structure */ -void -slhc_free(struct slcompress *comp) -{ - DEBUGP(DSLHC, "slhc_free(): Freeing compression states...\n"); - - if ( comp == NULLSLCOMPR ) - return; - - if ( comp->tstate != NULLSLSTATE ) - talloc_free(comp->tstate ); - - if ( comp->rstate != NULLSLSTATE ) - talloc_free( comp->rstate ); - - talloc_free( comp ); -} - - -/* Put a short in host order into a char array in network order */ -static inline unsigned char * -put16(unsigned char *cp, unsigned short x) -{ - *cp++ = x >> 8; - *cp++ = x; - - return cp; -} - - -/* Encode a number */ -static unsigned char * -encode(unsigned char *cp, unsigned short n) -{ - if(n >= 256 || n == 0){ - *cp++ = 0; - cp = put16(cp,n); - } else { - *cp++ = n; - } - - DEBUGP(DSLHC, "encode(): n=%04x\n",n); - return cp; -} - -/* Pull a 16-bit integer in host order from buffer in network byte order */ -static unsigned short -pull16(unsigned char **cpp) -{ - short rval; - - rval = *(*cpp)++; - rval <<= 8; - rval |= *(*cpp)++; - return rval; -} - -/* Decode a number */ -static long -decode(unsigned char **cpp) -{ - register int x; - - x = *(*cpp)++; - if(x == 0){ - return pull16(cpp) & 0xffff; /* pull16 returns -1 on error */ - } else { - return x & 0xff; /* -1 if PULLCHAR returned error */ - } -} - -/* - * icp and isize are the original packet. - * ocp is a place to put a copy if necessary. - * cpp is initially a pointer to icp. If the copy is used, - * change it to ocp. - */ - -int -slhc_compress(struct slcompress *comp, unsigned char *icp, int isize, - unsigned char *ocp, unsigned char **cpp, int compress_cid) -{ - register struct cstate *ocs = &(comp->tstate[comp->xmit_oldest]); - register struct cstate *lcs = ocs; - register struct cstate *cs = lcs->next; - register unsigned long deltaS, deltaA; - register short changes = 0; - int hlen; - unsigned char new_seq[16]; - register unsigned char *cp = new_seq; - struct iphdr *ip; - struct tcphdr *th, *oth; - __sum16 csum; - - - /* - * Don't play with runt packets. - */ - - if(isizeprotocol != IPPROTO_TCP || (ntohs(ip->frag_off) & 0x3fff)) { - /* Send as regular IP */ - if(ip->protocol != IPPROTO_TCP) - comp->sls_o_nontcp++; - else - comp->sls_o_tcp++; - DEBUGP(DSLHC, "slhc_compress(): Not a TCP packat, will not touch...\n"); - return isize; - } - /* Extract TCP header */ - - th = (struct tcphdr *)(((unsigned char *)ip) + ip->ihl*4); - hlen = ip->ihl*4 + th->doff*4; - - /* Bail if the TCP packet isn't `compressible' (i.e., ACK isn't set or - * some other control bit is set). Also uncompressible if - * it's a runt. - */ - if(hlen > isize || th->syn || th->fin || th->rst || - ! (th->ack)){ - /* TCP connection stuff; send as regular IP */ - comp->sls_o_tcp++; - DEBUGP(DSLHC, "slhc_compress(): Packet is part of a TCP connection, will not touch...\n"); - return isize; - } - /* - * Packet is compressible -- we're going to send either a - * COMPRESSED_TCP or UNCOMPRESSED_TCP packet. Either way, - * we need to locate (or create) the connection state. - * - * States are kept in a circularly linked list with - * xmit_oldest pointing to the end of the list. The - * list is kept in lru order by moving a state to the - * head of the list whenever it is referenced. Since - * the list is short and, empirically, the connection - * we want is almost always near the front, we locate - * states via linear search. If we don't find a state - * for the datagram, the oldest state is (re-)used. - */ - - DEBUGP(DSLHC, "slhc_compress(): Compressible packet detected!\n"); - - for ( ; ; ) { - if( ip->saddr == cs->cs_ip.saddr - && ip->daddr == cs->cs_ip.daddr - && th->source == cs->cs_tcp.source - && th->dest == cs->cs_tcp.dest) - goto found; - - /* if current equal oldest, at end of list */ - if ( cs == ocs ) - break; - lcs = cs; - cs = cs->next; - comp->sls_o_searches++; - } - /* - * Didn't find it -- re-use oldest cstate. Send an - * uncompressed packet that tells the other side what - * connection number we're using for this conversation. - * - * Note that since the state list is circular, the oldest - * state points to the newest and we only need to set - * xmit_oldest to update the lru linkage. - */ - - DEBUGP(DSLHC, "slhc_compress(): Header not yet seen, will memorize header for the next turn...\n"); - comp->sls_o_misses++; - comp->xmit_oldest = lcs->cs_this; - goto uncompressed; - -found: - DEBUGP(DSLHC, "slhc_compress(): Header already seen, trying to compress...\n"); - /* - * Found it -- move to the front on the connection list. - */ - if(lcs == ocs) { - /* found at most recently used */ - } else if (cs == ocs) { - /* found at least recently used */ - comp->xmit_oldest = lcs->cs_this; - } else { - /* more than 2 elements */ - lcs->next = cs->next; - cs->next = ocs->next; - ocs->next = cs; - } - - /* - * Make sure that only what we expect to change changed. - * Check the following: - * IP protocol version, header length & type of service. - * The "Don't fragment" bit. - * The time-to-live field. - * The TCP header length. - * IP options, if any. - * TCP options, if any. - * If any of these things are different between the previous & - * current datagram, we send the current datagram `uncompressed'. - */ - oth = &cs->cs_tcp; - - /* Display a little more debug information about which of the - * header fields changed unexpectedly */ - if(ip->version != cs->cs_ip.version) - DEBUGP(DSLHC, "slhc_compress(): Unexpected change: ip->version != cs->cs_ip.version\n"); - if(ip->ihl != cs->cs_ip.ihl) - DEBUGP(DSLHC, "slhc_compress(): Unexpected change: ip->ihl != cs->cs_ip.ihl\n"); - if(ip->tos != cs->cs_ip.tos) - DEBUGP(DSLHC, "slhc_compress(): Unexpected change: ip->tos != cs->cs_ip.tos\n"); - if((ip->frag_off & htons(0x4000)) != (cs->cs_ip.frag_off & htons(0x4000))) - DEBUGP(DSLHC, "slhc_compress(): Unexpected change: (ip->frag_off & htons(0x4000)) != (cs->cs_ip.frag_off & htons(0x4000))\n"); - if(ip->ttl != cs->cs_ip.ttl) - DEBUGP(DSLHC, "slhc_compress(): Unexpected change: ip->ttl != cs->cs_ip.ttl\n"); - if(th->doff != cs->cs_tcp.doff) - DEBUGP(DSLHC, "slhc_compress(): Unexpected change: th->doff != cs->cs_tcp.doff\n"); - if(ip->ihl > 5 && memcmp(ip+1,cs->cs_ipopt,((ip->ihl)-5)*4) != 0) { - DEBUGP(DSLHC, "slhc_compress(): Unexpected change: (ip->ihl > 5 && memcmp(ip+1,cs->cs_ipopt,((ip->ihl)-5)*4) != 0)\n"); - DEBUGP(DSLHC, "slhc_compress(): ip->ihl = %i\n", ip->ihl); - DEBUGP(DSLHC, "slhc_compress(): ip+1 = %s\n", - osmo_hexdump_nospc((uint8_t*)(ip+1),((ip->ihl)-5)*4)); - DEBUGP(DSLHC, "slhc_compress(): Unexpected change: cs->cs_ipopt = %s\n", - osmo_hexdump_nospc((uint8_t*)(cs->cs_ipopt),((ip->ihl)-5)*4)); - } - if(th->doff > 5 && memcmp(th+1,cs->cs_tcpopt,((th->doff)-5)*4) != 0) { - DEBUGP(DSLHC, "slhc_compress(): Unexpected change: (th->doff > 5 && memcmp(th+1,cs->cs_tcpopt,((th->doff)-5)*4) != 0)\n"); - DEBUGP(DSLHC, "slhc_compress(): th->doff = %i\n", th->doff); - DEBUGP(DSLHC, "slhc_compress(): th+1 = %s\n", - osmo_hexdump_nospc((uint8_t*)(th+1),((th->doff)-5)*4)); - DEBUGP(DSLHC, "slhc_compress(): cs->cs_tcpopt = %s\n", - osmo_hexdump_nospc((uint8_t*)cs->cs_tcpopt, - ((th->doff)-5)*4)); - } - - - if(ip->version != cs->cs_ip.version || ip->ihl != cs->cs_ip.ihl - || ip->tos != cs->cs_ip.tos - || (ip->frag_off & htons(0x4000)) != (cs->cs_ip.frag_off & htons(0x4000)) - || ip->ttl != cs->cs_ip.ttl - || th->doff != cs->cs_tcp.doff - || (ip->ihl > 5 && memcmp(ip+1,cs->cs_ipopt,((ip->ihl)-5)*4) != 0) - || (th->doff > 5 && memcmp(th+1,cs->cs_tcpopt,((th->doff)-5)*4) != 0)){ - DEBUGP(DSLHC, "slhc_compress(): The header contains unexpected changes, can't compress...\n"); - goto uncompressed; - } - - /* - * Figure out which of the changing fields changed. The - * receiver expects changes in the order: urgent, window, - * ack, seq (the order minimizes the number of temporaries - * needed in this section of code). - */ - if(th->urg){ - deltaS = ntohs(th->urg_ptr); - DEBUGP(DSLHC, "slhc_compress(): flag: Urgent Pointer (U) = 1\n"); - cp = encode(cp,deltaS); - changes |= NEW_U; - } else if(th->urg_ptr != oth->urg_ptr){ - /* argh! URG not set but urp changed -- a sensible - * implementation should never do this but RFC793 - * doesn't prohibit the change so we have to deal - * with it. */ - DEBUGP(DSLHC, "slhc_compress(): URG not set but urp changed, can't compress...\n"); - goto uncompressed; - } - if((deltaS = ntohs(th->window) - ntohs(oth->window)) != 0){ - DEBUGP(DSLHC, "slhc_compress(): flag: Delta Window (W) = 1\n"); - cp = encode(cp,deltaS); - changes |= NEW_W; - } - if((deltaA = ntohl(th->ack_seq) - ntohl(oth->ack_seq)) != 0L){ - if(deltaA > 0x0000ffff) { - DEBUGP(DSLHC, "slhc_compress(): (deltaA = ntohl(th->ack_seq) - ntohl(oth->ack_seq)) != 0L, can't compress...\n"); - goto uncompressed; - } - DEBUGP(DSLHC, "slhc_compress(): flag: Delta Ack (A) = 1\n"); - cp = encode(cp,deltaA); - changes |= NEW_A; - } - if((deltaS = ntohl(th->seq) - ntohl(oth->seq)) != 0L){ - if(deltaS > 0x0000ffff) { - DEBUGP(DSLHC, "slhc_compress(): (deltaS = ntohl(th->seq) - ntohl(oth->seq)) != 0L, can't compress...\n"); - goto uncompressed; - } - DEBUGP(DSLHC, "slhc_compress(): flag: Delta Sequence (S) = 1\n"); - cp = encode(cp,deltaS); - changes |= NEW_S; - } - - switch(changes){ - case 0: /* Nothing changed. If this packet contains data and the - * last one didn't, this is probably a data packet following - * an ack (normal on an interactive connection) and we send - * it compressed. Otherwise it's probably a retransmit, - * retransmitted ack or window probe. Send it uncompressed - * in case the other side missed the compressed version. - */ - if(ip->tot_len != cs->cs_ip.tot_len && - ntohs(cs->cs_ip.tot_len) == hlen) - break; - DEBUGP(DSLHC, "slhc_compress(): Retransmitted packet detected, can't compress...\n"); - goto uncompressed; - case SPECIAL_I: - case SPECIAL_D: - /* actual changes match one of our special case encodings -- - * send packet uncompressed. - */ - DEBUGP(DSLHC, "slhc_compress(): Special case detected, can't compress...\n"); - goto uncompressed; - case NEW_S|NEW_A: - if(deltaS == deltaA && - deltaS == ntohs(cs->cs_ip.tot_len) - hlen){ - /* special case for echoed terminal traffic */ - DEBUGP(DSLHC, "slhc_compress(): Special case for echoed terminal traffic detected...\n"); - DEBUGP(DSLHC, "slhc_compress(): flag: Delta Sequence (S) = 1, Delta Window (W) = 1, Urgent Pointer (U) = 1\n"); - changes = SPECIAL_I; - cp = new_seq; - } - break; - case NEW_S: - if(deltaS == ntohs(cs->cs_ip.tot_len) - hlen){ - /* special case for data xfer */ - DEBUGP(DSLHC, "slhc_compress(): Special case for data xfer detected...\n"); - DEBUGP(DSLHC, "slhc_compress(): flag: Delta Sequence (S) = 1, Delta Ack (A) = 1, Delta Window (W) = 1, Urgent Pointer (U) = 1\n"); - changes = SPECIAL_D; - cp = new_seq; - } - break; - } - deltaS = ntohs(ip->id) - ntohs(cs->cs_ip.id); - if(deltaS != 1){ - DEBUGP(DSLHC, "slhc_compress(): flag: Delta IP ID (I) = 1\n"); - cp = encode(cp,deltaS); - changes |= NEW_I; - } - if(th->psh) { - DEBUGP(DSLHC, "slhc_compress(): flag: Push (P) = 1\n"); - changes |= TCP_PUSH_BIT; - } - /* Grab the cksum before we overwrite it below. Then update our - * state with this packet's header. - */ - csum = th->check; - memcpy(&cs->cs_ip,ip,20); - memcpy(&cs->cs_tcp,th,20); - /* We want to use the original packet as our compressed packet. - * (cp - new_seq) is the number of bytes we need for compressed - * sequence numbers. In addition we need one byte for the change - * mask, one for the connection id and two for the tcp checksum. - * So, (cp - new_seq) + 4 bytes of header are needed. - */ - deltaS = cp - new_seq; - if(compress_cid == 0 || comp->xmit_current != cs->cs_this){ - cp = ocp; - *cpp = ocp; - DEBUGP(DSLHC, "slhc_compress(): flag: Connection number (C) = 1\n"); - *cp++ = changes | NEW_C; - *cp++ = cs->cs_this; - comp->xmit_current = cs->cs_this; - } else { - cp = ocp; - *cpp = ocp; - *cp++ = changes; - } - *(__sum16 *)cp = csum; - cp += 2; -/* deltaS is now the size of the change section of the compressed header */ - - DEBUGP(DSLHC, "slhc_compress(): Delta-list length (deltaS) = %li\n",deltaS); - DEBUGP(DSLHC, "slhc_compress(): Original header len (hlen) = %i\n",hlen); - - memcpy(cp,new_seq,deltaS); /* Write list of deltas */ - memcpy(cp+deltaS,icp+hlen,isize-hlen); - comp->sls_o_compressed++; - ocp[0] |= SL_TYPE_COMPRESSED_TCP; - return isize - hlen + deltaS + (cp - ocp); - - /* Update connection state cs & send uncompressed packet (i.e., - * a regular ip/tcp packet but with the 'conversation id' we hope - * to use on future compressed packets in the protocol field). - */ -uncompressed: - DEBUGP(DSLHC, "slhc_compress(): Packet will be sent uncompressed...\n"); - memcpy(&cs->cs_ip,ip,20); - memcpy(&cs->cs_tcp,th,20); - if (ip->ihl > 5) - memcpy(cs->cs_ipopt, ip+1, ((ip->ihl) - 5) * 4); - if (th->doff > 5) - memcpy(cs->cs_tcpopt, th+1, ((th->doff) - 5) * 4); - comp->xmit_current = cs->cs_this; - comp->sls_o_uncompressed++; - memcpy(ocp, icp, isize); - *cpp = ocp; - ocp[9] = cs->cs_this; - ocp[0] |= SL_TYPE_UNCOMPRESSED_TCP; - return isize; -} - - -int -slhc_uncompress(struct slcompress *comp, unsigned char *icp, int isize) -{ - register int changes; - long x; - register struct tcphdr *thp; - register struct iphdr *ip; - register struct cstate *cs; - int len, hdrlen; - unsigned char *cp = icp; - - /* We've got a compressed packet; read the change byte */ - comp->sls_i_compressed++; - if(isize < 3){ - comp->sls_i_error++; - return 0; - } - changes = *cp++; - if(changes & NEW_C){ - /* Make sure the state index is in range, then grab the state. - * If we have a good state index, clear the 'discard' flag. - */ - x = *cp++; /* Read conn index */ - if(x < 0 || x > comp->rslot_limit) - goto bad; - - comp->flags &=~ SLF_TOSS; - comp->recv_current = x; - } else { - /* this packet has an implicit state index. If we've - * had a line error since the last time we got an - * explicit state index, we have to toss the packet. */ - if(comp->flags & SLF_TOSS){ - comp->sls_i_tossed++; - return 0; - } - } - cs = &comp->rstate[comp->recv_current]; - thp = &cs->cs_tcp; - ip = &cs->cs_ip; - - thp->check = *(__sum16 *)cp; - cp += 2; - - thp->psh = (changes & TCP_PUSH_BIT) ? 1 : 0; -/* - * we can use the same number for the length of the saved header and - * the current one, because the packet wouldn't have been sent - * as compressed unless the options were the same as the previous one - */ - - hdrlen = ip->ihl * 4 + thp->doff * 4; - - switch(changes & SPECIALS_MASK){ - case SPECIAL_I: /* Echoed terminal traffic */ - DEBUGP(DSLHC, "slhc_uncompress(): Echoed terminal traffic detected\n"); - - { - register short i; - i = ntohs(ip->tot_len) - hdrlen; - thp->ack_seq = htonl( ntohl(thp->ack_seq) + i); - thp->seq = htonl( ntohl(thp->seq) + i); - } - break; - - case SPECIAL_D: /* Unidirectional data */ - DEBUGP(DSLHC, "slhc_uncompress(): Unidirectional data detected\n"); - thp->seq = htonl( ntohl(thp->seq) + - ntohs(ip->tot_len) - hdrlen); - break; - - default: - DEBUGP(DSLHC, "slhc_uncompress(): default packet type detected\n"); - if(changes & NEW_U){ - thp->urg = 1; - if((x = decode(&cp)) == -1) { - goto bad; - } - thp->urg_ptr = htons(x); - } else - thp->urg = 0; - if(changes & NEW_W){ - if((x = decode(&cp)) == -1) { - goto bad; - } - thp->window = htons( ntohs(thp->window) + x); - } - if(changes & NEW_A){ - if((x = decode(&cp)) == -1) { - goto bad; - } - thp->ack_seq = htonl( ntohl(thp->ack_seq) + x); - } - if(changes & NEW_S){ - if((x = decode(&cp)) == -1) { - goto bad; - } - thp->seq = htonl( ntohl(thp->seq) + x); - } - break; - } - if(changes & NEW_I){ - if((x = decode(&cp)) == -1) { - goto bad; - } - ip->id = htons (ntohs (ip->id) + x); - } else - ip->id = htons (ntohs (ip->id) + 1); - - /* - * At this point, cp points to the first byte of data in the - * packet. Put the reconstructed TCP and IP headers back on the - * packet. Recalculate IP checksum (but not TCP checksum). - */ - - len = isize - (cp - icp); - if (len < 0) - goto bad; - len += hdrlen; - ip->tot_len = htons(len); - ip->check = 0; - - DEBUGP(DSLHC, "slhc_uncompress(): making space for the reconstructed header...\n"); - memmove(icp + hdrlen, cp, len - hdrlen); - - cp = icp; - memcpy(cp, ip, 20); - cp += 20; - - if (ip->ihl > 5) { - memcpy(cp, cs->cs_ipopt, (ip->ihl - 5) * 4); - cp += (ip->ihl - 5) * 4; - } - - put_unaligned(ip_fast_csum(icp, ip->ihl), - &((struct iphdr *)icp)->check); - - memcpy(cp, thp, 20); - cp += 20; - - if (thp->doff > 5) { - memcpy(cp, cs->cs_tcpopt, ((thp->doff) - 5) * 4); - cp += ((thp->doff) - 5) * 4; - } - - return len; -bad: - DEBUGP(DSLHC, "slhc_uncompress(): bad packet detected!\n"); - comp->sls_i_error++; - return slhc_toss( comp ); -} - - -int -slhc_remember(struct slcompress *comp, unsigned char *icp, int isize) -{ - register struct cstate *cs; - unsigned ihl; - - unsigned char index; - - if(isize < 20) { - /* The packet is shorter than a legal IP header */ - comp->sls_i_runt++; - DEBUGP(DSLHC, "slhc_remember(): The packet is shorter than a legal IP header ==> slhc_toss()\n"); - return slhc_toss( comp ); - } - /* Peek at the IP header's IHL field to find its length */ - ihl = icp[0] & 0xf; - if(ihl < 20 / 4){ - /* The IP header length field is too small */ - comp->sls_i_runt++; - DEBUGP(DSLHC, "slhc_remember(): The IP header length field is too small ==> slhc_toss()\n"); - return slhc_toss( comp ); - } - index = icp[9]; - icp[9] = IPPROTO_TCP; - - if (ip_fast_csum(icp, ihl)) { - /* Bad IP header checksum; discard */ - comp->sls_i_badcheck++; - DEBUGP(DSLHC, "slhc_remember(): Bad IP header checksum; discard ==> slhc_toss()\n"); - return slhc_toss( comp ); - } - if(index > comp->rslot_limit) { - comp->sls_i_error++; - DEBUGP(DSLHC, "slhc_remember(): index > comp->rslot_limit ==> slhc_toss()\n"); - return slhc_toss(comp); - } - - /* Update local state */ - cs = &comp->rstate[comp->recv_current = index]; - comp->flags &=~ SLF_TOSS; - memcpy(&cs->cs_ip,icp,20); - memcpy(&cs->cs_tcp,icp + ihl*4,20); - if (ihl > 5) - memcpy(cs->cs_ipopt, icp + sizeof(struct iphdr), (ihl - 5) * 4); - if (cs->cs_tcp.doff > 5) - memcpy(cs->cs_tcpopt, icp + ihl*4 + sizeof(struct tcphdr), (cs->cs_tcp.doff - 5) * 4); - cs->cs_hsize = ihl*2 + cs->cs_tcp.doff*2; - /* Put headers back on packet - * Neither header checksum is recalculated - */ - comp->sls_i_uncompressed++; - return isize; -} - -int -slhc_toss(struct slcompress *comp) -{ - DEBUGP(DSLHC, "slhc_toss(): Reset compression state...\n"); - if ( comp == NULLSLCOMPR ) - return 0; - - comp->flags |= SLF_TOSS; - return 0; -} - -void slhc_i_status(struct slcompress *comp) -{ - if (comp != NULLSLCOMPR) { - DEBUGP(DSLHC, "slhc_i_status(): %d Cmp, %d Uncmp, %d Bad, %d Tossed\n", - comp->sls_i_compressed, - comp->sls_i_uncompressed, - comp->sls_i_error, - comp->sls_i_tossed); - } -} - -void slhc_o_status(struct slcompress *comp) -{ - if (comp != NULLSLCOMPR) { - DEBUGP(DSLHC, "slhc_o_status(): %d Cmp, %d Uncmp, %d AsIs, %d NotTCP %d Searches, %d Misses\n", - comp->sls_o_compressed, - comp->sls_o_uncompressed, - comp->sls_o_tcp, - comp->sls_o_nontcp, - comp->sls_o_searches, - comp->sls_o_misses); - } -} - diff --git a/openbsc/src/gprs/v42bis.c b/openbsc/src/gprs/v42bis.c deleted file mode 100644 index a04b0af5..00000000 --- a/openbsc/src/gprs/v42bis.c +++ /dev/null @@ -1,767 +0,0 @@ -/* - * SpanDSP - a series of DSP components for telephony - * - * v42bis.c - * - * Written by Steve Underwood - * - * Copyright (C) 2005, 2011 Steve Underwood - * - * All rights reserved. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License version 2.1, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this program; if not, write to the Free Software - * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - */ - -/* THIS IS A WORK IN PROGRESS. IT IS NOT FINISHED. - Currently it performs the core compression and decompression functions OK. - However, a number of the bells and whistles in V.42bis are incomplete. */ - -/*! \file */ - -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include - - -#define span_log(x,y,msg, ...) DEBUGP(DV42BIS,msg, ##__VA_ARGS__) -#define span_log_init(x,y,z) -#define span_log_set_protocol(x,y) - - -#define FALSE 0 -#define TRUE 1 - -/* Fixed parameters from the spec. */ -/* Character size (bits) */ -#define V42BIS_N3 8 -/* Number of characters in the alphabet */ -#define V42BIS_N4 256 -/* Index number of first dictionary entry used to store a string */ -#define V42BIS_N5 (V42BIS_N4 + V42BIS_N6) -/* Number of control codewords */ -#define V42BIS_N6 3 -/* V.42bis/9.2 */ -#define V42BIS_ESC_STEP 51 - -/* Compreeibility monitoring parameters for assessing automated switches between - transparent and compressed mode */ -#define COMPRESSIBILITY_MONITOR (256*V42BIS_N3) -#define COMPRESSIBILITY_MONITOR_HYSTERESIS 11 - -/* Control code words in compressed mode */ -enum -{ - V42BIS_ETM = 0, /* Enter transparent mode */ - V42BIS_FLUSH = 1, /* Flush data */ - V42BIS_STEPUP = 2 /* Step up codeword size */ -}; - -/* Command codes in transparent mode */ -enum -{ - V42BIS_ECM = 0, /* Enter compression mode */ - V42BIS_EID = 1, /* Escape character in data */ - V42BIS_RESET = 2 /* Force reinitialisation */ -}; - -static __inline__ void push_octet(v42bis_comp_state_t *s, int octet) -{ - s->output_buf[s->output_octet_count++] = (uint8_t) octet; - if (s->output_octet_count >= s->max_output_len) - { - s->handler(s->user_data, s->output_buf, s->output_octet_count); - s->output_octet_count = 0; - } -} -/*- End of function --------------------------------------------------------*/ - -static __inline__ void push_octets(v42bis_comp_state_t *s, const uint8_t buf[], int len) -{ - int i; - int chunk; - - i = 0; - while ((s->output_octet_count + len - i) >= s->max_output_len) - { - chunk = s->max_output_len - s->output_octet_count; - memcpy(&s->output_buf[s->output_octet_count], &buf[i], chunk); - s->handler(s->user_data, s->output_buf, s->max_output_len); - s->output_octet_count = 0; - i += chunk; - } - chunk = len - i; - if (chunk > 0) - { - memcpy(&s->output_buf[s->output_octet_count], &buf[i], chunk); - s->output_octet_count += chunk; - } -} -/*- End of function --------------------------------------------------------*/ - -static __inline__ void push_compressed_code(v42bis_comp_state_t *s, int code) -{ - s->bit_buffer |= code << s->bit_count; - s->bit_count += s->v42bis_parm_c2; - while (s->bit_count >= 8) - { - push_octet(s, s->bit_buffer & 0xFF); - s->bit_buffer >>= 8; - s->bit_count -= 8; - } -} -/*- End of function --------------------------------------------------------*/ - -static __inline__ void push_octet_alignment(v42bis_comp_state_t *s) -{ - if ((s->bit_count & 7)) - { - s->bit_count += (8 - (s->bit_count & 7)); - while (s->bit_count >= 8) - { - push_octet(s, s->bit_buffer & 0xFF); - s->bit_buffer >>= 8; - s->bit_count -= 8; - } - } -} -/*- End of function --------------------------------------------------------*/ - -static __inline__ void flush_octets(v42bis_comp_state_t *s) -{ - if (s->output_octet_count > 0) - { - s->handler(s->user_data, s->output_buf, s->output_octet_count); - s->output_octet_count = 0; - } -} -/*- End of function --------------------------------------------------------*/ - -static void dictionary_init(v42bis_comp_state_t *s) -{ - int i; - - memset(s->dict, 0, sizeof(s->dict)); - for (i = 0; i < V42BIS_N4; i++) - s->dict[i + V42BIS_N6].node_octet = i; - s->v42bis_parm_c1 = V42BIS_N5; - s->v42bis_parm_c2 = V42BIS_N3 + 1; - s->v42bis_parm_c3 = V42BIS_N4 << 1; - s->last_matched = 0; - s->update_at = 0; - s->last_added = 0; - s->bit_buffer = 0; - s->bit_count = 0; - s->flushed_length = 0; - s->string_length = 0; - s->escape_code = 0; - s->transparent = TRUE; - s->escaped = FALSE; - s->compression_performance = COMPRESSIBILITY_MONITOR; -} -/*- End of function --------------------------------------------------------*/ - -static uint16_t match_octet(v42bis_comp_state_t *s, uint16_t at, uint8_t octet) -{ - uint16_t e; - - if (at == 0) - return octet + V42BIS_N6; - e = s->dict[at].child; - while (e) - { - if (s->dict[e].node_octet == octet) - return e; - e = s->dict[e].next; - } - return 0; -} -/*- End of function --------------------------------------------------------*/ - -static uint16_t add_octet_to_dictionary(v42bis_comp_state_t *s, uint16_t at, uint8_t octet) -{ - uint16_t newx; - uint16_t next; - uint16_t e; - - newx = s->v42bis_parm_c1; - s->dict[newx].node_octet = octet; - s->dict[newx].parent = at; - s->dict[newx].child = 0; - s->dict[newx].next = s->dict[at].child; - s->dict[at].child = newx; - next = newx; - /* 6.5 Recovering a dictionary entry to use next */ - do - { - /* 6.5(a) and (b) */ - if (++next == s->v42bis_parm_n2) - next = V42BIS_N5; - } - while (s->dict[next].child); - /* 6.5(c) We need to reuse a leaf node */ - if (s->dict[next].parent) - { - /* 6.5(d) Detach the leaf node from its parent, and re-use it */ - e = s->dict[next].parent; - if (s->dict[e].child == next) - { - s->dict[e].child = s->dict[next].next; - } - else - { - e = s->dict[e].child; - while (s->dict[e].next != next) - e = s->dict[e].next; - s->dict[e].next = s->dict[next].next; - } - } - s->v42bis_parm_c1 = next; - return newx; -} -/*- End of function --------------------------------------------------------*/ - -static void send_string(v42bis_comp_state_t *s) -{ - push_octets(s, s->string, s->string_length); - s->string_length = 0; - s->flushed_length = 0; -} -/*- End of function --------------------------------------------------------*/ - -static void expand_codeword_to_string(v42bis_comp_state_t *s, uint16_t code) -{ - int i; - uint16_t p; - - /* Work out the length */ - for (i = 0, p = code; p; i++) - p = s->dict[p].parent; - s->string_length += i; - /* Now expand the known length of string */ - i = s->string_length - 1; - for (p = code; p; ) - { - s->string[i--] = s->dict[p].node_octet; - p = s->dict[p].parent; - } -} -/*- End of function --------------------------------------------------------*/ - -static void send_encoded_data(v42bis_comp_state_t *s, uint16_t code) -{ - int i; - - /* Update compressibility metric */ - /* Integrate at the compressed bit rate, and leak at the pre-compression bit rate */ - s->compression_performance += (s->v42bis_parm_c2 - s->compression_performance*s->string_length*V42BIS_N3/COMPRESSIBILITY_MONITOR); - if (s->transparent) - { - for (i = 0; i < s->string_length; i++) - { - push_octet(s, s->string[i]); - if (s->string[i] == s->escape_code) - { - push_octet(s, V42BIS_EID); - s->escape_code += V42BIS_ESC_STEP; - } - } - } - else - { - /* Allow for any escape octets in the string */ - for (i = 0; i < s->string_length; i++) - { - if (s->string[i] == s->escape_code) - s->escape_code += V42BIS_ESC_STEP; - } - /* 7.4 Encoding - we now have the longest matchable string, and will need to output the code for it. */ - while (code >= s->v42bis_parm_c3) - { - /* We need to increase the codeword size */ - /* 7.4(a) */ - push_compressed_code(s, V42BIS_STEPUP); - /* 7.4(b) */ - s->v42bis_parm_c2++; - /* 7.4(c) */ - s->v42bis_parm_c3 <<= 1; - /* 7.4(d) this might need to be repeated, so we loop */ - } - /* 7.5 Transfer - output the last state of the string */ - push_compressed_code(s, code); - } - s->string_length = 0; - s->flushed_length = 0; -} -/*- End of function --------------------------------------------------------*/ - -static void go_compressed(v42bis_state_t *ss) -{ - v42bis_comp_state_t *s; - - s = &ss->compress; - if (!s->transparent) - return; - span_log(&ss->logging, SPAN_LOG_FLOW, "Changing to compressed mode\n"); - /* Switch out of transparent now, between codes. We need to send the octet which did not - match, just before switching. */ - if (s->last_matched) - { - s->update_at = s->last_matched; - send_encoded_data(s, s->last_matched); - s->last_matched = 0; - } - push_octet(s, s->escape_code); - push_octet(s, V42BIS_ECM); - s->bit_buffer = 0; - s->transparent = FALSE; -} -/*- End of function --------------------------------------------------------*/ - -static void go_transparent(v42bis_state_t *ss) -{ - v42bis_comp_state_t *s; - - s = &ss->compress; - if (s->transparent) - return; - span_log(&ss->logging, SPAN_LOG_FLOW, "Changing to transparent mode\n"); - /* Switch into transparent now, between codes, and the unmatched octet should - go out in transparent mode, just below */ - if (s->last_matched) - { - s->update_at = s->last_matched; - send_encoded_data(s, s->last_matched); - s->last_matched = 0; - } - s->last_added = 0; - push_compressed_code(s, V42BIS_ETM); - push_octet_alignment(s); - s->transparent = TRUE; -} -/*- End of function --------------------------------------------------------*/ - -static void monitor_for_mode_change(v42bis_state_t *ss) -{ - v42bis_comp_state_t *s; - - s = &ss->compress; - switch (s->compression_mode) - { - case V42BIS_COMPRESSION_MODE_DYNAMIC: - /* 7.8 Data compressibility test */ - if (s->transparent) - { - if (s->compression_performance < COMPRESSIBILITY_MONITOR - COMPRESSIBILITY_MONITOR_HYSTERESIS) - { - /* 7.8.1 Transition to compressed mode */ - go_compressed(ss); - } - } - else - { - if (s->compression_performance > COMPRESSIBILITY_MONITOR) - { - /* 7.8.2 Transition to transparent mode */ - go_transparent(ss); - } - } - /* 7.8.3 Reset function - TODO */ - break; - case V42BIS_COMPRESSION_MODE_ALWAYS: - if (s->transparent) - go_compressed(ss); - break; - case V42BIS_COMPRESSION_MODE_NEVER: - if (!s->transparent) - go_transparent(ss); - break; - } -} -/*- End of function --------------------------------------------------------*/ - -static int v42bis_comp_init(v42bis_comp_state_t *s, - int p1, - int p2, - put_msg_func_t handler, - void *user_data, - int max_output_len) -{ - memset(s, 0, sizeof(*s)); - s->v42bis_parm_n2 = p1; - s->v42bis_parm_n7 = p2; - s->handler = handler; - s->user_data = user_data; - s->max_output_len = (max_output_len < V42BIS_MAX_OUTPUT_LENGTH) ? max_output_len : V42BIS_MAX_OUTPUT_LENGTH; - s->output_octet_count = 0; - dictionary_init(s); - return 0; -} -/*- End of function --------------------------------------------------------*/ - -static int comp_exit(v42bis_comp_state_t *s) -{ - s->v42bis_parm_n2 = 0; - return 0; -} -/*- End of function --------------------------------------------------------*/ - -SPAN_DECLARE(int) v42bis_compress(v42bis_state_t *ss, const uint8_t buf[], int len) -{ - v42bis_comp_state_t *s; - int i; - uint16_t code; - - s = &ss->compress; - if (!s->v42bis_parm_p0) - { - /* Compression is off - just push the incoming data out */ - push_octets(s, buf, len); - return 0; - } - for (i = 0; i < len; ) - { - /* 6.4 Add the string to the dictionary */ - if (s->update_at) - { - if (match_octet(s, s->update_at, buf[i]) == 0) - s->last_added = add_octet_to_dictionary(s, s->update_at, buf[i]); - s->update_at = 0; - } - /* Match string */ - while (i < len) - { - code = match_octet(s, s->last_matched, buf[i]); - if (code == 0) - { - s->update_at = s->last_matched; - send_encoded_data(s, s->last_matched); - s->last_matched = 0; - break; - } - if (code == s->last_added) - { - s->last_added = 0; - send_encoded_data(s, s->last_matched); - s->last_matched = 0; - break; - } - s->last_matched = code; - /* 6.3(b) If the string matches a dictionary entry, and the entry is not that entry - created by the last invocation of the string matching procedure, then the - next character shall be read and appended to the string and this step - repeated. */ - s->string[s->string_length++] = buf[i++]; - /* 6.4(a) The string must not exceed N7 in length */ - if (s->string_length + s->flushed_length == s->v42bis_parm_n7) - { - send_encoded_data(s, s->last_matched); - s->last_matched = 0; - break; - } - } - monitor_for_mode_change(ss); - } - return 0; -} -/*- End of function --------------------------------------------------------*/ - -SPAN_DECLARE(int) v42bis_compress_flush(v42bis_state_t *ss) -{ - v42bis_comp_state_t *s; - int len; - - s = &ss->compress; - if (s->update_at) - return 0; - if (s->last_matched) - { - len = s->string_length; - send_encoded_data(s, s->last_matched); - s->flushed_length += len; - } - if (!s->transparent) - { - s->update_at = s->last_matched; - s->last_matched = 0; - s->flushed_length = 0; - push_compressed_code(s, V42BIS_FLUSH); - push_octet_alignment(s); - } - flush_octets(s); - return 0; -} -/*- End of function --------------------------------------------------------*/ - -SPAN_DECLARE(int) v42bis_decompress(v42bis_state_t *ss, const uint8_t buf[], int len) -{ - v42bis_comp_state_t *s; - int i; - int j; - int yyy; - uint16_t code; - uint16_t p; - uint8_t ch; - uint8_t in; - - s = &ss->decompress; - if (!s->v42bis_parm_p0) - { - /* Compression is off - just push the incoming data out */ - push_octets(s, buf, len); - return 0; - } - for (i = 0; i < len; ) - { - if (s->transparent) - { - in = buf[i]; - if (s->escaped) - { - /* Command */ - s->escaped = FALSE; - switch (in) - { - case V42BIS_ECM: - /* Enter compressed mode */ - span_log(&ss->logging, SPAN_LOG_FLOW, "Hit V42BIS_ECM\n"); - send_string(s); - s->transparent = FALSE; - s->update_at = s->last_matched; - s->last_matched = 0; - i++; - continue; - case V42BIS_EID: - /* Escape symbol */ - span_log(&ss->logging, SPAN_LOG_FLOW, "Hit V42BIS_EID\n"); - in = s->escape_code; - s->escape_code += V42BIS_ESC_STEP; - break; - case V42BIS_RESET: - /* Reset dictionary */ - span_log(&ss->logging, SPAN_LOG_FLOW, "Hit V42BIS_RESET\n"); - /* TODO: */ - send_string(s); - dictionary_init(s); - i++; - continue; - default: - span_log(&ss->logging, SPAN_LOG_FLOW, "Hit V42BIS_???? - %" PRIu32 "\n", in); - return -1; - } - } - else if (in == s->escape_code) - { - s->escaped = TRUE; - i++; - continue; - } - - yyy = TRUE; - for (j = 0; j < 2 && yyy; j++) - { - if (s->update_at) - { - if (match_octet(s, s->update_at, in) == 0) - s->last_added = add_octet_to_dictionary(s, s->update_at, in); - s->update_at = 0; - } - - code = match_octet(s, s->last_matched, in); - if (code == 0) - { - s->update_at = s->last_matched; - send_string(s); - s->last_matched = 0; - } - else if (code == s->last_added) - { - s->last_added = 0; - send_string(s); - s->last_matched = 0; - } - else - { - s->last_matched = code; - s->string[s->string_length++] = in; - if (s->string_length + s->flushed_length == s->v42bis_parm_n7) - { - send_string(s); - s->last_matched = 0; - } - i++; - yyy = FALSE; - } - } - } - else - { - /* Get code from input */ - while (s->bit_count < s->v42bis_parm_c2 && i < len) - { - s->bit_buffer |= buf[i++] << s->bit_count; - s->bit_count += 8; - } - if (s->bit_count < s->v42bis_parm_c2) - continue; - code = s->bit_buffer & ((1 << s->v42bis_parm_c2) - 1); - s->bit_buffer >>= s->v42bis_parm_c2; - s->bit_count -= s->v42bis_parm_c2; - - if (code < V42BIS_N6) - { - /* We have a control code. */ - switch (code) - { - case V42BIS_ETM: - /* Enter transparent mode */ - span_log(&ss->logging, SPAN_LOG_FLOW, "Hit V42BIS_ETM\n"); - s->bit_count = 0; - s->transparent = TRUE; - s->last_matched = 0; - s->last_added = 0; - break; - case V42BIS_FLUSH: - /* Flush signal */ - span_log(&ss->logging, SPAN_LOG_FLOW, "Hit V42BIS_FLUSH\n"); - s->bit_count = 0; - break; - case V42BIS_STEPUP: - /* Increase code word size */ - span_log(&ss->logging, SPAN_LOG_FLOW, "Hit V42BIS_STEPUP\n"); - s->v42bis_parm_c2++; - s->v42bis_parm_c3 <<= 1; - if (s->v42bis_parm_c2 > (s->v42bis_parm_n2 >> 3)) - return -1; - break; - } - continue; - } - /* Regular codeword */ - if (code == s->v42bis_parm_c1) - return -1; - expand_codeword_to_string(s, code); - if (s->update_at) - { - ch = s->string[0]; - if ((p = match_octet(s, s->update_at, ch)) == 0) - { - s->last_added = add_octet_to_dictionary(s, s->update_at, ch); - if (code == s->v42bis_parm_c1) - return -1; - } - else if (p == s->last_added) - { - s->last_added = 0; - } - } - s->update_at = ((s->string_length + s->flushed_length) == s->v42bis_parm_n7) ? 0 : code; - /* Allow for any escapes which may be in this string */ - for (j = 0; j < s->string_length; j++) - { - if (s->string[j] == s->escape_code) - s->escape_code += V42BIS_ESC_STEP; - } - send_string(s); - } - } - return 0; -} -/*- End of function --------------------------------------------------------*/ - -SPAN_DECLARE(int) v42bis_decompress_flush(v42bis_state_t *ss) -{ - v42bis_comp_state_t *s; - int len; - - s = &ss->decompress; - len = s->string_length; - send_string(s); - s->flushed_length += len; - flush_octets(s); - return 0; -} -/*- End of function --------------------------------------------------------*/ - -SPAN_DECLARE(void) v42bis_compression_control(v42bis_state_t *s, int mode) -{ - s->compress.compression_mode = mode; -} -/*- End of function --------------------------------------------------------*/ - -SPAN_DECLARE(v42bis_state_t *) v42bis_init(const void *ctx, - v42bis_state_t *s, - int negotiated_p0, - int negotiated_p1, - int negotiated_p2, - put_msg_func_t encode_handler, - void *encode_user_data, - int max_encode_len, - put_msg_func_t decode_handler, - void *decode_user_data, - int max_decode_len) -{ - int ret; - - if (negotiated_p1 < V42BIS_MIN_DICTIONARY_SIZE || negotiated_p1 > 65535) - return NULL; - if (negotiated_p2 < V42BIS_MIN_STRING_SIZE || negotiated_p2 > V42BIS_MAX_STRING_SIZE) - return NULL; - if (s == NULL) - { - if ((s = (v42bis_state_t *) talloc_zero_size(ctx,sizeof(*s))) == NULL) - return NULL; - } - memset(s, 0, sizeof(*s)); - span_log_init(&s->logging, SPAN_LOG_NONE, NULL); - span_log_set_protocol(&s->logging, "V.42bis"); - - if ((ret = v42bis_comp_init(&s->compress, negotiated_p1, negotiated_p2, encode_handler, encode_user_data, max_encode_len))) - return NULL; - if ((ret = v42bis_comp_init(&s->decompress, negotiated_p1, negotiated_p2, decode_handler, decode_user_data, max_decode_len))) - { - comp_exit(&s->compress); - return NULL; - } - s->compress.v42bis_parm_p0 = negotiated_p0 & 2; - s->decompress.v42bis_parm_p0 = negotiated_p0 & 1; - - return s; -} -/*- End of function --------------------------------------------------------*/ - -SPAN_DECLARE(int) v42bis_release(v42bis_state_t *s) -{ - return 0; -} -/*- End of function --------------------------------------------------------*/ - -SPAN_DECLARE(int) v42bis_free(v42bis_state_t *s) -{ - comp_exit(&s->compress); - comp_exit(&s->decompress); - talloc_free(s); - return 0; -} -/*- End of function --------------------------------------------------------*/ -/*- End of file ------------------------------------------------------------*/ diff --git a/openbsc/src/ipaccess/Makefile.am b/openbsc/src/ipaccess/Makefile.am deleted file mode 100644 index 4dfe2473..00000000 --- a/openbsc/src/ipaccess/Makefile.am +++ /dev/null @@ -1,66 +0,0 @@ -AM_CPPFLAGS = \ - $(all_includes) \ - -I$(top_srcdir)/include \ - -I$(top_builddir) \ - $(NULL) - -AM_CFLAGS = \ - -Wall \ - $(LIBOSMOCORE_CFLAGS) \ - $(LIBOSMOGSM_CFLAGS) \ - $(LIBOSMOABIS_CFLAGS) \ - $(COVERAGE_CFLAGS) \ - $(NULL) - -AM_LDFLAGS = \ - $(COVERAGE_LDFLAGS) \ - $(NULL) - -OSMO_LIBS = \ - $(LIBOSMOCORE_LIBS) \ - $(LIBOSMOGSM_LIBS) \ - $(LIBOSMOABIS_LIBS) \ - $(NULL) - -bin_PROGRAMS = \ - abisip-find \ - ipaccess-config \ - ipaccess-proxy \ - $(NULL) - -abisip_find_LDADD = \ - $(top_builddir)/src/libbsc/libbsc.a \ - $(top_builddir)/src/libtrau/libtrau.a \ - $(top_builddir)/src/libcommon/libcommon.a \ - $(OSMO_LIBS) \ - $(NULL) - -abisip_find_SOURCES = \ - abisip-find.c \ - $(NULL) - -ipaccess_config_SOURCES = \ - ipaccess-config.c \ - ipaccess-firmware.c \ - network_listen.c \ - $(NULL) - -# FIXME: resolve the bogus dependencies patched around here: -ipaccess_config_LDADD = \ - $(top_builddir)/src/libbsc/libbsc.a \ - $(top_builddir)/src/libcommon-cs/libcommon-cs.a \ - $(top_builddir)/src/libtrau/libtrau.a \ - $(top_builddir)/src/libcommon/libcommon.a \ - $(OSMO_LIBS) \ - $(NULL) - -ipaccess_proxy_SOURCES = \ - ipaccess-proxy.c \ - $(NULL) - -ipaccess_proxy_LDADD = \ - $(top_builddir)/src/libbsc/libbsc.a \ - $(top_builddir)/src/libtrau/libtrau.a \ - $(top_builddir)/src/libcommon/libcommon.a \ - $(OSMO_LIBS) \ - $(NULL) diff --git a/openbsc/src/ipaccess/abisip-find.c b/openbsc/src/ipaccess/abisip-find.c deleted file mode 100644 index 21d9f229..00000000 --- a/openbsc/src/ipaccess/abisip-find.c +++ /dev/null @@ -1,216 +0,0 @@ -/* ip.access nanoBTS configuration tool */ - -/* (C) 2009-2010 by Harald Welte - * All Rights Reserved - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation; either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - * - */ - -#include -#include -#include -#include -#include -#include - - -#include -#include -#include -#include -#include - -static int udp_sock(const char *ifname) -{ - int fd, rc, bc = 1; - struct sockaddr_in sa; - - fd = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP); - if (fd < 0) - return fd; - - if (ifname) { -#ifdef __FreeBSD__ - rc = setsockopt(fd, SOL_SOCKET, IP_RECVIF, ifname, - strlen(ifname)); -#else - rc = setsockopt(fd, SOL_SOCKET, SO_BINDTODEVICE, ifname, - strlen(ifname)); -#endif - if (rc < 0) - goto err; - } - - memset(&sa, 0, sizeof(sa)); - sa.sin_family = AF_INET; - sa.sin_port = htons(3006); - sa.sin_addr.s_addr = INADDR_ANY; - - rc = bind(fd, (struct sockaddr *)&sa, sizeof(sa)); - if (rc < 0) - goto err; - - rc = setsockopt(fd, SOL_SOCKET, SO_BROADCAST, &bc, sizeof(bc)); - if (rc < 0) - goto err; - -#if 0 - /* we cannot bind, since the response packets don't come from - * the broadcast address */ - sa.sin_family = AF_INET; - sa.sin_port = htons(3006); - inet_aton("255.255.255.255", &sa.sin_addr); - - rc = connect(fd, (struct sockaddr *)&sa, sizeof(sa)); - if (rc < 0) - goto err; -#endif - return fd; - -err: - close(fd); - return rc; -} - -const unsigned char find_pkt[] = { 0x00, 0x0b+8, IPAC_PROTO_IPACCESS, 0x00, - IPAC_MSGT_ID_GET, - 0x01, IPAC_IDTAG_MACADDR, - 0x01, IPAC_IDTAG_IPADDR, - 0x01, IPAC_IDTAG_UNIT, - 0x01, IPAC_IDTAG_LOCATION1, - 0x01, IPAC_IDTAG_LOCATION2, - 0x01, IPAC_IDTAG_EQUIPVERS, - 0x01, IPAC_IDTAG_SWVERSION, - 0x01, IPAC_IDTAG_UNITNAME, - 0x01, IPAC_IDTAG_SERNR, - }; - - -static int bcast_find(int fd) -{ - struct sockaddr_in sa; - - sa.sin_family = AF_INET; - sa.sin_port = htons(3006); - inet_aton("255.255.255.255", &sa.sin_addr); - - return sendto(fd, find_pkt, sizeof(find_pkt), 0, (struct sockaddr *) &sa, sizeof(sa)); -} - -static int parse_response(unsigned char *buf, int len) -{ - uint8_t t_len; - uint8_t t_tag; - uint8_t *cur = buf; - - while (cur < buf + len) { - t_len = *cur++; - t_tag = *cur++; - - printf("%s='%s' ", ipa_ccm_idtag_name(t_tag), cur); - - cur += t_len; - } - printf("\n"); - return 0; -} - -static int read_response(int fd) -{ - unsigned char buf[255]; - struct sockaddr_in sa; - int len; - socklen_t sa_len = sizeof(sa); - - len = recvfrom(fd, buf, sizeof(buf), 0, (struct sockaddr *)&sa, &sa_len); - if (len < 0) - return len; - - /* 2 bytes length, 1 byte protocol */ - if (buf[2] != IPAC_PROTO_IPACCESS) - return 0; - - if (buf[4] != IPAC_MSGT_ID_RESP) - return 0; - - return parse_response(buf+6, len-6); -} - -static int bfd_cb(struct osmo_fd *bfd, unsigned int flags) -{ - if (flags & BSC_FD_READ) - return read_response(bfd->fd); - if (flags & BSC_FD_WRITE) { - bfd->when &= ~BSC_FD_WRITE; - return bcast_find(bfd->fd); - } - return 0; -} - -static struct osmo_timer_list timer; - -static void timer_cb(void *_data) -{ - struct osmo_fd *bfd = _data; - - bfd->when |= BSC_FD_WRITE; - - osmo_timer_schedule(&timer, 5, 0); -} - -int main(int argc, char **argv) -{ - struct osmo_fd bfd; - char *ifname = NULL; - int rc; - - printf("abisip-find (C) 2009 by Harald Welte\n"); - printf("This is FREE SOFTWARE with ABSOLUTELY NO WARRANTY\n\n"); - - if (argc < 2) { - fprintf(stdout, "you might need to specify the outgoing\n" - " network interface, e.g. ``%s eth0''\n", argv[0]); - } else { - ifname = argv[1]; - } - - bfd.cb = bfd_cb; - bfd.when = BSC_FD_READ | BSC_FD_WRITE; - bfd.fd = udp_sock(ifname); - if (bfd.fd < 0) { - perror("Cannot create local socket for broadcast udp"); - exit(1); - } - - rc = osmo_fd_register(&bfd); - if (rc < 0) { - fprintf(stderr, "Cannot register FD\n"); - exit(1); - } - - osmo_timer_setup(&timer, timer_cb, &bfd); - osmo_timer_schedule(&timer, 5, 0); - - printf("Trying to find ip.access BTS by broadcast UDP...\n"); - - while (1) { - rc = osmo_select_main(0); - if (rc < 0) - exit(3); - } - - exit(0); -} - diff --git a/openbsc/src/ipaccess/ipaccess-config.c b/openbsc/src/ipaccess/ipaccess-config.c deleted file mode 100644 index 6822c06a..00000000 --- a/openbsc/src/ipaccess/ipaccess-config.c +++ /dev/null @@ -1,1019 +0,0 @@ -/* ip.access nanoBTS configuration tool */ - -/* (C) 2009-2010 by Harald Welte - * (C) 2009-2011 by Holger Hans Peter Freyther - * (C) 2009-2010 by On-Waves - * All Rights Reserved - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation; either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - * - */ - -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include - - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -struct gsm_network *bsc_gsmnet; - -static int net_listen_testnr; -static int restart; -static char *prim_oml_ip; -static char *bts_ip_addr, *bts_ip_mask, *bts_ip_gw; -static char *unit_id; -static uint16_t nv_flags; -static uint16_t nv_mask; -static char *software = NULL; -static int sw_load_state = 0; -static int oml_state = 0; -static int dump_files = 0; -static char *firmware_analysis = NULL; -static int found_trx = 0; -static int loop_tests = 0; - -static void *tall_ctx_config = NULL; -static struct abis_nm_sw_desc *sw_load1 = NULL; -static struct abis_nm_sw_desc *sw_load2 = NULL; - -/* -static uint8_t prim_oml_attr[] = { 0x95, 0x00, 7, 0x88, 192, 168, 100, 11, 0x00, 0x00 }; -static uint8_t unit_id_attr[] = { 0x91, 0x00, 9, '2', '3', '4', '2', '/' , '0', '/', '0', 0x00 }; -*/ - -extern int ipaccess_fd_cb(struct osmo_fd *bfd, unsigned int what); -extern struct e1inp_line_ops ipaccess_e1inp_line_ops; - -/* Actively connect to a BTS. Currently used by ipaccess-config.c */ -static int ipaccess_connect(struct e1inp_line *line, struct sockaddr_in *sa) -{ - struct e1inp_ts *e1i_ts = &line->ts[0]; - struct osmo_fd *bfd = &e1i_ts->driver.ipaccess.fd; - int ret, on = 1; - - bfd->fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); - bfd->cb = ipaccess_fd_cb; - bfd->when = BSC_FD_READ | BSC_FD_WRITE; - bfd->data = line; - bfd->priv_nr = E1INP_SIGN_OML; - - if (bfd->fd < 0) { - LOGP(DLINP, LOGL_ERROR, "could not create TCP socket.\n"); - return -EIO; - } - - ret = setsockopt(bfd->fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)); - if (ret < 0) { - LOGP(DLINP, LOGL_ERROR, "could not set socket option\n"); - close(bfd->fd); - return -EIO; - } - - ret = connect(bfd->fd, (struct sockaddr *) sa, sizeof(*sa)); - if (ret < 0) { - LOGP(DLINP, LOGL_ERROR, "could not connect socket\n"); - close(bfd->fd); - return ret; - } - - ret = osmo_fd_register(bfd); - if (ret < 0) { - close(bfd->fd); - return ret; - } - return ret; - //return e1inp_line_register(line); -} - -/* configure pseudo E1 line in ip.access style and connect to BTS */ -static int ia_config_connect(struct gsm_bts *bts, struct sockaddr_in *sin) -{ - struct e1inp_line *line; - struct e1inp_ts *sign_ts, *rsl_ts; - struct e1inp_sign_link *oml_link, *rsl_link; - - line = talloc_zero(tall_bsc_ctx, struct e1inp_line); - if (!line) - return -ENOMEM; - - line->driver = e1inp_driver_find("ipa"); - if (!line->driver) { - fprintf(stderr, "cannot `ipa' driver, giving up.\n"); - return -EINVAL; - } - line->ops = &ipaccess_e1inp_line_ops; - - /* create E1 timeslots for signalling and TRAU frames */ - e1inp_ts_config_sign(&line->ts[1-1], line); - e1inp_ts_config_sign(&line->ts[2-1], line); - - /* create signalling links for TS1 */ - sign_ts = &line->ts[1-1]; - rsl_ts = &line->ts[2-1]; - oml_link = e1inp_sign_link_create(sign_ts, E1INP_SIGN_OML, - bts->c0, 0xff, 0); - rsl_link = e1inp_sign_link_create(rsl_ts, E1INP_SIGN_RSL, - bts->c0, 0, 0); - - /* create back-links from bts/trx */ - bts->oml_link = oml_link; - bts->c0->rsl_link = rsl_link; - - /* default port at BTS for incoming connections is 3006 */ - if (sin->sin_port == 0) - sin->sin_port = htons(3006); - - return ipaccess_connect(line, sin); -} - -/* - * Callback function for NACK on the OML NM - * - * Currently we send the config requests but don't check the - * result. The nanoBTS will send us a NACK when we did something the - * BTS didn't like. - */ -static int ipacc_msg_nack(uint8_t mt) -{ - fprintf(stderr, "Failure to set attribute. This seems fatal\n"); - exit(-1); - return 0; -} - -static void check_restart_or_exit(struct gsm_bts_trx *trx) -{ - if (restart) { - abis_nm_ipaccess_restart(trx); - } else { - exit(0); - } -} - -static int ipacc_msg_ack(uint8_t mt, struct gsm_bts_trx *trx) -{ - if (sw_load_state == 1) { - fprintf(stderr, "The new software is activaed.\n"); - check_restart_or_exit(trx); - } else if (oml_state == 1) { - fprintf(stderr, "Set the NV Attributes.\n"); - check_restart_or_exit(trx); - } - - return 0; -} - -static const uint8_t phys_conf_min[] = { 0x02 }; - -static uint16_t build_physconf(uint8_t *physconf_buf, const struct rxlev_stats *st) -{ - uint16_t *whitelist = (uint16_t *) (physconf_buf + 4); - int num_arfcn; - unsigned int arfcnlist_size; - - /* Create whitelist from rxlevels */ - physconf_buf[0] = phys_conf_min[0]; - physconf_buf[1] = NM_IPAC_EIE_ARFCN_WHITE; - num_arfcn = ipac_rxlevstat2whitelist(whitelist, st, 0, 100); - arfcnlist_size = num_arfcn * 2; - *((uint16_t *) (physconf_buf+2)) = htons(arfcnlist_size); - DEBUGP(DNM, "physconf_buf (%s)\n", osmo_hexdump(physconf_buf, arfcnlist_size+4)); - return arfcnlist_size+4; -} - -static int nwl_sig_cb(unsigned int subsys, unsigned int signal, - void *handler_data, void *signal_data) -{ - struct gsm_bts_trx *trx; - uint8_t physconf_buf[2*NUM_ARFCNS+16]; - uint16_t physconf_len; - - switch (signal) { - case S_IPAC_NWL_COMPLETE: - trx = signal_data; - DEBUGP(DNM, "received S_IPAC_NWL_COMPLETE signal\n"); - switch (trx->ipaccess.test_nr) { - case NM_IPACC_TESTNO_CHAN_USAGE: - /* Dump RxLev results */ - //rxlev_stat_dump(&trx->ipaccess.rxlev_stat); - /* Create whitelist from results */ - physconf_len = build_physconf(physconf_buf, - &trx->ipaccess.rxlev_stat); - /* Start next test abbout BCCH channel usage */ - ipac_nwl_test_start(trx, NM_IPACC_TESTNO_BCCH_CHAN_USAGE, - physconf_buf, physconf_len); - break; - case NM_IPACC_TESTNO_BCCH_CHAN_USAGE: - /* Dump BCCH RxLev results */ - //rxlev_stat_dump(&trx->ipaccess.rxlev_stat); - /* Create whitelist from results */ - physconf_len = build_physconf(physconf_buf, - &trx->ipaccess.rxlev_stat); - /* Start next test about BCCH info */ - ipac_nwl_test_start(trx, NM_IPACC_TESTNO_BCCH_INFO, - physconf_buf, physconf_len); - break; - case NM_IPACC_TESTNO_BCCH_INFO: - /* re-start full process with CHAN_USAGE */ - if (loop_tests) { - DEBUGP(DNM, "starting next test cycle\n"); - ipac_nwl_test_start(trx, net_listen_testnr, phys_conf_min, - sizeof(phys_conf_min)); - } else { - exit(0); - } - break; - } - break; - } - return 0; -} - -static int nm_state_event(int evt, uint8_t obj_class, void *obj, - struct gsm_nm_state *old_state, struct gsm_nm_state *new_state, - struct abis_om_obj_inst *obj_inst); - -static int nm_sig_cb(unsigned int subsys, unsigned int signal, - void *handler_data, void *signal_data) -{ - struct ipacc_ack_signal_data *ipacc_data; - struct nm_statechg_signal_data *nsd; - - switch (signal) { - case S_NM_IPACC_NACK: - ipacc_data = signal_data; - return ipacc_msg_nack(ipacc_data->msg_type); - case S_NM_IPACC_ACK: - ipacc_data = signal_data; - return ipacc_msg_ack(ipacc_data->msg_type, ipacc_data->trx); - case S_NM_IPACC_RESTART_ACK: - printf("The BTS has acked the restart. Exiting.\n"); - exit(0); - break; - case S_NM_IPACC_RESTART_NACK: - printf("The BTS has nacked the restart. Exiting.\n"); - exit(0); - break; - case S_NM_STATECHG_OPER: - case S_NM_STATECHG_ADM: - nsd = signal_data; - nm_state_event(signal, nsd->obj_class, nsd->obj, nsd->old_state, - nsd->new_state, nsd->obj_inst); - break; - default: - break; - } - - return 0; -} - -/* callback function passed to the ABIS OML code */ -static int percent; -static int percent_old; -static int swload_cbfn(unsigned int hook, unsigned int event, struct msgb *_msg, - void *data, void *param) -{ - struct msgb *msg; - struct gsm_bts_trx *trx; - - if (hook != GSM_HOOK_NM_SWLOAD) - return 0; - - trx = (struct gsm_bts_trx *) data; - - switch (event) { - case NM_MT_LOAD_INIT_ACK: - fprintf(stdout, "Software Load Initiate ACK\n"); - break; - case NM_MT_LOAD_INIT_NACK: - fprintf(stderr, "ERROR: Software Load Initiate NACK\n"); - exit(5); - break; - case NM_MT_LOAD_END_ACK: - fprintf(stderr, "LOAD END ACK..."); - /* now make it the default */ - sw_load_state = 1; - - msg = msgb_alloc(1024, "sw: nvattr"); - msg->l2h = msgb_put(msg, 3); - msg->l3h = &msg->l2h[3]; - - /* activate software */ - if (sw_load1) - abis_nm_put_sw_desc(msg, sw_load1, true); - - if (sw_load2) - abis_nm_put_sw_desc(msg, sw_load2, true); - - /* fill in the data */ - msg->l2h[0] = NM_ATT_IPACC_CUR_SW_CFG; - msg->l2h[1] = msgb_l3len(msg) >> 8; - msg->l2h[2] = msgb_l3len(msg) & 0xff; - printf("Foo l2h: %p l3h: %p... length l2: %u l3: %u\n", msg->l2h, msg->l3h, msgb_l2len(msg), msgb_l3len(msg)); - abis_nm_ipaccess_set_nvattr(trx, msg->l2h, msgb_l2len(msg)); - msgb_free(msg); - break; - case NM_MT_LOAD_END_NACK: - fprintf(stderr, "ERROR: Software Load End NACK\n"); - exit(3); - break; - case NM_MT_ACTIVATE_SW_NACK: - fprintf(stderr, "ERROR: Activate Software NACK\n"); - exit(4); - break; - case NM_MT_ACTIVATE_SW_ACK: - break; - case NM_MT_LOAD_SEG_ACK: - percent = abis_nm_software_load_status(trx->bts); - if (percent > percent_old) - printf("Software Download Progress: %d%%\n", percent); - percent_old = percent; - break; - case NM_MT_LOAD_ABORT: - fprintf(stderr, "ERROR: Load aborted by the BTS.\n"); - exit(6); - break; - } - return 0; -} - -static void nv_put_ip_if_cfg(struct msgb *nmsg, uint32_t ip, uint32_t mask) -{ - msgb_put_u8(nmsg, NM_ATT_IPACC_IP_IF_CFG); - - msgb_put_u32(nmsg, ip); - msgb_put_u32(nmsg, mask); -} - -static void nv_put_gw_cfg(struct msgb *nmsg, uint32_t addr, uint32_t mask, uint32_t gw) -{ - msgb_put_u8(nmsg, NM_ATT_IPACC_IP_GW_CFG); - msgb_put_u32(nmsg, addr); - msgb_put_u32(nmsg, mask); - msgb_put_u32(nmsg, gw); -} - -static void nv_put_unit_id(struct msgb *nmsg, const char *unit_id) -{ - msgb_tl16v_put(nmsg, NM_ATT_IPACC_UNIT_ID, strlen(unit_id)+1, - (const uint8_t *)unit_id); -} - -static void nv_put_prim_oml(struct msgb *nmsg, uint32_t ip, uint16_t port) -{ - int len; - - /* 0x88 + IP + port */ - len = 1 + sizeof(ip) + sizeof(port); - - msgb_put_u8(nmsg, NM_ATT_IPACC_PRIM_OML_CFG_LIST); - msgb_put_u16(nmsg, len); - - msgb_put_u8(nmsg, 0x88); - - /* IP address */ - msgb_put_u32(nmsg, ip); - - /* port number */ - msgb_put_u16(nmsg, port); -} - -static void nv_put_flags(struct msgb *nmsg, uint16_t nv_flags, uint16_t nv_mask) -{ - msgb_put_u8(nmsg, NM_ATT_IPACC_NV_FLAGS); - msgb_put_u16(nmsg, sizeof(nv_flags) + sizeof(nv_mask)); - msgb_put_u8(nmsg, nv_flags & 0xff); - msgb_put_u8(nmsg, nv_mask & 0xff); - msgb_put_u8(nmsg, nv_flags >> 8); - msgb_put_u8(nmsg, nv_mask >> 8); -} - -/* human-readable test names for the ip.access tests */ -static const struct value_string ipa_test_strs[] = { - { 64, "ccch-usage" }, - { 65, "bcch-usage" }, - { 66, "freq-sync" }, - { 67, "rtp-usage" }, - { 68, "rtp-perf" }, - { 69, "gprs-ccch" }, - { 70, "pccch-usage" }, - { 71, "gprs-usage" }, - { 72, "esta-mf" }, - { 73, "uplink-mf" }, - { 74, "dolink-mf" }, - { 75, "tbf-details" }, - { 76, "tbf-usage" }, - { 77, "llc-data" }, - { 78, "pdch-usage" }, - { 79, "power-control" }, - { 80, "link-adaption" }, - { 81, "tch-usage" }, - { 82, "amr-mf" }, - { 83, "rtp-multiplex-perf" }, - { 84, "rtp-multiplex-usage" }, - { 85, "srtp-multiplex-usage" }, - { 86, "abis-traffic" }, - { 89, "gprs-multiplex-perf" }, - { 90, "gprs-multiplex-usage" }, - { 0, NULL }, -}; - -/* human-readable names for the ip.access nanoBTS NVRAM Flags */ -static const struct value_string ipa_nvflag_strs[] = { - { 0x0001, "static-ip" }, - { 0x0002, "static-gw" }, - { 0x0004, "no-dhcp-vsi" }, - { 0x0008, "dhcp-enabled" }, - { 0x0040, "led-disabled" }, - { 0x0100, "secondary-oml-enabled" }, - { 0x0200, "diag-enabled" }, - { 0x0400, "cli-enabled" }, - { 0x0800, "http-enabled" }, - { 0x1000, "post-enabled" }, - { 0x2000, "snmp-enabled" }, - { 0, NULL } -}; - -/* set the flags in flags/mask according to a string-identified flag and 'enable' */ -static int ipa_nvflag_set(uint16_t *flags, uint16_t *mask, const char *name, int en) -{ - int rc; - rc = get_string_value(ipa_nvflag_strs, name); - if (rc < 0) - return rc; - - *mask |= rc; - if (en) - *flags |= rc; - else - *flags &= ~rc; - - return 0; -} - -static void bootstrap_om(struct gsm_bts_trx *trx) -{ - struct msgb *nmsg = msgb_alloc(1024, "nested msgb"); - int need_to_set_attr = 0; - int len; - - printf("OML link established using TRX %d\n", trx->nr); - - if (unit_id) { - len = strlen(unit_id); - if (len > nmsg->data_len-10) - goto out_err; - printf("setting Unit ID to '%s'\n", unit_id); - nv_put_unit_id(nmsg, unit_id); - need_to_set_attr = 1; - } - if (prim_oml_ip) { - struct in_addr ia; - - if (!inet_aton(prim_oml_ip, &ia)) { - fprintf(stderr, "invalid IP address: %s\n", - prim_oml_ip); - goto out_err; - } - - printf("setting primary OML link IP to '%s'\n", inet_ntoa(ia)); - nv_put_prim_oml(nmsg, ntohl(ia.s_addr), 0); - need_to_set_attr = 1; - } - if (nv_mask) { - printf("setting NV Flags/Mask to 0x%04x/0x%04x\n", - nv_flags, nv_mask); - nv_put_flags(nmsg, nv_flags, nv_mask); - need_to_set_attr = 1; - } - if (bts_ip_addr && bts_ip_mask) { - struct in_addr ia_addr, ia_mask; - - if (!inet_aton(bts_ip_addr, &ia_addr)) { - fprintf(stderr, "invalid IP address: %s\n", - bts_ip_addr); - goto out_err; - } - - if (!inet_aton(bts_ip_mask, &ia_mask)) { - fprintf(stderr, "invalid IP address: %s\n", - bts_ip_mask); - goto out_err; - } - - printf("setting static IP Address/Mask\n"); - nv_put_ip_if_cfg(nmsg, ntohl(ia_addr.s_addr), ntohl(ia_mask.s_addr)); - need_to_set_attr = 1; - } - if (bts_ip_gw) { - struct in_addr ia_gw; - - if (!inet_aton(bts_ip_gw, &ia_gw)) { - fprintf(stderr, "invalid IP address: %s\n", - bts_ip_gw); - goto out_err; - } - - printf("setting static IP Gateway\n"); - /* we only set the default gateway with zero addr/mask */ - nv_put_gw_cfg(nmsg, 0, 0, ntohl(ia_gw.s_addr)); - need_to_set_attr = 1; - } - - if (need_to_set_attr) { - abis_nm_ipaccess_set_nvattr(trx, nmsg->head, nmsg->len); - oml_state = 1; - } - - if (restart && !prim_oml_ip && !software) { - printf("restarting BTS\n"); - abis_nm_ipaccess_restart(trx); - } - -out_err: - msgb_free(nmsg); -} - -static int nm_state_event(int evt, uint8_t obj_class, void *obj, - struct gsm_nm_state *old_state, struct gsm_nm_state *new_state, - struct abis_om_obj_inst *obj_inst) -{ - if (obj_class == NM_OC_BASEB_TRANSC) { - if (!found_trx && obj_inst->trx_nr != 0xff) { - struct gsm_bts_trx *trx = container_of(obj, struct gsm_bts_trx, bb_transc); - bootstrap_om(trx); - found_trx = 1; - } - } else if (evt == S_NM_STATECHG_OPER && - obj_class == NM_OC_RADIO_CARRIER && - new_state->availability == 3) { - struct gsm_bts_trx *trx = obj; - - if (net_listen_testnr) - ipac_nwl_test_start(trx, net_listen_testnr, - phys_conf_min, sizeof(phys_conf_min)); - else if (software) { - int rc; - printf("Attempting software upload with '%s'\n", software); - rc = abis_nm_software_load(trx->bts, trx->nr, software, 19, 0, swload_cbfn, trx); - if (rc < 0) { - fprintf(stderr, "Failed to start software load\n"); - exit(-3); - } - } - } - return 0; -} - -static struct abis_nm_sw_desc *create_swload(struct sdp_header *header) -{ - struct abis_nm_sw_desc *load; - - load = talloc_zero(tall_ctx_config, struct abis_nm_sw_desc); - - osmo_strlcpy((char *)load->file_id, header->firmware_info.sw_part, - sizeof(load->file_id)); - load->file_id_len = strlen((char*)load->file_id) + 1; - - osmo_strlcpy((char *)load->file_version, header->firmware_info.version, - sizeof(load->file_version)); - load->file_version_len = strlen((char*)load->file_version) + 1; - - return load; -} - -static int find_sw_load_params(const char *filename) -{ - struct stat stat; - struct sdp_header *header; - struct llist_head *entry; - int fd; - void *tall_firm_ctx = 0; - - entry = talloc_zero(tall_firm_ctx, struct llist_head); - INIT_LLIST_HEAD(entry); - - fd = open(filename, O_RDONLY); - if (!fd) { - perror("nada"); - return -1; - } - - /* verify the file */ - if (fstat(fd, &stat) == -1) { - perror("Can not stat the file"); - close(fd); - return -1; - } - - ipaccess_analyze_file(fd, stat.st_size, 0, entry); - if (close(fd) != 0) { - perror("Close failed.\n"); - return -1; - } - - /* try to find what we are looking for */ - llist_for_each_entry(header, entry, entry) { - if (ntohs(header->firmware_info.more_more_magic) == 0x1000) { - sw_load1 = create_swload(header); - } else if (ntohs(header->firmware_info.more_more_magic) == 0x2001) { - sw_load2 = create_swload(header); - } - } - - if (!sw_load1 || !sw_load2) { - fprintf(stderr, "Did not find data.\n"); - talloc_free(tall_firm_ctx); - return -1; - } - - talloc_free(tall_firm_ctx); - return 0; -} - -static void dump_entry(struct sdp_header_item *sub_entry, int part, int fd) -{ - int out_fd; - int copied; - char filename[4096]; - off_t target; - - if (!dump_files) - return; - - if (sub_entry->header_entry.something1 == 0) - return; - - snprintf(filename, sizeof(filename), "part.%d", part++); - out_fd = open(filename, O_WRONLY | O_CREAT, 0660); - if (out_fd < 0) { - perror("Can not dump firmware"); - return; - } - - target = sub_entry->absolute_offset + ntohl(sub_entry->header_entry.start) + 4; - if (lseek(fd, target, SEEK_SET) != target) { - perror("seek failed"); - close(out_fd); - return; - } - - for (copied = 0; copied < ntohl(sub_entry->header_entry.length); ++copied) { - char c; - if (read(fd, &c, sizeof(c)) != sizeof(c)) { - perror("copy failed"); - break; - } - - if (write(out_fd, &c, sizeof(c)) != sizeof(c)) { - perror("write failed"); - break; - } - } - - close(out_fd); -} - -static void analyze_firmware(const char *filename) -{ - struct stat stat; - struct sdp_header *header; - struct sdp_header_item *sub_entry; - struct llist_head *entry; - int fd; - void *tall_firm_ctx = 0; - int part = 0; - - entry = talloc_zero(tall_firm_ctx, struct llist_head); - INIT_LLIST_HEAD(entry); - - printf("Opening possible firmware '%s'\n", filename); - fd = open(filename, O_RDONLY); - if (!fd) { - perror("nada"); - return; - } - - /* verify the file */ - if (fstat(fd, &stat) == -1) { - perror("Can not stat the file"); - close(fd); - return; - } - - ipaccess_analyze_file(fd, stat.st_size, 0, entry); - - llist_for_each_entry(header, entry, entry) { - printf("Printing header information:\n"); - printf("more_more_magic: 0x%x\n", ntohs(header->firmware_info.more_more_magic)); - printf("header_length: %u\n", ntohl(header->firmware_info.header_length)); - printf("file_length: %u\n", ntohl(header->firmware_info.file_length)); - printf("sw_part: %.20s\n", header->firmware_info.sw_part); - printf("text1: %.64s\n", header->firmware_info.text1); - printf("time: %.12s\n", header->firmware_info.time); - printf("date: %.14s\n", header->firmware_info.date); - printf("text2: %.10s\n", header->firmware_info.text2); - printf("version: %.20s\n", header->firmware_info.version); - printf("subitems...\n"); - - llist_for_each_entry(sub_entry, &header->header_list, entry) { - printf("\tsomething1: %u\n", sub_entry->header_entry.something1); - printf("\ttext1: %.64s\n", sub_entry->header_entry.text1); - printf("\ttime: %.12s\n", sub_entry->header_entry.time); - printf("\tdate: %.14s\n", sub_entry->header_entry.date); - printf("\ttext2: %.10s\n", sub_entry->header_entry.text2); - printf("\tversion: %.20s\n", sub_entry->header_entry.version); - printf("\tlength: %u\n", ntohl(sub_entry->header_entry.length)); - printf("\taddr1: 0x%x\n", ntohl(sub_entry->header_entry.addr1)); - printf("\taddr2: 0x%x\n", ntohl(sub_entry->header_entry.addr2)); - printf("\tstart: 0x%x\n", ntohl(sub_entry->header_entry.start)); - printf("\tabs. offset: 0x%lx\n", sub_entry->absolute_offset); - printf("\n\n"); - - dump_entry(sub_entry, part++, fd); - } - printf("\n\n"); - } - - if (close(fd) != 0) { - perror("Close failed.\n"); - return; - } - - talloc_free(tall_firm_ctx); -} - -static void print_usage(void) -{ - printf("Usage: ipaccess-config IP_OF_BTS\n"); -} - -static void print_help(void) -{ -#if 0 - printf("Commands for reading from the BTS:\n"); - printf(" -D --dump\t\t\tDump the BTS configuration\n"); - printf("\n"); -#endif - printf("Commands for writing to the BTS:\n"); - printf(" -u --unit-id UNIT_ID\t\tSet the Unit ID of the BTS\n"); - printf(" -o --oml-ip IP\t\tSet primary OML IP (IP of your BSC)\n"); - printf(" -i --ip-address IP/MASK\tSet static IP address + netmask of BTS\n"); - printf(" -g --ip-gateway IP\t\tSet static IP gateway of BTS\n"); - printf(" -r --restart\t\t\tRestart the BTS (after other operations)\n"); - printf(" -n --nvram-flags FLAGS/MASK\tSet NVRAM attributes\n"); - printf(" -S --nvattr-set FLAG\tSet one additional NVRAM attribute\n"); - printf(" -U --nvattr-unset FLAG\tSet one additional NVRAM attribute\n"); - printf(" -l --listen TESTNR\t\tPerform specified test number\n"); - printf(" -L --Listen TEST_NAME\t\tPerform specified test\n"); - printf(" -s --stream-id ID\t\tSet the IPA Stream Identifier for OML\n"); - printf(" -d --software FIRMWARE\tDownload firmware into BTS\n"); - printf("\n"); - printf("Miscellaneous commands:\n"); - printf(" -h --help\t\t\tthis text\n"); - printf(" -H --HELP\t\t\tPrint parameter details.\n"); - printf(" -f --firmware FIRMWARE\tProvide firmware information\n"); - printf(" -w --write-firmware\t\tThis will dump the firmware parts to the filesystem. Use with -f.\n"); - printf(" -p --loop\t\t\tLoop the tests executed with the --listen command.\n"); -} - -static void print_value_string(const struct value_string *val, int size) -{ - int i; - - for (i = 0; i < size - 1; ++i) { - char sep = val[i + 1].str == NULL ? '.' : ','; - printf("%s%c ", val[i].str, sep); - } - printf("\n"); -} - -static void print_options(void) -{ - - printf("Options for NVRAM (-S,-U):\n "); - print_value_string(&ipa_nvflag_strs[0], ARRAY_SIZE(ipa_nvflag_strs)); - - printf("Options for Tests (-L):\n "); - print_value_string(&ipa_test_strs[0], ARRAY_SIZE(ipa_test_strs)); -} - -extern void bts_model_nanobts_init(); - -int main(int argc, char **argv) -{ - struct gsm_bts *bts; - struct sockaddr_in sin; - int rc, option_index = 0, stream_id = 0xff; - - tall_ctx_config = talloc_named_const(NULL, 0, "ipaccess-config"); - msgb_talloc_ctx_init(tall_ctx_config, 0); - - osmo_init_logging(&log_info); - log_parse_category_mask(osmo_stderr_target, "DNM,0"); - bts_model_nanobts_init(); - - printf("ipaccess-config (C) 2009-2010 by Harald Welte and others\n"); - printf("This is FREE SOFTWARE with ABSOLUTELY NO WARRANTY\n\n"); - - while (1) { - int c; - unsigned long ul; - char *slash; - static struct option long_options[] = { - { "unit-id", 1, 0, 'u' }, - { "oml-ip", 1, 0, 'o' }, - { "ip-address", 1, 0, 'i' }, - { "ip-gateway", 1, 0, 'g' }, - { "restart", 0, 0, 'r' }, - { "nvram-flags", 1, 0, 'n' }, - { "nvattr-set", 1, 0, 'S' }, - { "nvattr-unset", 1, 0, 'U' }, - { "help", 0, 0, 'h' }, - { "HELP", 0, 0, 'H' }, - { "listen", 1, 0, 'l' }, - { "Listen", 1, 0, 'L' }, - { "stream-id", 1, 0, 's' }, - { "software", 1, 0, 'd' }, - { "firmware", 1, 0, 'f' }, - { "write-firmware", 0, 0, 'w' }, - { "disable-color", 0, 0, 'c'}, - { "loop", 0, 0, 'p' }, - { 0, 0, 0, 0 }, - }; - - c = getopt_long(argc, argv, "u:o:i:g:rn:S:U:l:L:hs:d:f:wcpH", long_options, - &option_index); - - if (c == -1) - break; - - switch (c) { - case 'u': - unit_id = optarg; - break; - case 'o': - prim_oml_ip = optarg; - break; - case 'i': - slash = strchr(optarg, '/'); - if (!slash) - exit(2); - bts_ip_addr = optarg; - *slash = 0; - bts_ip_mask = slash+1; - break; - case 'g': - bts_ip_gw = optarg; - break; - case 'r': - restart = 1; - break; - case 'n': - slash = strchr(optarg, '/'); - if (!slash) - exit(2); - ul = strtoul(optarg, NULL, 16); - nv_flags = ul & 0xffff; - ul = strtoul(slash+1, NULL, 16); - nv_mask = ul & 0xffff; - break; - case 'S': - if (ipa_nvflag_set(&nv_flags, &nv_mask, optarg, 1) < 0) - exit(2); - break; - case 'U': - if (ipa_nvflag_set(&nv_flags, &nv_mask, optarg, 0) < 0) - exit(2); - break; - case 'l': - net_listen_testnr = atoi(optarg); - break; - case 'L': - net_listen_testnr = get_string_value(ipa_test_strs, - optarg); - if (net_listen_testnr < 0) { - fprintf(stderr, - "The test '%s' is not known. Use -H to" - " see available tests.\n", optarg); - exit(2); - } - break; - case 's': - stream_id = atoi(optarg); - break; - case 'd': - software = strdup(optarg); - if (find_sw_load_params(optarg) != 0) - exit(0); - break; - case 'f': - firmware_analysis = optarg; - break; - case 'w': - dump_files = 1; - break; - case 'c': - log_set_use_color(osmo_stderr_target, 0); - break; - case 'p': - loop_tests = 1; - break; - case 'h': - print_usage(); - print_help(); - exit(0); - case 'H': - print_options(); - exit(0); - } - }; - - if (firmware_analysis) - analyze_firmware(firmware_analysis); - - if (optind >= argc) { - /* only warn if we have not done anything else */ - if (!firmware_analysis) - fprintf(stderr, "you have to specify the IP address of the BTS. Use --help for more information\n"); - exit(2); - } - libosmo_abis_init(tall_ctx_config); - - bsc_gsmnet = bsc_network_init(tall_bsc_ctx, 1, 1, NULL); - if (!bsc_gsmnet) - exit(1); - - bts = gsm_bts_alloc_register(bsc_gsmnet, GSM_BTS_TYPE_NANOBTS, - HARDCODED_BSIC); - /* ip.access supports up to 4 chained TRX */ - gsm_bts_trx_alloc(bts); - gsm_bts_trx_alloc(bts); - gsm_bts_trx_alloc(bts); - bts->oml_tei = stream_id; - - osmo_signal_register_handler(SS_NM, nm_sig_cb, NULL); - osmo_signal_register_handler(SS_IPAC_NWL, nwl_sig_cb, NULL); - - ipac_nwl_init(); - - printf("Trying to connect to ip.access BTS ...\n"); - - memset(&sin, 0, sizeof(sin)); - sin.sin_family = AF_INET; - inet_aton(argv[optind], &sin.sin_addr); - rc = ia_config_connect(bts, &sin); - if (rc < 0) { - perror("Error connecting to the BTS"); - exit(1); - } - - bts->oml_link->ts->sign.delay = 10; - bts->c0->rsl_link->ts->sign.delay = 10; - while (1) { - rc = osmo_select_main(0); - if (rc < 0) - exit(3); - } - - exit(0); -} - diff --git a/openbsc/src/ipaccess/ipaccess-firmware.c b/openbsc/src/ipaccess/ipaccess-firmware.c deleted file mode 100644 index 5f55bb52..00000000 --- a/openbsc/src/ipaccess/ipaccess-firmware.c +++ /dev/null @@ -1,135 +0,0 @@ -/* Routines for parsing an ipacces SDP firmware file */ - -/* (C) 2009 by Holger Hans Peter Freyther - * All Rights Reserved - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation; either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - * - */ - -#include -#include -#include - -#include -#include -#include -#include - -#define PART_LENGTH 138 - -osmo_static_assert(sizeof(struct sdp_header_entry) == 138, right_entry); -osmo_static_assert(sizeof(struct sdp_firmware) == 158, _right_header_length); - -/* more magic, the second "int" in the header */ -static char more_magic[] = { 0x10, 0x02 }; - -int ipaccess_analyze_file(int fd, const unsigned int st_size, const unsigned int base_offset, struct llist_head *list) -{ - struct sdp_firmware *firmware_header = 0; - struct sdp_header *header; - char buf[4096]; - int rc, i; - uint16_t table_size; - uint16_t table_offset; - off_t table_start; - - - rc = read(fd, buf, sizeof(*firmware_header)); - if (rc < 0) { - perror("Can not read header start."); - return -1; - } - - firmware_header = (struct sdp_firmware *) &buf[0]; - if (strncmp(firmware_header->magic, " SDP", 4) != 0) { - fprintf(stderr, "Wrong magic.\n"); - return -1; - } - - if (memcmp(firmware_header->more_magic, more_magic, 2) != 0) { - fprintf(stderr, "Wrong more magic. Got: 0x%x 0x%x vs. 0x%x 0x%x\n", - firmware_header->more_magic[0] & 0xff, firmware_header->more_magic[1] & 0xff, - more_magic[0], more_magic[1]); - return -1; - } - - - if (ntohl(firmware_header->file_length) != st_size) { - fprintf(stderr, "The filesize and the header do not match.\n"); - return -1; - } - - /* add the firmware */ - header = talloc_zero(list, struct sdp_header); - header->firmware_info = *firmware_header; - INIT_LLIST_HEAD(&header->header_list); - llist_add(&header->entry, list); - - table_offset = ntohs(firmware_header->table_offset); - table_start = lseek(fd, table_offset, SEEK_CUR); - if (table_start == -1) { - fprintf(stderr, "Failed to seek to the rel position: 0x%x\n", table_offset); - return -1; - } - - if (read(fd, &table_size, sizeof(table_size)) != sizeof(table_size)) { - fprintf(stderr, "The table size could not be read.\n"); - return -1; - } - - table_size = ntohs(table_size); - - if (table_size % PART_LENGTH != 0) { - fprintf(stderr, "The part length seems to be wrong: 0x%x\n", table_size); - return -1; - } - - /* look into each firmware now */ - for (i = 0; i < table_size / PART_LENGTH; ++i) { - struct sdp_header_entry entry; - struct sdp_header_item *header_entry; - unsigned int offset = table_start + 2; - offset += i * 138; - - if (lseek(fd, offset, SEEK_SET) != offset) { - fprintf(stderr, "Can not seek to the offset: %u.\n", offset); - return -1; - } - - rc = read(fd, &entry, sizeof(entry)); - if (rc != sizeof(entry)) { - fprintf(stderr, "Can not read the header entry.\n"); - return -1; - } - - header_entry = talloc_zero(header, struct sdp_header_item); - header_entry->header_entry = entry; - header_entry->absolute_offset = base_offset; - llist_add(&header_entry->entry, &header->header_list); - - /* now we need to find the SDP file... */ - offset = ntohl(entry.start) + 4 + base_offset; - if (lseek(fd, offset, SEEK_SET) != offset) { - perror("can't seek to sdp"); - return -1; - } - - - ipaccess_analyze_file(fd, ntohl(entry.length), offset, list); - } - - return 0; -} - diff --git a/openbsc/src/ipaccess/ipaccess-proxy.c b/openbsc/src/ipaccess/ipaccess-proxy.c deleted file mode 100644 index d3674426..00000000 --- a/openbsc/src/ipaccess/ipaccess-proxy.c +++ /dev/null @@ -1,1226 +0,0 @@ -/* OpenBSC Abis/IP proxy ip.access nanoBTS */ - -/* (C) 2009 by Harald Welte - * (C) 2010 by On-Waves - * (C) 2010 by Holger Hans Peter Freyther - * - * All Rights Reserved - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation; either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - * - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#define _GNU_SOURCE -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -/* one instance of an ip.access protocol proxy */ -struct ipa_proxy { - /* socket where we listen for incoming OML from BTS */ - struct osmo_fd oml_listen_fd; - /* socket where we listen for incoming RSL from BTS */ - struct osmo_fd rsl_listen_fd; - /* list of BTS's (struct ipa_bts_conn */ - struct llist_head bts_list; - /* the BSC reconnect timer */ - struct osmo_timer_list reconn_timer; - /* global GPRS NS data */ - struct in_addr gprs_addr; - struct in_addr listen_addr; -}; - -/* global pointer to the proxy structure */ -static struct ipa_proxy *ipp; - -struct ipa_proxy_conn { - struct osmo_fd fd; - struct llist_head tx_queue; - struct ipa_bts_conn *bts_conn; -}; -#define MAX_TRX 4 - -/* represents a particular BTS in our proxy */ -struct ipa_bts_conn { - /* list of BTS's (ipa_proxy->bts_list) */ - struct llist_head list; - /* back pointer to the proxy which we belong to */ - struct ipa_proxy *ipp; - /* the unit ID as determined by CCM */ - struct { - uint16_t site_id; - uint16_t bts_id; - } unit_id; - - /* incoming connections from BTS */ - struct ipa_proxy_conn *oml_conn; - struct ipa_proxy_conn *rsl_conn[MAX_TRX]; - - /* outgoing connections to BSC */ - struct ipa_proxy_conn *bsc_oml_conn; - struct ipa_proxy_conn *bsc_rsl_conn[MAX_TRX]; - - /* UDP sockets for BTS and BSC injection */ - struct osmo_fd udp_bts_fd; - struct osmo_fd udp_bsc_fd; - - /* NS data */ - struct in_addr bts_addr; - struct osmo_fd gprs_ns_fd; - int gprs_local_port; - uint16_t gprs_orig_port; - uint32_t gprs_orig_ip; - - char *id_tags[256]; - uint8_t *id_resp; - unsigned int id_resp_len; -}; - -enum ipp_fd_type { - OML_FROM_BTS = 1, - RSL_FROM_BTS = 2, - OML_TO_BSC = 3, - RSL_TO_BSC = 4, - UDP_TO_BTS = 5, - UDP_TO_BSC = 6, -}; - -/* some of the code against we link from OpenBSC needs this */ -void *tall_bsc_ctx; - -static char *listen_ipaddr; -static char *bsc_ipaddr; -static char *gprs_ns_ipaddr; - -static int gprs_ns_cb(struct osmo_fd *bfd, unsigned int what); - -#define PROXY_ALLOC_SIZE 1200 - -static struct ipa_bts_conn *find_bts_by_unitid(struct ipa_proxy *ipp, - uint16_t site_id, - uint16_t bts_id) -{ - struct ipa_bts_conn *ipbc; - - llist_for_each_entry(ipbc, &ipp->bts_list, list) { - if (ipbc->unit_id.site_id == site_id && - ipbc->unit_id.bts_id == bts_id) - return ipbc; - } - - return NULL; -} - -struct ipa_proxy_conn *alloc_conn(void) -{ - struct ipa_proxy_conn *ipc; - - ipc = talloc_zero(tall_bsc_ctx, struct ipa_proxy_conn); - if (!ipc) - return NULL; - - INIT_LLIST_HEAD(&ipc->tx_queue); - - return ipc; -} - -static int store_idtags(struct ipa_bts_conn *ipbc, struct tlv_parsed *tlvp) -{ - unsigned int i, len; - - for (i = 0; i <= 0xff; i++) { - if (!TLVP_PRESENT(tlvp, i)) - continue; - - len = TLVP_LEN(tlvp, i); -#if 0 - if (!ipbc->id_tags[i]) - ipbc->id_tags[i] = talloc_size(tall_bsc_ctx, len); - else -#endif - ipbc->id_tags[i] = talloc_realloc_size(ipbc, - ipbc->id_tags[i], len); - if (!ipbc->id_tags[i]) - return -ENOMEM; - - memset(ipbc->id_tags[i], 0, len); - //memcpy(ipbc->id_tags[i], TLVP_VAL(tlvp, i), len); - } - return 0; -} - - -static struct ipa_proxy_conn *connect_bsc(struct sockaddr_in *sa, int priv_nr, void *data); - -#define logp_ipbc_uid(ss, lvl, ipbc, trx_id) _logp_ipbc_uid(ss, lvl, __FILE__, __LINE__, ipbc, trx_id) - -static void _logp_ipbc_uid(unsigned int ss, unsigned int lvl, char *file, int line, - struct ipa_bts_conn *ipbc, uint8_t trx_id) -{ - if (ipbc) - logp2(ss, lvl, file, line, 0, "(%u/%u/%u) ", ipbc->unit_id.site_id, - ipbc->unit_id.bts_id, trx_id); - else - logp2(ss, lvl, file, line, 0, "unknown "); -} - -static int handle_udp_read(struct osmo_fd *bfd) -{ - struct ipa_bts_conn *ipbc = bfd->data; - struct ipa_proxy_conn *other_conn = NULL; - struct msgb *msg = msgb_alloc(PROXY_ALLOC_SIZE, "Abis/IP UDP"); - struct ipaccess_head *hh; - int ret; - - /* with UDP sockets, we cannot read partial packets but have to read - * all of it in one go */ - hh = (struct ipaccess_head *) msg->data; - ret = recv(bfd->fd, msg->data, msg->data_len, 0); - if (ret < 0) { - if (errno != EAGAIN) - LOGP(DLINP, LOGL_ERROR, "recv error %s\n", strerror(errno)); - msgb_free(msg); - return ret; - } - if (ret == 0) { - DEBUGP(DLINP, "UDP peer disappeared, dead socket\n"); - osmo_fd_unregister(bfd); - close(bfd->fd); - bfd->fd = -1; - msgb_free(msg); - return -EIO; - } - if (ret < sizeof(*hh)) { - DEBUGP(DLINP, "could not even read header!?!\n"); - msgb_free(msg); - return -EIO; - } - msgb_put(msg, ret); - msg->l2h = msg->data + sizeof(*hh); - DEBUGP(DLMI, "UDP RX: %s\n", osmo_hexdump(msg->data, msg->len)); - - if (hh->len != msg->len - sizeof(*hh)) { - DEBUGP(DLINP, "length (%u/%u) disagrees with header(%u)\n", - msg->len, msg->len - 3, hh->len); - msgb_free(msg); - return -EIO; - } - - switch (bfd->priv_nr & 0xff) { - case UDP_TO_BTS: - /* injection towards BTS */ - switch (hh->proto) { - case IPAC_PROTO_RSL: - /* FIXME: what to do about TRX > 0 */ - other_conn = ipbc->rsl_conn[0]; - break; - default: - DEBUGP(DLINP, "Unknown protocol 0x%02x, sending to " - "OML FD\n", hh->proto); - /* fall through */ - case IPAC_PROTO_IPACCESS: - case IPAC_PROTO_OML: - other_conn = ipbc->oml_conn; - break; - } - break; - case UDP_TO_BSC: - /* injection towards BSC */ - switch (hh->proto) { - case IPAC_PROTO_RSL: - /* FIXME: what to do about TRX > 0 */ - other_conn = ipbc->bsc_rsl_conn[0]; - break; - default: - DEBUGP(DLINP, "Unknown protocol 0x%02x, sending to " - "OML FD\n", hh->proto); - /* fall through */ - case IPAC_PROTO_IPACCESS: - case IPAC_PROTO_OML: - other_conn = ipbc->bsc_oml_conn; - break; - } - break; - default: - DEBUGP(DLINP, "Unknown filedescriptor priv_nr=%04x\n", bfd->priv_nr); - break; - } - - if (other_conn) { - /* enqueue the message for TX on the respective FD */ - msgb_enqueue(&other_conn->tx_queue, msg); - other_conn->fd.when |= BSC_FD_WRITE; - } else - msgb_free(msg); - - return 0; -} - -static int handle_udp_write(struct osmo_fd *bfd) -{ - /* not implemented yet */ - bfd->when &= ~BSC_FD_WRITE; - - return -EIO; -} - -/* callback from select.c in case one of the fd's can be read/written */ -static int udp_fd_cb(struct osmo_fd *bfd, unsigned int what) -{ - int rc = 0; - - if (what & BSC_FD_READ) - rc = handle_udp_read(bfd); - if (what & BSC_FD_WRITE) - rc = handle_udp_write(bfd); - - return rc; -} - - -static int ipbc_alloc_connect(struct ipa_proxy_conn *ipc, struct osmo_fd *bfd, - uint16_t site_id, uint16_t bts_id, - uint16_t trx_id, struct tlv_parsed *tlvp, - struct msgb *msg) -{ - struct ipa_bts_conn *ipbc; - uint16_t udp_port; - int ret = 0; - struct sockaddr_in sin; - - memset(&sin, 0, sizeof(sin)); - sin.sin_family = AF_INET; - inet_aton(bsc_ipaddr, &sin.sin_addr); - - DEBUGP(DLINP, "(%u/%u/%u) New BTS connection: ", - site_id, bts_id, trx_id); - - /* OML needs to be established before RSL */ - if ((bfd->priv_nr & 0xff) != OML_FROM_BTS) { - DEBUGPC(DLINP, "Not a OML connection ?!?\n"); - return -EIO; - } - - /* allocate new BTS connection data structure */ - ipbc = talloc_zero(tall_bsc_ctx, struct ipa_bts_conn); - if (!ipbc) { - ret = -ENOMEM; - goto err_out; - } - - DEBUGPC(DLINP, "Created BTS Conn data structure\n"); - ipbc->ipp = ipp; - ipbc->unit_id.site_id = site_id; - ipbc->unit_id.bts_id = bts_id; - ipbc->oml_conn = ipc; - ipc->bts_conn = ipbc; - - /* store the content of the ID TAGS for later reference */ - store_idtags(ipbc, tlvp); - ipbc->id_resp_len = msg->len; - ipbc->id_resp = talloc_size(tall_bsc_ctx, ipbc->id_resp_len); - memcpy(ipbc->id_resp, msg->data, ipbc->id_resp_len); - - /* Create OML TCP connection towards BSC */ - sin.sin_port = htons(IPA_TCP_PORT_OML); - ipbc->bsc_oml_conn = connect_bsc(&sin, OML_TO_BSC, ipbc); - if (!ipbc->bsc_oml_conn) { - ret = -EIO; - goto err_bsc_conn; - } - - DEBUGP(DLINP, "(%u/%u/%u) OML Connected to BSC\n", - site_id, bts_id, trx_id); - - /* Create UDP socket for BTS packet injection */ - udp_port = 10000 + (site_id % 1000)*100 + (bts_id % 100); - ret = make_sock(&ipbc->udp_bts_fd, IPPROTO_UDP, INADDR_ANY, udp_port, - UDP_TO_BTS, udp_fd_cb, ipbc); - if (ret < 0) - goto err_udp_bts; - DEBUGP(DLINP, "(%u/%u/%u) Created UDP socket for injection " - "towards BTS at port %u\n", site_id, bts_id, trx_id, udp_port); - - /* Create UDP socket for BSC packet injection */ - udp_port = 20000 + (site_id % 1000)*100 + (bts_id % 100); - ret = make_sock(&ipbc->udp_bsc_fd, IPPROTO_UDP, INADDR_ANY, udp_port, - UDP_TO_BSC, udp_fd_cb, ipbc); - if (ret < 0) - goto err_udp_bsc; - DEBUGP(DLINP, "(%u/%u/%u) Created UDP socket for injection " - "towards BSC at port %u\n", site_id, bts_id, trx_id, udp_port); - - - /* GPRS NS related code */ - if (gprs_ns_ipaddr) { - struct sockaddr_in sock; - socklen_t len = sizeof(sock); - struct in_addr addr; - uint32_t ip; - - inet_aton(listen_ipaddr, &addr); - ip = ntohl(addr.s_addr); /* make_sock() needs host byte order */ - ret = make_sock(&ipbc->gprs_ns_fd, IPPROTO_UDP, ip, 0, 0, - gprs_ns_cb, ipbc); - if (ret < 0) { - LOGP(DLINP, LOGL_ERROR, "Creating the GPRS socket failed.\n"); - goto err_udp_bsc; - } - - ret = getsockname(ipbc->gprs_ns_fd.fd, (struct sockaddr* ) &sock, &len); - ipbc->gprs_local_port = ntohs(sock.sin_port); - LOGP(DLINP, LOGL_NOTICE, - "Created GPRS NS Socket. Listening on: %s:%d\n", - inet_ntoa(sock.sin_addr), ipbc->gprs_local_port); - - ret = getpeername(bfd->fd, (struct sockaddr* ) &sock, &len); - ipbc->bts_addr = sock.sin_addr; - } - - llist_add(&ipbc->list, &ipp->bts_list); - - return 0; - -err_udp_bsc: - osmo_fd_unregister(&ipbc->udp_bts_fd); -err_udp_bts: - osmo_fd_unregister(&ipbc->bsc_oml_conn->fd); - close(ipbc->bsc_oml_conn->fd.fd); - talloc_free(ipbc->bsc_oml_conn); - ipbc->bsc_oml_conn = NULL; -err_bsc_conn: - talloc_free(ipbc->id_resp); - talloc_free(ipbc); -#if 0 - osmo_fd_unregister(bfd); - close(bfd->fd); - talloc_free(bfd); -#endif -err_out: - return ret; -} - -static int ipaccess_rcvmsg(struct ipa_proxy_conn *ipc, struct msgb *msg, - struct osmo_fd *bfd) -{ - struct tlv_parsed tlvp; - uint8_t msg_type = *(msg->l2h); - struct ipaccess_unit unit_data; - struct ipa_bts_conn *ipbc; - int ret = 0; - - switch (msg_type) { - case IPAC_MSGT_PING: - ret = ipa_ccm_send_pong(bfd->fd); - break; - case IPAC_MSGT_PONG: - DEBUGP(DLMI, "PONG!\n"); - break; - case IPAC_MSGT_ID_RESP: - DEBUGP(DLMI, "ID_RESP "); - /* parse tags, search for Unit ID */ - ipa_ccm_idtag_parse(&tlvp, (uint8_t *)msg->l2h + 2, - msgb_l2len(msg)-2); - DEBUGP(DLMI, "\n"); - - if (!TLVP_PRESENT(&tlvp, IPAC_IDTAG_UNIT)) { - LOGP(DLINP, LOGL_ERROR, "No Unit ID in ID RESPONSE !?!\n"); - return -EIO; - } - - /* lookup BTS, create sign_link, ... */ - memset(&unit_data, 0, sizeof(unit_data)); - ipa_parse_unitid((char *)TLVP_VAL(&tlvp, IPAC_IDTAG_UNIT), - &unit_data); - ipbc = find_bts_by_unitid(ipp, unit_data.site_id, unit_data.bts_id); - if (!ipbc) { - /* We have not found an ipbc (per-bts proxy instance) - * for this BTS yet. The first connection of a new BTS must - * be a OML connection. We allocate the associated data structures, - * and try to connect to the remote end */ - - return ipbc_alloc_connect(ipc, bfd, unit_data.site_id, - unit_data.bts_id, - unit_data.trx_id, &tlvp, msg); - /* if this fails, the caller will clean up bfd */ - } else { - struct sockaddr_in sin; - memset(&sin, 0, sizeof(sin)); - sin.sin_family = AF_INET; - inet_aton(bsc_ipaddr, &sin.sin_addr); - - DEBUGP(DLINP, "Identified BTS %u/%u/%u\n", - unit_data.site_id, unit_data.bts_id, unit_data.trx_id); - - if ((bfd->priv_nr & 0xff) != RSL_FROM_BTS) { - LOGP(DLINP, LOGL_ERROR, "Second OML connection from " - "same BTS ?!?\n"); - return 0; - } - - if (unit_data.trx_id >= MAX_TRX) { - LOGP(DLINP, LOGL_ERROR, "We don't support more " - "than %u TRX\n", MAX_TRX); - return -EINVAL; - } - - ipc->bts_conn = ipbc; - /* store TRX number in higher 8 bit of the bfd private number */ - bfd->priv_nr |= unit_data.trx_id << 8; - ipbc->rsl_conn[unit_data.trx_id] = ipc; - - /* Create RSL TCP connection towards BSC */ - sin.sin_port = htons(IPA_TCP_PORT_RSL); - ipbc->bsc_rsl_conn[unit_data.trx_id] = - connect_bsc(&sin, RSL_TO_BSC | (unit_data.trx_id << 8), ipbc); - if (!ipbc->bsc_oml_conn) - return -EIO; - DEBUGP(DLINP, "(%u/%u/%u) Connected RSL to BSC\n", - unit_data.site_id, unit_data.bts_id, unit_data.trx_id); - } - break; - case IPAC_MSGT_ID_GET: - DEBUGP(DLMI, "ID_GET\n"); - if ((bfd->priv_nr & 0xff) != OML_TO_BSC && - (bfd->priv_nr & 0xff) != RSL_TO_BSC) { - DEBUGP(DLINP, "IDentity REQuest from BTS ?!?\n"); - return -EIO; - } - ipbc = ipc->bts_conn; - if (!ipbc) { - DEBUGP(DLINP, "ID_GET from BSC before we have ID_RESP from BTS\n"); - return -EIO; - } - ret = write(bfd->fd, ipbc->id_resp, ipbc->id_resp_len); - if (ret != ipbc->id_resp_len) { - LOGP(DLINP, LOGL_ERROR, "Partial write: %d of %d\n", - ret, ipbc->id_resp_len); - return -EIO; - } - ret = 0; - break; - case IPAC_MSGT_ID_ACK: - DEBUGP(DLMI, "ID_ACK? -> ACK!\n"); - ret = ipa_ccm_send_id_ack(bfd->fd); - break; - default: - LOGP(DLMI, LOGL_ERROR, "Unhandled IPA type; %d\n", msg_type); - return 1; - break; - } - return ret; -} - -struct msgb *ipaccess_proxy_read_msg(struct osmo_fd *bfd, int *error) -{ - struct msgb *msg = msgb_alloc(PROXY_ALLOC_SIZE, "Abis/IP"); - struct ipaccess_head *hh; - int len, ret = 0; - - if (!msg) { - *error = -ENOMEM; - return NULL; - } - - /* first read our 3-byte header */ - hh = (struct ipaccess_head *) msg->data; - ret = recv(bfd->fd, msg->data, 3, 0); - if (ret < 0) { - if (errno != EAGAIN) - LOGP(DLINP, LOGL_ERROR, "recv error: %s\n", strerror(errno)); - msgb_free(msg); - *error = ret; - return NULL; - } else if (ret == 0) { - msgb_free(msg); - *error = ret; - return NULL; - } - - msgb_put(msg, ret); - - /* then read te length as specified in header */ - msg->l2h = msg->data + sizeof(*hh); - len = ntohs(hh->len); - ret = recv(bfd->fd, msg->l2h, len, 0); - if (ret < len) { - LOGP(DLINP, LOGL_ERROR, "short read!\n"); - msgb_free(msg); - *error = -EIO; - return NULL; - } - msgb_put(msg, ret); - - return msg; -} - -static struct ipa_proxy_conn *ipc_by_priv_nr(struct ipa_bts_conn *ipbc, - unsigned int priv_nr) -{ - struct ipa_proxy_conn *bsc_conn; - unsigned int trx_id = priv_nr >> 8; - - switch (priv_nr & 0xff) { - case OML_FROM_BTS: /* incoming OML data from BTS, forward to BSC OML */ - bsc_conn = ipbc->bsc_oml_conn; - break; - case RSL_FROM_BTS: /* incoming RSL data from BTS, forward to BSC RSL */ - bsc_conn = ipbc->bsc_rsl_conn[trx_id]; - break; - case OML_TO_BSC: /* incoming OML data from BSC, forward to BTS OML */ - bsc_conn = ipbc->oml_conn; - break; - case RSL_TO_BSC: /* incoming RSL data from BSC, forward to BTS RSL */ - bsc_conn = ipbc->rsl_conn[trx_id]; - break; - default: - bsc_conn = NULL; - break; - } - return bsc_conn; -} - -static void reconn_tmr_cb(void *data) -{ - struct ipa_proxy *ipp = data; - struct ipa_bts_conn *ipbc; - struct sockaddr_in sin; - int i; - - DEBUGP(DLINP, "Running reconnect timer\n"); - - memset(&sin, 0, sizeof(sin)); - sin.sin_family = AF_INET; - inet_aton(bsc_ipaddr, &sin.sin_addr); - - llist_for_each_entry(ipbc, &ipp->bts_list, list) { - /* if OML to BSC is dead, try to restore it */ - if (ipbc->oml_conn && !ipbc->bsc_oml_conn) { - sin.sin_port = htons(IPA_TCP_PORT_OML); - logp_ipbc_uid(DLINP, LOGL_NOTICE, ipbc, 0); - LOGPC(DLINP, LOGL_NOTICE, "OML Trying to reconnect\n"); - ipbc->bsc_oml_conn = connect_bsc(&sin, OML_TO_BSC, ipbc); - if (!ipbc->bsc_oml_conn) - goto reschedule; - logp_ipbc_uid(DLINP, LOGL_NOTICE, ipbc, 0); - LOGPC(DLINP, LOGL_NOTICE, "OML Reconnected\n"); - } - /* if we (still) don't have a OML connection, skip RSL */ - if (!ipbc->oml_conn || !ipbc->bsc_oml_conn) - continue; - - for (i = 0; i < ARRAY_SIZE(ipbc->rsl_conn); i++) { - unsigned int priv_nr; - /* don't establish RSL links which we don't have */ - if (!ipbc->rsl_conn[i]) - continue; - if (ipbc->bsc_rsl_conn[i]) - continue; - priv_nr = ipbc->rsl_conn[i]->fd.priv_nr; - priv_nr &= ~0xff; - priv_nr |= RSL_TO_BSC; - sin.sin_port = htons(IPA_TCP_PORT_RSL); - logp_ipbc_uid(DLINP, LOGL_NOTICE, ipbc, priv_nr >> 8); - LOGPC(DLINP, LOGL_NOTICE, "RSL Trying to reconnect\n"); - ipbc->bsc_rsl_conn[i] = connect_bsc(&sin, priv_nr, ipbc); - if (!ipbc->bsc_rsl_conn[i]) - goto reschedule; - logp_ipbc_uid(DLINP, LOGL_NOTICE, ipbc, priv_nr >> 8); - LOGPC(DLINP, LOGL_NOTICE, "RSL Reconnected\n"); - } - } - return; - -reschedule: - osmo_timer_schedule(&ipp->reconn_timer, 5, 0); -} - -static void handle_dead_socket(struct osmo_fd *bfd) -{ - struct ipa_proxy_conn *ipc = bfd->data; /* local conn */ - struct ipa_proxy_conn *bsc_conn; /* remote conn */ - struct ipa_bts_conn *ipbc = ipc->bts_conn; - unsigned int trx_id = bfd->priv_nr >> 8; - struct msgb *msg, *msg2; - - osmo_fd_unregister(bfd); - close(bfd->fd); - bfd->fd = -1; - - /* FIXME: clear tx_queue, remove all references, etc. */ - llist_for_each_entry_safe(msg, msg2, &ipc->tx_queue, list) - msgb_free(msg); - - switch (bfd->priv_nr & 0xff) { - case OML_FROM_BTS: /* incoming OML data from BTS, forward to BSC OML */ - /* The BTS started a connection with us but we got no - * IPAC_MSGT_ID_RESP message yet, in that scenario we did not - * allocate the ipa_bts_conn structure. */ - if (ipbc == NULL) - break; - ipbc->oml_conn = NULL; - bsc_conn = ipbc->bsc_oml_conn; - /* close the connection to the BSC */ - osmo_fd_unregister(&bsc_conn->fd); - close(bsc_conn->fd.fd); - llist_for_each_entry_safe(msg, msg2, &bsc_conn->tx_queue, list) - msgb_free(msg); - talloc_free(bsc_conn); - ipbc->bsc_oml_conn = NULL; - /* FIXME: do we need to delete the entire ipbc ? */ - break; - case RSL_FROM_BTS: /* incoming RSL data from BTS, forward to BSC RSL */ - ipbc->rsl_conn[trx_id] = NULL; - bsc_conn = ipbc->bsc_rsl_conn[trx_id]; - /* close the connection to the BSC */ - osmo_fd_unregister(&bsc_conn->fd); - close(bsc_conn->fd.fd); - llist_for_each_entry_safe(msg, msg2, &bsc_conn->tx_queue, list) - msgb_free(msg); - talloc_free(bsc_conn); - ipbc->bsc_rsl_conn[trx_id] = NULL; - break; - case OML_TO_BSC: /* incoming OML data from BSC, forward to BTS OML */ - ipbc->bsc_oml_conn = NULL; - bsc_conn = ipbc->oml_conn; - /* start reconnect timer */ - osmo_timer_schedule(&ipp->reconn_timer, 5, 0); - break; - case RSL_TO_BSC: /* incoming RSL data from BSC, forward to BTS RSL */ - ipbc->bsc_rsl_conn[trx_id] = NULL; - bsc_conn = ipbc->rsl_conn[trx_id]; - /* start reconnect timer */ - osmo_timer_schedule(&ipp->reconn_timer, 5, 0); - break; - default: - bsc_conn = NULL; - break; - } - - talloc_free(ipc); -} - -static void patch_gprs_msg(struct ipa_bts_conn *ipbc, int priv_nr, struct msgb *msg) -{ - uint8_t *nsvci; - - if ((priv_nr & 0xff) != OML_FROM_BTS && (priv_nr & 0xff) != OML_TO_BSC) - return; - - if (msgb_l2len(msg) != 39) - return; - - /* - * Check if this is a IPA Set Attribute or IPA Set Attribute ACK - * and if the FOM Class is GPRS NSVC0 and then we will patch it. - * - * The patch assumes the message looks like the one from the trace - * but we only match messages with a specific size anyway... So - * this hack should work just fine. - */ - - if (msg->l2h[0] == 0x10 && msg->l2h[1] == 0x80 && - msg->l2h[2] == 0x00 && msg->l2h[3] == 0x15 && - msg->l2h[18] == 0xf5 && msg->l2h[19] == 0xf2) { - nsvci = &msg->l2h[23]; - ipbc->gprs_orig_port = *(uint16_t *)(nsvci+8); - ipbc->gprs_orig_ip = *(uint32_t *)(nsvci+10); - *(uint16_t *)(nsvci+8) = htons(ipbc->gprs_local_port); - *(uint32_t *)(nsvci+10) = ipbc->ipp->listen_addr.s_addr; - } else if (msg->l2h[0] == 0x10 && msg->l2h[1] == 0x80 && - msg->l2h[2] == 0x00 && msg->l2h[3] == 0x15 && - msg->l2h[18] == 0xf6 && msg->l2h[19] == 0xf2) { - nsvci = &msg->l2h[23]; - *(uint16_t *)(nsvci+8) = ipbc->gprs_orig_port; - *(uint32_t *)(nsvci+10) = ipbc->gprs_orig_ip; - } -} - -static int handle_tcp_read(struct osmo_fd *bfd) -{ - struct ipa_proxy_conn *ipc = bfd->data; - struct ipa_bts_conn *ipbc = ipc->bts_conn; - struct ipa_proxy_conn *bsc_conn; - struct msgb *msg; - struct ipaccess_head *hh; - int ret = 0; - char *btsbsc; - - if ((bfd->priv_nr & 0xff) <= 2) - btsbsc = "BTS"; - else - btsbsc = "BSC"; - - msg = ipaccess_proxy_read_msg(bfd, &ret); - if (!msg) { - if (ret == 0) { - logp_ipbc_uid(DLINP, LOGL_NOTICE, ipbc, bfd->priv_nr >> 8); - LOGPC(DLINP, LOGL_NOTICE, "%s disappeared, " - "dead socket\n", btsbsc); - handle_dead_socket(bfd); - } - return ret; - } - - msgb_put(msg, ret); - logp_ipbc_uid(DLMI, LOGL_DEBUG, ipbc, bfd->priv_nr >> 8); - DEBUGPC(DLMI, "RX<-%s: %s\n", btsbsc, osmo_hexdump(msg->data, msg->len)); - - hh = (struct ipaccess_head *) msg->data; - if (hh->proto == IPAC_PROTO_IPACCESS) { - ret = ipaccess_rcvmsg(ipc, msg, bfd); - if (ret < 0) { - osmo_fd_unregister(bfd); - close(bfd->fd); - bfd->fd = -1; - talloc_free(bfd); - msgb_free(msg); - return ret; - } else if (ret == 0) { - /* we do not forward parts of the CCM protocol - * through the proxy but rather terminate it ourselves. */ - msgb_free(msg); - return ret; - } - } - - if (!ipbc) { - LOGP(DLINP, LOGL_ERROR, - "received %s packet but no ipc->bts_conn?!?\n", btsbsc); - msgb_free(msg); - return -EIO; - } - - bsc_conn = ipc_by_priv_nr(ipbc, bfd->priv_nr); - if (bsc_conn) { - if (gprs_ns_ipaddr) - patch_gprs_msg(ipbc, bfd->priv_nr, msg); - /* enqueue packet towards BSC */ - msgb_enqueue(&bsc_conn->tx_queue, msg); - /* mark respective filedescriptor as 'we want to write' */ - bsc_conn->fd.when |= BSC_FD_WRITE; - } else { - logp_ipbc_uid(DLINP, LOGL_INFO, ipbc, bfd->priv_nr >> 8); - LOGPC(DLINP, LOGL_INFO, "Dropping packet from %s, " - "since remote connection is dead\n", btsbsc); - msgb_free(msg); - } - - return ret; -} - -/* a TCP socket is ready to be written to */ -static int handle_tcp_write(struct osmo_fd *bfd) -{ - struct ipa_proxy_conn *ipc = bfd->data; - struct ipa_bts_conn *ipbc = ipc->bts_conn; - struct llist_head *lh; - struct msgb *msg; - char *btsbsc; - int ret; - - if ((bfd->priv_nr & 0xff) <= 2) - btsbsc = "BTS"; - else - btsbsc = "BSC"; - - - /* get the next msg for this timeslot */ - if (llist_empty(&ipc->tx_queue)) { - bfd->when &= ~BSC_FD_WRITE; - return 0; - } - lh = ipc->tx_queue.next; - llist_del(lh); - msg = llist_entry(lh, struct msgb, list); - - logp_ipbc_uid(DLMI, LOGL_DEBUG, ipbc, bfd->priv_nr >> 8); - DEBUGPC(DLMI, "TX %04x: %s\n", bfd->priv_nr, - osmo_hexdump(msg->data, msg->len)); - - ret = send(bfd->fd, msg->data, msg->len, 0); - msgb_free(msg); - - if (ret == 0) { - logp_ipbc_uid(DLINP, LOGL_NOTICE, ipbc, bfd->priv_nr >> 8); - LOGP(DLINP, LOGL_NOTICE, "%s disappeared, dead socket\n", btsbsc); - handle_dead_socket(bfd); - } - - return ret; -} - -/* callback from select.c in case one of the fd's can be read/written */ -static int proxy_ipaccess_fd_cb(struct osmo_fd *bfd, unsigned int what) -{ - int rc = 0; - - if (what & BSC_FD_READ) { - rc = handle_tcp_read(bfd); - if (rc < 0) - return rc; - } - if (what & BSC_FD_WRITE) - rc = handle_tcp_write(bfd); - - return rc; -} - -/* callback of the listening filedescriptor */ -static int listen_fd_cb(struct osmo_fd *listen_bfd, unsigned int what) -{ - int ret; - struct ipa_proxy_conn *ipc; - struct osmo_fd *bfd; - struct sockaddr_in sa; - socklen_t sa_len = sizeof(sa); - - if (!(what & BSC_FD_READ)) - return 0; - - ret = accept(listen_bfd->fd, (struct sockaddr *) &sa, &sa_len); - if (ret < 0) { - perror("accept"); - return ret; - } - DEBUGP(DLINP, "accept()ed new %s link from %s\n", - (listen_bfd->priv_nr & 0xff) == OML_FROM_BTS ? "OML" : "RSL", - inet_ntoa(sa.sin_addr)); - - ipc = alloc_conn(); - if (!ipc) { - close(ret); - return -ENOMEM; - } - - bfd = &ipc->fd; - bfd->fd = ret; - bfd->data = ipc; - bfd->priv_nr = listen_bfd->priv_nr; - bfd->cb = proxy_ipaccess_fd_cb; - bfd->when = BSC_FD_READ; - ret = osmo_fd_register(bfd); - if (ret < 0) { - LOGP(DLINP, LOGL_ERROR, "could not register FD\n"); - close(bfd->fd); - talloc_free(ipc); - return ret; - } - - /* Request ID. FIXME: request LOCATION, HW/SW VErsion, Unit Name, Serno */ - ret = ipa_ccm_send_id_req(bfd->fd); - - return 0; -} - -static void send_ns(int fd, const char *buf, int size, struct in_addr ip, int port) -{ - int ret; - struct sockaddr_in addr; - socklen_t len = sizeof(addr); - memset(&addr, 0, sizeof(addr)); - - addr.sin_family = AF_INET; - addr.sin_port = htons(port); - addr.sin_addr = ip; - - ret = sendto(fd, buf, size, 0, (struct sockaddr *) &addr, len); - if (ret < 0) { - LOGP(DLINP, LOGL_ERROR, "Failed to forward GPRS message.\n"); - } -} - -static int gprs_ns_cb(struct osmo_fd *bfd, unsigned int what) -{ - struct ipa_bts_conn *bts; - char buf[4096]; - int ret; - struct sockaddr_in sock; - socklen_t len = sizeof(sock); - - /* 1. get the data... */ - ret = recvfrom(bfd->fd, buf, sizeof(buf), 0, (struct sockaddr *) &sock, &len); - if (ret < 0) { - LOGP(DLINP, LOGL_ERROR, "Failed to recv GPRS NS msg: %s.\n", strerror(errno)); - return -1; - } - - bts = bfd->data; - - /* 2. figure out where to send it to */ - if (memcmp(&sock.sin_addr, &ipp->gprs_addr, sizeof(sock.sin_addr)) == 0) { - LOGP(DLINP, LOGL_DEBUG, "GPRS NS msg from network.\n"); - send_ns(bfd->fd, buf, ret, bts->bts_addr, 23000); - } else if (memcmp(&sock.sin_addr, &bts->bts_addr, sizeof(sock.sin_addr)) == 0) { - LOGP(DLINP, LOGL_DEBUG, "GPRS NS msg from BTS.\n"); - send_ns(bfd->fd, buf, ret, ipp->gprs_addr, 23000); - } else { - LOGP(DLINP, LOGL_ERROR, "Unknown GPRS source: %s\n", inet_ntoa(sock.sin_addr)); - } - - return 0; -} - -/* Actively connect to a BSC. */ -static struct ipa_proxy_conn *connect_bsc(struct sockaddr_in *sa, int priv_nr, void *data) -{ - struct ipa_proxy_conn *ipc; - struct osmo_fd *bfd; - int ret, on = 1; - - ipc = alloc_conn(); - if (!ipc) - return NULL; - - ipc->bts_conn = data; - - bfd = &ipc->fd; - bfd->fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); - bfd->cb = ipaccess_fd_cb; - bfd->when = BSC_FD_READ | BSC_FD_WRITE; - bfd->data = ipc; - bfd->priv_nr = priv_nr; - - if (bfd->fd < 0) { - LOGP(DLINP, LOGL_ERROR, "Could not create socket: %s\n", - strerror(errno)); - talloc_free(ipc); - return NULL; - } - - ret = setsockopt(bfd->fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)); - if (ret < 0) { - LOGP(DLINP, LOGL_ERROR, "Could not set socket option\n"); - close(bfd->fd); - talloc_free(ipc); - return NULL; - } - - ret = connect(bfd->fd, (struct sockaddr *) sa, sizeof(*sa)); - if (ret < 0) { - LOGP(DLINP, LOGL_ERROR, "Could not connect socket: %s\n", - inet_ntoa(sa->sin_addr)); - close(bfd->fd); - talloc_free(ipc); - return NULL; - } - - /* pre-fill tx_queue with identity request */ - ret = osmo_fd_register(bfd); - if (ret < 0) { - close(bfd->fd); - talloc_free(ipc); - return NULL; - } - - return ipc; -} - -static int ipaccess_proxy_setup(void) -{ - int ret; - - ipp = talloc_zero(tall_bsc_ctx, struct ipa_proxy); - if (!ipp) - return -ENOMEM; - INIT_LLIST_HEAD(&ipp->bts_list); - osmo_timer_setup(&ipp->reconn_timer, reconn_tmr_cb, ipp); - - /* Listen for OML connections */ - ret = make_sock(&ipp->oml_listen_fd, IPPROTO_TCP, INADDR_ANY, - IPA_TCP_PORT_OML, OML_FROM_BTS, listen_fd_cb, NULL); - if (ret < 0) - return ret; - - /* Listen for RSL connections */ - ret = make_sock(&ipp->rsl_listen_fd, IPPROTO_TCP, INADDR_ANY, - IPA_TCP_PORT_RSL, RSL_FROM_BTS, listen_fd_cb, NULL); - - if (ret < 0) - return ret; - - /* Connect the GPRS NS Socket */ - if (gprs_ns_ipaddr) { - inet_aton(gprs_ns_ipaddr, &ipp->gprs_addr); - inet_aton(listen_ipaddr, &ipp->listen_addr); - } - - return ret; -} - -static void signal_handler(int signal) -{ - fprintf(stdout, "signal %u received\n", signal); - - switch (signal) { - case SIGABRT: - /* in case of abort, we want to obtain a talloc report - * and then return to the caller, who will abort the process */ - case SIGUSR1: - talloc_report_full(tall_bsc_ctx, stderr); - break; - default: - break; - } -} - -static void print_help(void) -{ - printf(" ipaccess-proxy is a proxy BTS.\n"); - printf(" -h --help. This help text.\n"); - printf(" -l --listen IP. The ip to listen to.\n"); - printf(" -b --bsc IP. The BSC IP address.\n"); - printf(" -g --gprs IP. Take GPRS NS from that IP.\n"); - printf("\n"); - printf(" -s --disable-color. Disable the color inside the logging message.\n"); - printf(" -e --log-level number. Set the global loglevel.\n"); - printf(" -T --timestamp. Prefix every log message with a timestamp.\n"); - printf(" -V --version. Print the version of OpenBSC.\n"); -} - -static void print_usage(void) -{ - printf("Usage: ipaccess-proxy [options]\n"); -} - -enum { - IPA_PROXY_OPT_LISTEN_NONE = 0, - IPA_PROXY_OPT_LISTEN_IP = (1 << 0), - IPA_PROXY_OPT_BSC_IP = (1 << 1), -}; - -static void handle_options(int argc, char** argv) -{ - int options_mask = 0; - - /* disable explicit missing arguments error output from getopt_long */ - opterr = 0; - - while (1) { - int option_index = 0, c; - static struct option long_options[] = { - {"help", 0, 0, 'h'}, - {"disable-color", 0, 0, 's'}, - {"timestamp", 0, 0, 'T'}, - {"log-level", 1, 0, 'e'}, - {"listen", 1, 0, 'l'}, - {"bsc", 1, 0, 'b'}, - {0, 0, 0, 0} - }; - - c = getopt_long(argc, argv, "hsTe:l:b:g:", - long_options, &option_index); - if (c == -1) - break; - - switch (c) { - case 'h': - print_usage(); - print_help(); - exit(0); - case 'l': - listen_ipaddr = optarg; - options_mask |= IPA_PROXY_OPT_LISTEN_IP; - break; - case 'b': - bsc_ipaddr = optarg; - options_mask |= IPA_PROXY_OPT_BSC_IP; - break; - case 'g': - gprs_ns_ipaddr = optarg; - break; - case 's': - log_set_use_color(osmo_stderr_target, 0); - break; - case 'T': - log_set_print_timestamp(osmo_stderr_target, 1); - break; - case 'e': - log_set_log_level(osmo_stderr_target, atoi(optarg)); - break; - case '?': - if (optopt) { - printf("ERROR: missing mandatory argument " - "for `%s' option\n", argv[optind-1]); - } else { - printf("ERROR: unknown option `%s'\n", - argv[optind-1]); - } - print_usage(); - print_help(); - exit(EXIT_FAILURE); - break; - default: - /* ignore */ - break; - } - } - if ((options_mask & (IPA_PROXY_OPT_LISTEN_IP | IPA_PROXY_OPT_BSC_IP)) - != (IPA_PROXY_OPT_LISTEN_IP | IPA_PROXY_OPT_BSC_IP)) { - printf("ERROR: You have to specify `--listen' and `--bsc' " - "options at least.\n"); - print_usage(); - print_help(); - exit(EXIT_FAILURE); - } -} - -int main(int argc, char **argv) -{ - int rc; - - tall_bsc_ctx = talloc_named_const(NULL, 1, "ipaccess-proxy"); - msgb_talloc_ctx_init(tall_bsc_ctx, 0); - - osmo_init_logging(&log_info); - log_parse_category_mask(osmo_stderr_target, "DLINP:DLMI"); - - handle_options(argc, argv); - - rc = ipaccess_proxy_setup(); - if (rc < 0) - exit(1); - - signal(SIGUSR1, &signal_handler); - signal(SIGABRT, &signal_handler); - osmo_init_ignore_signals(); - - while (1) { - osmo_select_main(0); - } -} diff --git a/openbsc/src/ipaccess/network_listen.c b/openbsc/src/ipaccess/network_listen.c deleted file mode 100644 index 3b44ceb7..00000000 --- a/openbsc/src/ipaccess/network_listen.c +++ /dev/null @@ -1,257 +0,0 @@ -/* ip.access nanoBTS network listen mode */ - -/* (C) 2009-2010 by Harald Welte - * - * All Rights Reserved - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation; either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - * - */ - -#include -#include -#include -#include -#include -#include - -#include - -#include -#include -#include -#include - -#include -#include -#include -#include -#include - -#define WHITELIST_MAX_SIZE ((NUM_ARFCNS*2)+2+1) - -int ipac_rxlevstat2whitelist(uint16_t *buf, const struct rxlev_stats *st, uint8_t min_rxlev, - uint16_t max_num_arfcns) -{ - int i; - unsigned int num_arfcn = 0; - - for (i = NUM_RXLEVS-1; i >= min_rxlev; i--) { - int16_t arfcn = -1; - - while ((arfcn = rxlev_stat_get_next(st, i, arfcn)) >= 0) { - *buf++ = htons(arfcn); - num_arfcn++; - - } - - if (num_arfcn > max_num_arfcns) - break; - } - - return num_arfcn; -} - -enum ipac_test_state { - IPAC_TEST_S_IDLE, - IPAC_TEST_S_RQD, - IPAC_TEST_S_EXEC, - IPAC_TEST_S_PARTIAL, -}; - -int ipac_nwl_test_start(struct gsm_bts_trx *trx, uint8_t testnr, - const uint8_t *phys_conf, unsigned int phys_conf_len) -{ - struct msgb *msg; - - if (trx->ipaccess.test_state != IPAC_TEST_S_IDLE) { - fprintf(stderr, "Cannot start test in state %u\n", trx->ipaccess.test_state); - return -EINVAL; - } - - switch (testnr) { - case NM_IPACC_TESTNO_CHAN_USAGE: - case NM_IPACC_TESTNO_BCCH_CHAN_USAGE: - rxlev_stat_reset(&trx->ipaccess.rxlev_stat); - break; - } - - msg = msgb_alloc_headroom(phys_conf_len+256, 128, "OML"); - - if (phys_conf && phys_conf_len) { - uint8_t *payload; - /* first put the phys conf header */ - msgb_tv16_put(msg, NM_ATT_PHYS_CONF, phys_conf_len); - payload = msgb_put(msg, phys_conf_len); - memcpy(payload, phys_conf, phys_conf_len); - } - - abis_nm_perform_test(trx->bts, NM_OC_RADIO_CARRIER, 0, trx->nr, 0xff, - testnr, 1, msg); - trx->ipaccess.test_nr = testnr; - - /* FIXME: start safety timer until when test is supposed to complete */ - - return 0; -} - -static uint16_t last_arfcn; -static struct gsm_sysinfo_freq nwl_si_freq[1024]; -#define FREQ_TYPE_NCELL_2 0x04 /* sub channel of SI 2 */ -#define FREQ_TYPE_NCELL_2bis 0x08 /* sub channel of SI 2bis */ -#define FREQ_TYPE_NCELL_2ter 0x10 /* sub channel of SI 2ter */ - -struct ipacc_ferr_elem { - int16_t freq_err; - uint8_t freq_qual; - uint8_t arfcn; -} __attribute__((packed)); - -struct ipacc_cusage_elem { - uint16_t arfcn:10, - rxlev:6; -} __attribute__ ((packed)); - -static int test_rep(void *_msg) -{ - struct msgb *msg = _msg; - struct abis_om_fom_hdr *foh = msgb_l3(msg); - uint16_t test_rep_len, ferr_list_len; - struct ipacc_ferr_elem *ife; - struct ipac_bcch_info binfo; - struct e1inp_sign_link *sign_link = (struct e1inp_sign_link *)msg->dst; - int i, rc; - - DEBUGP(DNM, "TEST REPORT: "); - - if (foh->data[0] != NM_ATT_TEST_NO || - foh->data[2] != NM_ATT_TEST_REPORT) - return -EINVAL; - - DEBUGPC(DNM, "test_no=0x%02x ", foh->data[1]); - /* data[2] == NM_ATT_TEST_REPORT */ - /* data[3..4]: test_rep_len */ - memcpy(&test_rep_len, &foh->data[3], sizeof(uint16_t)); - test_rep_len = ntohs(test_rep_len); - /* data[5]: ip.access test result */ - DEBUGPC(DNM, "tst_res=%s\n", ipacc_testres_name(foh->data[5])); - - /* data[6]: ip.access nested IE. 3 == freq_err_list */ - switch (foh->data[6]) { - case NM_IPAC_EIE_FREQ_ERR_LIST: - /* data[7..8]: length of ferr_list */ - memcpy(&ferr_list_len, &foh->data[7], sizeof(uint16_t)); - ferr_list_len = ntohs(ferr_list_len); - - /* data[9...]: frequency error list elements */ - for (i = 0; i < ferr_list_len; i+= sizeof(*ife)) { - ife = (struct ipacc_ferr_elem *) (foh->data + 9 + i); - DEBUGP(DNM, "==> ARFCN %4u, Frequency Error %6hd\n", - ife->arfcn, ntohs(ife->freq_err)); - } - break; - case NM_IPAC_EIE_CHAN_USE_LIST: - /* data[7..8]: length of ferr_list */ - memcpy(&ferr_list_len, &foh->data[7], sizeof(uint16_t)); - ferr_list_len = ntohs(ferr_list_len); - - /* data[9...]: channel usage list elements */ - for (i = 0; i < ferr_list_len; i+= 2) { - uint16_t *cu_ptr = (uint16_t *)(foh->data + 9 + i); - uint16_t cu = ntohs(*cu_ptr); - uint16_t arfcn = cu & 0x3ff; - uint8_t rxlev = cu >> 10; - DEBUGP(DNM, "==> ARFCN %4u, RxLev %2u\n", arfcn, rxlev); - rxlev_stat_input(&sign_link->trx->ipaccess.rxlev_stat, - arfcn, rxlev); - } - break; - case NM_IPAC_EIE_BCCH_INFO_TYPE: - break; - case NM_IPAC_EIE_BCCH_INFO: - rc = ipac_parse_bcch_info(&binfo, foh->data+6); - if (rc < 0) { - DEBUGP(DNM, "BCCH Info parsing failed\n"); - break; - } - DEBUGP(DNM, "==> ARFCN %u, RxLev %2u, RxQual %2u: %3d-%d, LAC %d CI %d BSIC %u\n", - binfo.arfcn, binfo.rx_lev, binfo.rx_qual, - binfo.cgi.mcc, binfo.cgi.mnc, - binfo.cgi.lac, binfo.cgi.ci, binfo.bsic); - - if (binfo.arfcn != last_arfcn) { - /* report is on a new arfcn, need to clear channel list */ - memset(nwl_si_freq, 0, sizeof(nwl_si_freq)); - last_arfcn = binfo.arfcn; - } - if (binfo.info_type & IPAC_BINF_NEIGH_BA_SI2) { - DEBUGP(DNM, "BA SI2: %s\n", osmo_hexdump(binfo.ba_list_si2, sizeof(binfo.ba_list_si2))); - gsm48_decode_freq_list(nwl_si_freq, binfo.ba_list_si2, sizeof(binfo.ba_list_si2), - 0x8c, FREQ_TYPE_NCELL_2); - } - if (binfo.info_type & IPAC_BINF_NEIGH_BA_SI2bis) { - DEBUGP(DNM, "BA SI2bis: %s\n", osmo_hexdump(binfo.ba_list_si2bis, sizeof(binfo.ba_list_si2bis))); - gsm48_decode_freq_list(nwl_si_freq, binfo.ba_list_si2bis, sizeof(binfo.ba_list_si2bis), - 0x8e, FREQ_TYPE_NCELL_2bis); - } - if (binfo.info_type & IPAC_BINF_NEIGH_BA_SI2ter) { - DEBUGP(DNM, "BA SI2ter: %s\n", osmo_hexdump(binfo.ba_list_si2ter, sizeof(binfo.ba_list_si2ter))); - gsm48_decode_freq_list(nwl_si_freq, binfo.ba_list_si2ter, sizeof(binfo.ba_list_si2ter), - 0x8e, FREQ_TYPE_NCELL_2ter); - } - for (i = 0; i < ARRAY_SIZE(nwl_si_freq); i++) { - if (nwl_si_freq[i].mask) - DEBUGP(DNM, "Neighbor Cell on ARFCN %u\n", i); - } - break; - default: - break; - } - - switch (foh->data[5]) { - case NM_IPACC_TESTRES_SUCCESS: - case NM_IPACC_TESTRES_STOPPED: - case NM_IPACC_TESTRES_TIMEOUT: - case NM_IPACC_TESTRES_NO_CHANS: - sign_link->trx->ipaccess.test_state = IPAC_TEST_S_IDLE; - /* Send signal to notify higher layers of test completion */ - DEBUGP(DNM, "dispatching S_IPAC_NWL_COMPLETE signal\n"); - osmo_signal_dispatch(SS_IPAC_NWL, S_IPAC_NWL_COMPLETE, - sign_link->trx); - break; - case NM_IPACC_TESTRES_PARTIAL: - sign_link->trx->ipaccess.test_state = IPAC_TEST_S_PARTIAL; - break; - } - - return 0; -} - -static int nwl_sig_cb(unsigned int subsys, unsigned int signal, - void *handler_data, void *signal_data) -{ - switch (signal) { - case S_NM_TEST_REP: - return test_rep(signal_data); - default: - break; - } - - return 0; -} - -void ipac_nwl_init(void) -{ - osmo_signal_register_handler(SS_NM, nwl_sig_cb, NULL); -} diff --git a/openbsc/src/libbsc/Makefile.am b/openbsc/src/libbsc/Makefile.am deleted file mode 100644 index e78bde62..00000000 --- a/openbsc/src/libbsc/Makefile.am +++ /dev/null @@ -1,57 +0,0 @@ -AM_CPPFLAGS = \ - $(all_includes) \ - -I$(top_srcdir)/include \ - -I$(top_builddir) \ - $(NULL) - -AM_CFLAGS = \ - -Wall \ - $(LIBOSMOCORE_CFLAGS) \ - $(LIBOSMOGSM_CFLAGS) \ - $(LIBOSMOVTY_CFLAGS) \ - $(LIBOSMOABIS_CFLAGS) \ - $(COVERAGE_CFLAGS) \ - $(NULL) - -noinst_LIBRARIES = \ - libbsc.a \ - $(NULL) - -libbsc_a_SOURCES = \ - abis_nm.c \ - abis_nm_vty.c \ - abis_om2000.c \ - abis_om2000_vty.c \ - abis_rsl.c \ - bsc_rll.c \ - bsc_subscriber.c \ - paging.c \ - bts_ericsson_rbs2000.c \ - bts_ipaccess_nanobts.c \ - bts_siemens_bs11.c \ - bts_nokia_site.c \ - bts_unknown.c \ - bts_sysmobts.c \ - chan_alloc.c \ - handover_decision.c \ - handover_logic.c \ - meas_rep.c \ - pcu_sock.c \ - rest_octets.c \ - system_information.c \ - e1_config.c \ - bsc_api.c \ - bsc_msc.c bsc_vty.c \ - gsm_04_08_utils.c \ - gsm_04_80_utils.c \ - bsc_init.c \ - bts_init.c \ - bsc_rf_ctrl.c \ - arfcn_range_encode.c \ - bsc_ctrl_commands.c \ - bsc_ctrl_lookup.c \ - net_init.c \ - bsc_dyn_ts.c \ - bts_ipaccess_nanobts_omlattr.c \ - $(NULL) - diff --git a/openbsc/src/libbsc/abis_nm.c b/openbsc/src/libbsc/abis_nm.c deleted file mode 100644 index 019d0395..00000000 --- a/openbsc/src/libbsc/abis_nm.c +++ /dev/null @@ -1,2917 +0,0 @@ -/* GSM Network Management (OML) messages on the A-bis interface - * 3GPP TS 12.21 version 8.0.0 Release 1999 / ETSI TS 100 623 V8.0.0 */ - -/* (C) 2008-2009 by Harald Welte - * - * All Rights Reserved - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation; either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public 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 - -#define OM_ALLOC_SIZE 1024 -#define OM_HEADROOM_SIZE 128 -#define IPACC_SEGMENT_SIZE 245 - -int abis_nm_tlv_parse(struct tlv_parsed *tp, struct gsm_bts *bts, const uint8_t *buf, int len) -{ - if (!bts->model) - return -EIO; - return tlv_parse(tp, &bts->model->nm_att_tlvdef, buf, len, 0, 0); -} - -static int is_in_arr(enum abis_nm_msgtype mt, const enum abis_nm_msgtype *arr, int size) -{ - int i; - - for (i = 0; i < size; i++) { - if (arr[i] == mt) - return 1; - } - - return 0; -} - -#if 0 -/* is this msgtype the usual ACK/NACK type ? */ -static int is_ack_nack(enum abis_nm_msgtype mt) -{ - return !is_in_arr(mt, no_ack_nack, ARRAY_SIZE(no_ack_nack)); -} -#endif - -/* is this msgtype a report ? */ -static int is_report(enum abis_nm_msgtype mt) -{ - return is_in_arr(mt, abis_nm_reports, ARRAY_SIZE(abis_nm_reports)); -} - -#define MT_ACK(x) (x+1) -#define MT_NACK(x) (x+2) - -static void fill_om_hdr(struct abis_om_hdr *oh, uint8_t len) -{ - oh->mdisc = ABIS_OM_MDISC_FOM; - oh->placement = ABIS_OM_PLACEMENT_ONLY; - oh->sequence = 0; - oh->length = len; -} - -static struct abis_om_fom_hdr *fill_om_fom_hdr(struct abis_om_hdr *oh, uint8_t len, - uint8_t msg_type, uint8_t obj_class, - uint8_t bts_nr, uint8_t trx_nr, uint8_t ts_nr) -{ - struct abis_om_fom_hdr *foh = - (struct abis_om_fom_hdr *) oh->data; - - fill_om_hdr(oh, len+sizeof(*foh)); - foh->msg_type = msg_type; - foh->obj_class = obj_class; - foh->obj_inst.bts_nr = bts_nr; - foh->obj_inst.trx_nr = trx_nr; - foh->obj_inst.ts_nr = ts_nr; - return foh; -} - -static struct msgb *nm_msgb_alloc(void) -{ - return msgb_alloc_headroom(OM_ALLOC_SIZE, OM_HEADROOM_SIZE, - "OML"); -} - -int _abis_nm_sendmsg(struct msgb *msg) -{ - msg->l2h = msg->data; - - if (!msg->dst) { - LOGP(DNM, LOGL_ERROR, "%s: msg->dst == NULL\n", __func__); - return -EINVAL; - } - - return abis_sendmsg(msg); -} - -/* Send a OML NM Message from BSC to BTS */ -static int abis_nm_queue_msg(struct gsm_bts *bts, struct msgb *msg) -{ - msg->dst = bts->oml_link; - - /* queue OML messages */ - if (llist_empty(&bts->abis_queue) && !bts->abis_nm_pend) { - bts->abis_nm_pend = OBSC_NM_W_ACK_CB(msg); - return _abis_nm_sendmsg(msg); - } else { - msgb_enqueue(&bts->abis_queue, msg); - return 0; - } - -} - -int abis_nm_sendmsg(struct gsm_bts *bts, struct msgb *msg) -{ - OBSC_NM_W_ACK_CB(msg) = 1; - return abis_nm_queue_msg(bts, msg); -} - -static int abis_nm_sendmsg_direct(struct gsm_bts *bts, struct msgb *msg) -{ - OBSC_NM_W_ACK_CB(msg) = 0; - return abis_nm_queue_msg(bts, msg); -} - -static int abis_nm_rcvmsg_sw(struct msgb *mb); - -int nm_is_running(struct gsm_nm_state *s) { - return (s->operational == NM_OPSTATE_ENABLED) && ( - (s->availability == NM_AVSTATE_OK) || - (s->availability == 0xff) - ); -} - -/* Update the administrative state of a given object in our in-memory data - * structures and send an event to the higher layer */ -static int update_admstate(struct gsm_bts *bts, uint8_t obj_class, - struct abis_om_obj_inst *obj_inst, uint8_t adm_state) -{ - struct gsm_nm_state *nm_state, new_state; - struct nm_statechg_signal_data nsd; - - memset(&nsd, 0, sizeof(nsd)); - - nsd.obj = gsm_objclass2obj(bts, obj_class, obj_inst); - if (!nsd.obj) - return -EINVAL; - nm_state = gsm_objclass2nmstate(bts, obj_class, obj_inst); - if (!nm_state) - return -1; - - new_state = *nm_state; - new_state.administrative = adm_state; - - nsd.bts = bts; - nsd.obj_class = obj_class; - nsd.old_state = nm_state; - nsd.new_state = &new_state; - nsd.obj_inst = obj_inst; - osmo_signal_dispatch(SS_NM, S_NM_STATECHG_ADM, &nsd); - - nm_state->administrative = adm_state; - - return 0; -} - -static int abis_nm_rx_statechg_rep(struct msgb *mb) -{ - struct abis_om_hdr *oh = msgb_l2(mb); - struct abis_om_fom_hdr *foh = msgb_l3(mb); - struct e1inp_sign_link *sign_link = mb->dst; - struct gsm_bts *bts = sign_link->trx->bts; - struct tlv_parsed tp; - struct gsm_nm_state *nm_state, new_state; - - DEBUGPC(DNM, "STATE CHG: "); - - memset(&new_state, 0, sizeof(new_state)); - - nm_state = gsm_objclass2nmstate(bts, foh->obj_class, &foh->obj_inst); - if (!nm_state) { - DEBUGPC(DNM, "unknown object class\n"); - return -EINVAL; - } - - new_state = *nm_state; - - abis_nm_tlv_parse(&tp, bts, foh->data, oh->length-sizeof(*foh)); - if (TLVP_PRESENT(&tp, NM_ATT_OPER_STATE)) { - new_state.operational = *TLVP_VAL(&tp, NM_ATT_OPER_STATE); - DEBUGPC(DNM, "OP_STATE=%s ", - abis_nm_opstate_name(new_state.operational)); - } - if (TLVP_PRESENT(&tp, NM_ATT_AVAIL_STATUS)) { - if (TLVP_LEN(&tp, NM_ATT_AVAIL_STATUS) == 0) - new_state.availability = 0xff; - else - new_state.availability = *TLVP_VAL(&tp, NM_ATT_AVAIL_STATUS); - DEBUGPC(DNM, "AVAIL=%s(%02x) ", - abis_nm_avail_name(new_state.availability), - new_state.availability); - } else - new_state.availability = 0xff; - if (TLVP_PRESENT(&tp, NM_ATT_ADM_STATE)) { - new_state.administrative = *TLVP_VAL(&tp, NM_ATT_ADM_STATE); - DEBUGPC(DNM, "ADM=%2s ", - get_value_string(abis_nm_adm_state_names, - new_state.administrative)); - } - DEBUGPC(DNM, "\n"); - - if ((new_state.administrative != 0 && nm_state->administrative == 0) || - new_state.operational != nm_state->operational || - new_state.availability != nm_state->availability) { - /* Update the operational state of a given object in our in-memory data - * structures and send an event to the higher layer */ - struct nm_statechg_signal_data nsd; - nsd.obj = gsm_objclass2obj(bts, foh->obj_class, &foh->obj_inst); - nsd.obj_class = foh->obj_class; - nsd.old_state = nm_state; - nsd.new_state = &new_state; - nsd.obj_inst = &foh->obj_inst; - nsd.bts = bts; - osmo_signal_dispatch(SS_NM, S_NM_STATECHG_OPER, &nsd); - nm_state->operational = new_state.operational; - nm_state->availability = new_state.availability; - if (nm_state->administrative == 0) - nm_state->administrative = new_state.administrative; - } -#if 0 - if (op_state == 1) { - /* try to enable objects that are disabled */ - abis_nm_opstart(bts, foh->obj_class, - foh->obj_inst.bts_nr, - foh->obj_inst.trx_nr, - foh->obj_inst.ts_nr); - } -#endif - return 0; -} - -static inline void log_oml_fail_rep(const struct gsm_bts *bts, const char *type, - const char *severity, const uint8_t *p_val, - const char *text) -{ - enum abis_nm_pcause_type pcause = p_val[0]; - enum abis_mm_event_causes cause = osmo_load16be(p_val + 1); - - LOGPC(DNM, LOGL_ERROR, "BTS %u: Failure Event Report: ", bts->nr); - if (type) - LOGPC(DNM, LOGL_ERROR, "Type=%s, ", type); - if (severity) - LOGPC(DNM, LOGL_ERROR, "Severity=%s, ", severity); - - LOGPC(DNM, LOGL_ERROR, "Probable cause=%s: ", - get_value_string(abis_nm_pcause_type_names, pcause)); - - if (pcause == NM_PCAUSE_T_MANUF) - LOGPC(DNM, LOGL_ERROR, "%s, ", - get_value_string(abis_mm_event_cause_names, cause)); - else - LOGPC(DNM, LOGL_ERROR, "%02X %02X ", p_val[1], p_val[2]); - - if (text) { - LOGPC(DNM, LOGL_ERROR, "Additional Text=%s. ", text); - } - - LOGPC(DNM, LOGL_ERROR, "\n"); -} - -static inline void handle_manufact_report(struct gsm_bts *bts, const uint8_t *p_val, const char *type, - const char *severity, const char *text) -{ - enum abis_mm_event_causes cause = osmo_load16be(p_val + 1); - - switch (cause) { - case OSMO_EVT_PCU_VERS: - if (text) { - LOGPC(DNM, LOGL_NOTICE, "BTS %u reported connected PCU version %s\n", bts->nr, text); - osmo_strlcpy(bts->pcu_version, text, sizeof(bts->pcu_version)); - } else { - LOGPC(DNM, LOGL_ERROR, "BTS %u reported PCU disconnection.\n", bts->nr); - bts->pcu_version[0] = '\0'; - } - break; - default: - log_oml_fail_rep(bts, type, severity, p_val, text); - }; -} - -static int rx_fail_evt_rep(struct msgb *mb, struct gsm_bts *bts) -{ - struct abis_om_hdr *oh = msgb_l2(mb); - struct abis_om_fom_hdr *foh = msgb_l3(mb); - struct e1inp_sign_link *sign_link = mb->dst; - struct tlv_parsed tp; - int rc = 0; - const uint8_t *p_val = NULL; - char *p_text = NULL; - const char *e_type = NULL, *severity = NULL; - - abis_nm_tlv_parse(&tp, sign_link->trx->bts, foh->data, - oh->length-sizeof(*foh)); - - if (TLVP_PRESENT(&tp, NM_ATT_ADD_TEXT)) { - p_val = TLVP_VAL(&tp, NM_ATT_ADD_TEXT); - p_text = talloc_strndup(tall_bsc_ctx, (const char *) p_val, - TLVP_LEN(&tp, NM_ATT_ADD_TEXT)); - } - - if (TLVP_PRESENT(&tp, NM_ATT_EVENT_TYPE)) - e_type = abis_nm_event_type_name(*TLVP_VAL(&tp, - NM_ATT_EVENT_TYPE)); - - if (TLVP_PRESENT(&tp, NM_ATT_SEVERITY)) - severity = abis_nm_severity_name(*TLVP_VAL(&tp, - NM_ATT_SEVERITY)); - - if (TLVP_PRESENT(&tp, NM_ATT_PROB_CAUSE)) { - p_val = TLVP_VAL(&tp, NM_ATT_PROB_CAUSE); - - switch (p_val[0]) { - case NM_PCAUSE_T_MANUF: - handle_manufact_report(bts, p_val, e_type, severity, - p_text); - break; - default: - log_oml_fail_rep(bts, e_type, severity, p_val, p_text); - }; - } else { - LOGPC(DNM, LOGL_ERROR, "BTS%u: Failure Event Report without " - "Probable Cause?!\n", bts->nr); - rc = -EINVAL; - } - - if (p_text) - talloc_free(p_text); - - return rc; -} - -static int abis_nm_rcvmsg_report(struct msgb *mb, struct gsm_bts *bts) -{ - struct abis_om_fom_hdr *foh = msgb_l3(mb); - uint8_t mt = foh->msg_type; - - abis_nm_debugp_foh(DNM, foh); - - //nmh->cfg->report_cb(mb, foh); - - switch (mt) { - case NM_MT_STATECHG_EVENT_REP: - return abis_nm_rx_statechg_rep(mb); - break; - case NM_MT_SW_ACTIVATED_REP: - DEBUGPC(DNM, "Software Activated Report\n"); - osmo_signal_dispatch(SS_NM, S_NM_SW_ACTIV_REP, mb); - break; - case NM_MT_FAILURE_EVENT_REP: - rx_fail_evt_rep(mb, bts); - osmo_signal_dispatch(SS_NM, S_NM_FAIL_REP, mb); - break; - case NM_MT_TEST_REP: - DEBUGPC(DNM, "Test Report\n"); - osmo_signal_dispatch(SS_NM, S_NM_TEST_REP, mb); - break; - default: - DEBUGPC(DNM, "reporting NM MT 0x%02x\n", mt); - break; - - }; - - return 0; -} - -/* Activate the specified software into the BTS */ -static int ipacc_sw_activate(struct gsm_bts *bts, uint8_t obj_class, uint8_t i0, uint8_t i1, - uint8_t i2, const struct abis_nm_sw_desc *sw_desc) -{ - struct abis_om_hdr *oh; - struct msgb *msg = nm_msgb_alloc(); - uint16_t len = abis_nm_sw_desc_len(sw_desc, true); - - oh = (struct abis_om_hdr *) msgb_put(msg, ABIS_OM_FOM_HDR_SIZE); - fill_om_fom_hdr(oh, len, NM_MT_ACTIVATE_SW, obj_class, i0, i1, i2); - abis_nm_put_sw_desc(msg, sw_desc, true); - - return abis_nm_sendmsg(bts, msg); -} - -int abis_nm_select_newest_sw(const struct abis_nm_sw_desc *sw_descr, - const size_t size) -{ - int res = 0; - int i; - - for (i = 1; i < size; ++i) { - if (memcmp(sw_descr[res].file_version, sw_descr[i].file_version, - OSMO_MIN(sw_descr[i].file_version_len, - sw_descr[res].file_version_len)) < 0) { - res = i; - } - } - - return res; -} - -static inline bool handle_attr(const struct gsm_bts *bts, enum bts_attribute id, uint8_t *val, uint8_t len) -{ - switch (id) { - case BTS_TYPE_VARIANT: - LOGP(DNM, LOGL_NOTICE, "BTS%u reported variant: %s\n", bts->nr, val); - break; - case BTS_SUB_MODEL: - LOGP(DNM, LOGL_NOTICE, "BTS%u reported submodel: %s\n", bts->nr, val); - break; - default: - return false; - } - return true; -} - -/* Parse Attribute Response Info - return pointer to the actual content */ -static inline uint8_t *parse_attr_resp_info_unreported(uint8_t bts_nr, uint8_t *ari, uint16_t ari_len, uint16_t *out_len) -{ - uint8_t num_unreported = ari[0], i; - - DEBUGP(DNM, "BTS%u Get Attributes Response Info: %u bytes total with %u unreported attributes\n", - bts_nr, ari_len, num_unreported); - - /* +1 because we have to account for number of unreported attributes, prefixing the list: */ - for (i = 0; i < num_unreported; i++) - LOGP(DNM, LOGL_ERROR, "BTS%u Attribute %s is unreported\n", - bts_nr, get_value_string(abis_nm_att_names, ari[i + 1])); - - /* the data starts right after the list of unreported attributes + space for length of that list */ - *out_len = ari_len - (num_unreported + 2); - - return ari + num_unreported + 1; /* we have to account for 1st byte with number of unreported attributes */ -} - -/* Parse Attribute Response Info content for 3GPP TS 52.021 §9.4.30 Manufacturer Id */ -static inline uint8_t *parse_attr_resp_info_manuf_id(struct gsm_bts *bts, uint8_t *data, uint16_t *data_len) -{ - struct tlv_parsed tp; - uint16_t m_id_len = 0; - uint8_t adjust = 0, i; - - abis_nm_tlv_parse(&tp, bts, data, *data_len); - if (TLVP_PRES_LEN(&tp, NM_ATT_MANUF_ID, 2)) { - m_id_len = TLVP_LEN(&tp, NM_ATT_MANUF_ID); - - /* log potential BTS feature vector overflow */ - if (m_id_len > sizeof(bts->_features_data)) - LOGP(DNM, LOGL_NOTICE, "BTS%u Get Attributes Response: feature vector is truncated to %u bytes\n", - bts->nr, MAX_BTS_FEATURES/8); - - /* check that max. expected BTS attribute is above given feature vector length */ - if (m_id_len > OSMO_BYTES_FOR_BITS(_NUM_BTS_FEAT)) - LOGP(DNM, LOGL_NOTICE, "BTS%u Get Attributes Response: reported unexpectedly long (%u bytes) " - "feature vector - most likely it was compiled against newer BSC headers. " - "Consider upgrading your BSC to later version.\n", - bts->nr, m_id_len); - - memcpy(bts->_features_data, TLVP_VAL(&tp, NM_ATT_MANUF_ID), sizeof(bts->_features_data)); - adjust = m_id_len + 3; /* adjust for parsed TL16V struct */ - - for (i = 0; i < _NUM_BTS_FEAT; i++) - if (gsm_bts_has_feature(bts, i) != gsm_btsmodel_has_feature(bts->model, i)) - LOGP(DNM, LOGL_NOTICE, "BTS%u feature '%s' reported via OML does not match statically " - "set feature: %u != %u. Please fix.\n", bts->nr, - get_value_string(gsm_bts_features_descs, i), - gsm_bts_has_feature(bts, i), gsm_btsmodel_has_feature(bts->model, i)); - } - - *data_len -= adjust; - - return data + adjust; -} - -/* Parse Attribute Response Info content for 3GPP TS 52.021 §9.4.28 Manufacturer Dependent State */ -static inline uint8_t *parse_attr_resp_info_manuf_state(const struct gsm_bts_trx *trx, uint8_t *data, uint16_t *data_len) -{ - struct tlv_parsed tp; - const uint8_t *power; - uint8_t adjust = 0; - - if (!trx) /* this attribute does not make sense on BTS level, only on TRX level */ - return data; - - abis_nm_tlv_parse(&tp, trx->bts, data, *data_len); - if (TLVP_PRES_LEN(&tp, NM_ATT_MANUF_STATE, 1)) { - power = TLVP_VAL(&tp, NM_ATT_MANUF_STATE); - LOGP(DNM, LOGL_NOTICE, "%s Get Attributes Response: nominal power is %u\n", gsm_trx_name(trx), *power); - adjust = 2; /* adjust for parsed TV struct */ - } - - *data_len -= adjust; - - return data + adjust; -} - -/* Handle 3GPP TS 52.021 §9.4.64 Get Attribute Response Info */ -static int abis_nm_rx_get_attr_resp(struct msgb *mb, const struct gsm_bts_trx *trx) -{ - struct abis_om_hdr *oh = msgb_l2(mb); - struct abis_om_fom_hdr *foh = msgb_l3(mb); - struct e1inp_sign_link *sign_link = mb->dst; - struct gsm_bts *bts = trx ? trx->bts : sign_link->trx->bts; - struct tlv_parsed tp; - uint8_t *data, i; - uint16_t data_len; - int rc; - struct abis_nm_sw_desc sw_descr[MAX_BTS_ATTR]; - - abis_nm_debugp_foh(DNM, foh); - - DEBUGPC(DNM, "Get Attributes Response for BTS%u\n", bts->nr); - - abis_nm_tlv_parse(&tp, bts, foh->data, oh->length-sizeof(*foh)); - if (!TLVP_PRES_LEN(&tp, NM_ATT_GET_ARI, 1)) { - LOGP(DNM, LOGL_ERROR, "BTS%u: Get Attributes Response without Response Info?!\n", bts->nr); - return -EINVAL; - } - - data = parse_attr_resp_info_unreported(bts->nr, TLVP_VAL(&tp, NM_ATT_GET_ARI), TLVP_LEN(&tp, NM_ATT_GET_ARI), - &data_len); - - data = parse_attr_resp_info_manuf_state(trx, data, &data_len); - data = parse_attr_resp_info_manuf_id(bts, data, &data_len); - - /* after parsing manufacturer-specific attributes there's list of replies in form of sw-conf structure: */ - rc = abis_nm_get_sw_conf(data, data_len, &sw_descr[0], ARRAY_SIZE(sw_descr)); - if (rc > 0) { - for (i = 0; i < rc; i++) { - if (!handle_attr(bts, str2btsattr((const char *)sw_descr[i].file_id), - sw_descr[i].file_version, sw_descr[i].file_version_len)) - LOGP(DNM, LOGL_NOTICE, "BTS%u: ARI reported sw[%d/%d]: %s is %s\n", - bts->nr, i, rc, sw_descr[i].file_id, sw_descr[i].file_version); - } - } else - LOGP(DNM, LOGL_ERROR, "BTS%u: failed to parse SW-Config part of Get Attribute Response Info: %s\n", - bts->nr, strerror(-rc)); - - return 0; -} - -/* 3GPP TS 52.021 §6.2.5 */ -static int abis_nm_rx_sw_act_req(struct msgb *mb) -{ - struct abis_om_hdr *oh = msgb_l2(mb); - struct abis_om_fom_hdr *foh = msgb_l3(mb); - struct e1inp_sign_link *sign_link = mb->dst; - struct tlv_parsed tp; - const uint8_t *sw_config; - int ret, sw_config_len, len; - struct abis_nm_sw_desc sw_descr[MAX_BTS_ATTR]; - - abis_nm_debugp_foh(DNM, foh); - - DEBUGPC(DNM, "SW Activate Request: "); - - DEBUGP(DNM, "Software Activate Request, ACKing and Activating\n"); - - ret = abis_nm_sw_act_req_ack(sign_link->trx->bts, foh->obj_class, - foh->obj_inst.bts_nr, - foh->obj_inst.trx_nr, - foh->obj_inst.ts_nr, 0, - foh->data, oh->length-sizeof(*foh)); - if (ret != 0) { - LOGP(DNM, LOGL_ERROR, - "Sending SW ActReq ACK failed: %d\n", ret); - return ret; - } - - abis_nm_tlv_parse(&tp, sign_link->trx->bts, foh->data, oh->length-sizeof(*foh)); - sw_config = TLVP_VAL(&tp, NM_ATT_SW_CONFIG); - sw_config_len = TLVP_LEN(&tp, NM_ATT_SW_CONFIG); - if (!TLVP_PRESENT(&tp, NM_ATT_SW_CONFIG)) { - LOGP(DNM, LOGL_ERROR, - "SW config not found! Can't continue.\n"); - return -EINVAL; - } else { - DEBUGP(DNM, "Found SW config: %s\n", osmo_hexdump(sw_config, sw_config_len)); - } - - /* Parse up to two sw descriptions from the data */ - len = abis_nm_get_sw_conf(sw_config, sw_config_len, &sw_descr[0], - ARRAY_SIZE(sw_descr)); - if (len <= 0) { - LOGP(DNM, LOGL_ERROR, "Failed to parse SW Config.\n"); - return -EINVAL; - } - - ret = abis_nm_select_newest_sw(&sw_descr[0], len); - DEBUGP(DNM, "Selected sw description %d of %d\n", ret, len); - - return ipacc_sw_activate(sign_link->trx->bts, foh->obj_class, - foh->obj_inst.bts_nr, - foh->obj_inst.trx_nr, - foh->obj_inst.ts_nr, - &sw_descr[ret]); -} - -/* Receive a CHANGE_ADM_STATE_ACK, parse the TLV and update local state */ -static int abis_nm_rx_chg_adm_state_ack(struct msgb *mb) -{ - struct abis_om_hdr *oh = msgb_l2(mb); - struct abis_om_fom_hdr *foh = msgb_l3(mb); - struct e1inp_sign_link *sign_link = mb->dst; - struct tlv_parsed tp; - uint8_t adm_state; - - abis_nm_tlv_parse(&tp, sign_link->trx->bts, foh->data, oh->length-sizeof(*foh)); - if (!TLVP_PRESENT(&tp, NM_ATT_ADM_STATE)) - return -EINVAL; - - adm_state = *TLVP_VAL(&tp, NM_ATT_ADM_STATE); - - return update_admstate(sign_link->trx->bts, foh->obj_class, &foh->obj_inst, adm_state); -} - -static int abis_nm_rx_lmt_event(struct msgb *mb) -{ - struct abis_om_hdr *oh = msgb_l2(mb); - struct abis_om_fom_hdr *foh = msgb_l3(mb); - struct e1inp_sign_link *sign_link = mb->dst; - struct tlv_parsed tp; - - DEBUGP(DNM, "LMT Event "); - abis_nm_tlv_parse(&tp, sign_link->trx->bts, foh->data, oh->length-sizeof(*foh)); - if (TLVP_PRESENT(&tp, NM_ATT_BS11_LMT_LOGON_SESSION) && - TLVP_LEN(&tp, NM_ATT_BS11_LMT_LOGON_SESSION) >= 1) { - uint8_t onoff = *TLVP_VAL(&tp, NM_ATT_BS11_LMT_LOGON_SESSION); - DEBUGPC(DNM, "LOG%s ", onoff ? "ON" : "OFF"); - } - if (TLVP_PRESENT(&tp, NM_ATT_BS11_LMT_USER_ACC_LEV) && - TLVP_LEN(&tp, NM_ATT_BS11_LMT_USER_ACC_LEV) >= 1) { - uint8_t level = *TLVP_VAL(&tp, NM_ATT_BS11_LMT_USER_ACC_LEV); - DEBUGPC(DNM, "Level=%u ", level); - } - if (TLVP_PRESENT(&tp, NM_ATT_BS11_LMT_USER_NAME) && - TLVP_LEN(&tp, NM_ATT_BS11_LMT_USER_NAME) >= 1) { - char *name = (char *) TLVP_VAL(&tp, NM_ATT_BS11_LMT_USER_NAME); - DEBUGPC(DNM, "Username=%s ", name); - } - DEBUGPC(DNM, "\n"); - /* FIXME: parse LMT LOGON TIME */ - return 0; -} - -void abis_nm_queue_send_next(struct gsm_bts *bts) -{ - int wait = 0; - struct msgb *msg; - /* the queue is empty */ - while (!llist_empty(&bts->abis_queue)) { - msg = msgb_dequeue(&bts->abis_queue); - wait = OBSC_NM_W_ACK_CB(msg); - _abis_nm_sendmsg(msg); - - if (wait) - break; - } - - bts->abis_nm_pend = wait; -} - -/* Receive a OML NM Message from BTS */ -static int abis_nm_rcvmsg_fom(struct msgb *mb) -{ - struct abis_om_hdr *oh = msgb_l2(mb); - struct abis_om_fom_hdr *foh = msgb_l3(mb); - struct e1inp_sign_link *sign_link = mb->dst; - uint8_t mt = foh->msg_type; - /* sign_link might get deleted via osmo_signal_dispatch -> save bts */ - struct gsm_bts *bts = sign_link->trx->bts; - int ret = 0; - - /* check for unsolicited message */ - if (is_report(mt)) - return abis_nm_rcvmsg_report(mb, bts); - - if (is_in_arr(mt, abis_nm_sw_load_msgs, ARRAY_SIZE(abis_nm_sw_load_msgs))) - return abis_nm_rcvmsg_sw(mb); - - if (is_in_arr(mt, abis_nm_nacks, ARRAY_SIZE(abis_nm_nacks))) { - struct nm_nack_signal_data nack_data; - struct tlv_parsed tp; - - abis_nm_debugp_foh(DNM, foh); - - DEBUGPC(DNM, "%s NACK ", abis_nm_nack_name(mt)); - - abis_nm_tlv_parse(&tp, bts, foh->data, oh->length-sizeof(*foh)); - if (TLVP_PRESENT(&tp, NM_ATT_NACK_CAUSES)) - DEBUGPC(DNM, "CAUSE=%s\n", - abis_nm_nack_cause_name(*TLVP_VAL(&tp, NM_ATT_NACK_CAUSES))); - else - DEBUGPC(DNM, "\n"); - - nack_data.msg = mb; - nack_data.mt = mt; - nack_data.bts = bts; - osmo_signal_dispatch(SS_NM, S_NM_NACK, &nack_data); - abis_nm_queue_send_next(bts); - return 0; - } -#if 0 - /* check if last message is to be acked */ - if (is_ack_nack(nmh->last_msgtype)) { - if (mt == MT_ACK(nmh->last_msgtype)) { - DEBUGP(DNM, "received ACK (0x%x)\n", foh->msg_type); - /* we got our ACK, continue sending the next msg */ - } else if (mt == MT_NACK(nmh->last_msgtype)) { - /* we got a NACK, signal this to the caller */ - DEBUGP(DNM, "received NACK (0x%x)\n", foh->msg_type); - /* FIXME: somehow signal this to the caller */ - } else { - /* really strange things happen */ - return -EINVAL; - } - } -#endif - - switch (mt) { - case NM_MT_CHG_ADM_STATE_ACK: - ret = abis_nm_rx_chg_adm_state_ack(mb); - break; - case NM_MT_SW_ACT_REQ: - ret = abis_nm_rx_sw_act_req(mb); - break; - case NM_MT_BS11_LMT_SESSION: - ret = abis_nm_rx_lmt_event(mb); - break; - case NM_MT_OPSTART_ACK: - abis_nm_debugp_foh(DNM, foh); - DEBUGPC(DNM, "Opstart ACK\n"); - break; - case NM_MT_SET_CHAN_ATTR_ACK: - abis_nm_debugp_foh(DNM, foh); - DEBUGPC(DNM, "Set Channel Attributes ACK\n"); - break; - case NM_MT_SET_RADIO_ATTR_ACK: - abis_nm_debugp_foh(DNM, foh); - DEBUGPC(DNM, "Set Radio Carrier Attributes ACK\n"); - break; - case NM_MT_CONN_MDROP_LINK_ACK: - abis_nm_debugp_foh(DNM, foh); - DEBUGPC(DNM, "CONN MDROP LINK ACK\n"); - break; - case NM_MT_IPACC_RESTART_ACK: - osmo_signal_dispatch(SS_NM, S_NM_IPACC_RESTART_ACK, NULL); - break; - case NM_MT_IPACC_RESTART_NACK: - osmo_signal_dispatch(SS_NM, S_NM_IPACC_RESTART_NACK, NULL); - break; - case NM_MT_SET_BTS_ATTR_ACK: - break; - case NM_MT_GET_ATTR_RESP: - ret = abis_nm_rx_get_attr_resp(mb, gsm_bts_trx_num(bts, (foh)->obj_inst.trx_nr)); - break; - default: - abis_nm_debugp_foh(DNM, foh); - LOGPC(DNM, LOGL_ERROR, "Unhandled message %s\n", - get_value_string(abis_nm_msgtype_names, mt)); - } - - abis_nm_queue_send_next(bts); - return ret; -} - -static int abis_nm_rx_ipacc(struct msgb *mb); - -static int abis_nm_rcvmsg_manuf(struct msgb *mb) -{ - int rc; - struct e1inp_sign_link *sign_link = mb->dst; - int bts_type = sign_link->trx->bts->type; - - switch (bts_type) { - case GSM_BTS_TYPE_NANOBTS: - case GSM_BTS_TYPE_OSMOBTS: - rc = abis_nm_rx_ipacc(mb); - abis_nm_queue_send_next(sign_link->trx->bts); - break; - default: - LOGP(DNM, LOGL_ERROR, "don't know how to parse OML for this " - "BTS type (%u)\n", bts_type); - rc = 0; - break; - } - - return rc; -} - -/* High-Level API */ -/* Entry-point where L2 OML from BTS enters the NM code */ -int abis_nm_rcvmsg(struct msgb *msg) -{ - struct abis_om_hdr *oh = msgb_l2(msg); - int rc = 0; - - /* Various consistency checks */ - if (oh->placement != ABIS_OM_PLACEMENT_ONLY) { - LOGP(DNM, LOGL_ERROR, "ABIS OML placement 0x%x not supported\n", - oh->placement); - if (oh->placement != ABIS_OM_PLACEMENT_FIRST) { - rc = -EINVAL; - goto err; - } - } - if (oh->sequence != 0) { - LOGP(DNM, LOGL_ERROR, "ABIS OML sequence 0x%x != 0x00\n", - oh->sequence); - rc = -EINVAL; - goto err; - } -#if 0 - unsigned int l2_len = msg->tail - (uint8_t *)msgb_l2(msg); - unsigned int hlen = sizeof(*oh) + sizeof(struct abis_om_fom_hdr); - if (oh->length + hlen > l2_len) { - LOGP(DNM, LOGL_ERROR, "ABIS OML truncated message (%u > %u)\n", - oh->length + sizeof(*oh), l2_len); - return -EINVAL; - } - if (oh->length + hlen < l2_len) - LOGP(DNM, LOGL_ERROR, "ABIS OML message with extra trailer?!? (oh->len=%d, sizeof_oh=%d l2_len=%d\n", oh->length, sizeof(*oh), l2_len); -#endif - msg->l3h = (unsigned char *)oh + sizeof(*oh); - - switch (oh->mdisc) { - case ABIS_OM_MDISC_FOM: - rc = abis_nm_rcvmsg_fom(msg); - break; - case ABIS_OM_MDISC_MANUF: - rc = abis_nm_rcvmsg_manuf(msg); - break; - case ABIS_OM_MDISC_MMI: - case ABIS_OM_MDISC_TRAU: - LOGP(DNM, LOGL_ERROR, "unimplemented ABIS OML message discriminator 0x%x\n", - oh->mdisc); - break; - default: - LOGP(DNM, LOGL_ERROR, "unknown ABIS OML message discriminator 0x%x\n", - oh->mdisc); - rc = -EINVAL; - break; - } -err: - msgb_free(msg); - return rc; -} - -#if 0 -/* initialized all resources */ -struct abis_nm_h *abis_nm_init(struct abis_nm_cfg *cfg) -{ - struct abis_nm_h *nmh; - - nmh = malloc(sizeof(*nmh)); - if (!nmh) - return NULL; - - nmh->cfg = cfg; - - return nmh; -} - -/* free all resources */ -void abis_nm_fini(struct abis_nm_h *nmh) -{ - free(nmh); -} -#endif - -/* Here we are trying to define a high-level API that can be used by - * the actual BSC implementation. However, the architecture is currently - * still under design. Ideally the calls to this API would be synchronous, - * while the underlying stack behind the APi runs in a traditional select - * based state machine. - */ - -/* 6.2 Software Load: */ -enum sw_state { - SW_STATE_NONE, - SW_STATE_WAIT_INITACK, - SW_STATE_WAIT_SEGACK, - SW_STATE_WAIT_ENDACK, - SW_STATE_WAIT_ACTACK, - SW_STATE_ERROR, -}; - -struct abis_nm_sw { - struct gsm_bts *bts; - int trx_nr; - gsm_cbfn *cbfn; - void *cb_data; - int forced; - - /* this will become part of the SW LOAD INITIATE */ - uint8_t obj_class; - uint8_t obj_instance[3]; - - uint8_t file_id[255]; - uint8_t file_id_len; - - uint8_t file_version[255]; - uint8_t file_version_len; - - uint8_t window_size; - uint8_t seg_in_window; - - int fd; - FILE *stream; - enum sw_state state; - int last_seg; -}; - -static struct abis_nm_sw g_sw; - -static void sw_add_file_id_and_ver(struct abis_nm_sw *sw, struct msgb *msg) -{ - if (sw->bts->type == GSM_BTS_TYPE_NANOBTS) { - msgb_v_put(msg, NM_ATT_SW_DESCR); - msgb_tl16v_put(msg, NM_ATT_FILE_ID, sw->file_id_len, sw->file_id); - msgb_tl16v_put(msg, NM_ATT_FILE_VERSION, sw->file_version_len, - sw->file_version); - } else if (sw->bts->type == GSM_BTS_TYPE_BS11) { - msgb_tlv_put(msg, NM_ATT_FILE_ID, sw->file_id_len, sw->file_id); - msgb_tlv_put(msg, NM_ATT_FILE_VERSION, sw->file_version_len, - sw->file_version); - } else { - LOGP(DNM, LOGL_ERROR, "Please implement this for the BTS.\n"); - } -} - -/* 6.2.1 / 8.3.1: Load Data Initiate */ -static int sw_load_init(struct abis_nm_sw *sw) -{ - struct abis_om_hdr *oh; - struct msgb *msg = nm_msgb_alloc(); - uint8_t len = 3*2 + sw->file_id_len + sw->file_version_len; - - oh = (struct abis_om_hdr *) msgb_put(msg, ABIS_OM_FOM_HDR_SIZE); - fill_om_fom_hdr(oh, len, NM_MT_LOAD_INIT, sw->obj_class, - sw->obj_instance[0], sw->obj_instance[1], - sw->obj_instance[2]); - - sw_add_file_id_and_ver(sw, msg); - msgb_tv_put(msg, NM_ATT_WINDOW_SIZE, sw->window_size); - - return abis_nm_sendmsg(sw->bts, msg); -} - -static int is_last_line(FILE *stream) -{ - char next_seg_buf[256]; - long pos; - - /* check if we're sending the last line */ - pos = ftell(stream); - - /* Did ftell fail? Then we are at the end for sure */ - if (pos < 0) - return 1; - - if (!fgets(next_seg_buf, sizeof(next_seg_buf)-2, stream)) { - int rc = fseek(stream, pos, SEEK_SET); - if (rc < 0) - return rc; - return 1; - } - - fseek(stream, pos, SEEK_SET); - return 0; -} - -/* 6.2.2 / 8.3.2 Load Data Segment */ -static int sw_load_segment(struct abis_nm_sw *sw) -{ - struct abis_om_hdr *oh; - struct msgb *msg = nm_msgb_alloc(); - char seg_buf[256]; - char *line_buf = seg_buf+2; - unsigned char *tlv; - int len; - - oh = (struct abis_om_hdr *) msgb_put(msg, ABIS_OM_FOM_HDR_SIZE); - - switch (sw->bts->type) { - case GSM_BTS_TYPE_BS11: - if (fgets(line_buf, sizeof(seg_buf)-2, sw->stream) == NULL) { - perror("fgets reading segment"); - return -EINVAL; - } - seg_buf[0] = 0x00; - - /* check if we're sending the last line */ - sw->last_seg = is_last_line(sw->stream); - if (sw->last_seg) - seg_buf[1] = 0; - else - seg_buf[1] = 1 + sw->seg_in_window++; - - len = strlen(line_buf) + 2; - tlv = msgb_put(msg, TLV_GROSS_LEN(len)); - tlv_put(tlv, NM_ATT_BS11_FILE_DATA, len, (uint8_t *)seg_buf); - /* BS11 wants CR + LF in excess of the TLV length !?! */ - tlv[1] -= 2; - - /* we only now know the exact length for the OM hdr */ - len = strlen(line_buf)+2; - break; - case GSM_BTS_TYPE_NANOBTS: { - osmo_static_assert(sizeof(seg_buf) >= IPACC_SEGMENT_SIZE, buffer_big_enough); - len = read(sw->fd, &seg_buf, IPACC_SEGMENT_SIZE); - if (len < 0) { - perror("read failed"); - return -EINVAL; - } - - if (len != IPACC_SEGMENT_SIZE) - sw->last_seg = 1; - - ++sw->seg_in_window; - msgb_tl16v_put(msg, NM_ATT_IPACC_FILE_DATA, len, (const uint8_t *) seg_buf); - len += 3; - break; - } - default: - LOGP(DNM, LOGL_ERROR, "sw_load_segment needs implementation for the BTS.\n"); - /* FIXME: Other BTS types */ - return -1; - } - - fill_om_fom_hdr(oh, len, NM_MT_LOAD_SEG, sw->obj_class, - sw->obj_instance[0], sw->obj_instance[1], - sw->obj_instance[2]); - - return abis_nm_sendmsg_direct(sw->bts, msg); -} - -/* 6.2.4 / 8.3.4 Load Data End */ -static int sw_load_end(struct abis_nm_sw *sw) -{ - struct abis_om_hdr *oh; - struct msgb *msg = nm_msgb_alloc(); - uint8_t len = 2*2 + sw->file_id_len + sw->file_version_len; - - oh = (struct abis_om_hdr *) msgb_put(msg, ABIS_OM_FOM_HDR_SIZE); - fill_om_fom_hdr(oh, len, NM_MT_LOAD_END, sw->obj_class, - sw->obj_instance[0], sw->obj_instance[1], - sw->obj_instance[2]); - - sw_add_file_id_and_ver(sw, msg); - return abis_nm_sendmsg(sw->bts, msg); -} - -/* Activate the specified software into the BTS */ -static int sw_activate(struct abis_nm_sw *sw) -{ - struct abis_om_hdr *oh; - struct msgb *msg = nm_msgb_alloc(); - uint8_t len = 2*2 + sw->file_id_len + sw->file_version_len; - - oh = (struct abis_om_hdr *) msgb_put(msg, ABIS_OM_FOM_HDR_SIZE); - fill_om_fom_hdr(oh, len, NM_MT_ACTIVATE_SW, sw->obj_class, - sw->obj_instance[0], sw->obj_instance[1], - sw->obj_instance[2]); - - /* FIXME: this is BS11 specific format */ - msgb_tlv_put(msg, NM_ATT_FILE_ID, sw->file_id_len, sw->file_id); - msgb_tlv_put(msg, NM_ATT_FILE_VERSION, sw->file_version_len, - sw->file_version); - - return abis_nm_sendmsg(sw->bts, msg); -} - -struct sdp_firmware { - char magic[4]; - char more_magic[4]; - unsigned int header_length; - unsigned int file_length; -} __attribute__ ((packed)); - -static int parse_sdp_header(struct abis_nm_sw *sw) -{ - struct sdp_firmware firmware_header; - int rc; - struct stat stat; - - rc = read(sw->fd, &firmware_header, sizeof(firmware_header)); - if (rc != sizeof(firmware_header)) { - LOGP(DNM, LOGL_ERROR, "Could not read SDP file header.\n"); - return -1; - } - - if (strncmp(firmware_header.magic, " SDP", 4) != 0) { - LOGP(DNM, LOGL_ERROR, "The magic number1 is wrong.\n"); - return -1; - } - - if (firmware_header.more_magic[0] != 0x10 || - firmware_header.more_magic[1] != 0x02 || - firmware_header.more_magic[2] != 0x00 || - firmware_header.more_magic[3] != 0x00) { - LOGP(DNM, LOGL_ERROR, "The more magic number is wrong.\n"); - return -1; - } - - - if (fstat(sw->fd, &stat) == -1) { - LOGP(DNM, LOGL_ERROR, "Could not stat the file.\n"); - return -1; - } - - if (ntohl(firmware_header.file_length) != stat.st_size) { - LOGP(DNM, LOGL_ERROR, "The filesizes do not match.\n"); - return -1; - } - - /* go back to the start as we checked the whole filesize.. */ - lseek(sw->fd, 0l, SEEK_SET); - LOGP(DNM, LOGL_NOTICE, "The ipaccess SDP header is not fully understood.\n" - "There might be checksums in the file that are not\n" - "verified and incomplete firmware might be flashed.\n" - "There is absolutely no WARRANTY that flashing will\n" - "work.\n"); - return 0; -} - -static int sw_open_file(struct abis_nm_sw *sw, const char *fname) -{ - char file_id[12+1]; - char file_version[80+1]; - int rc; - - sw->fd = open(fname, O_RDONLY); - if (sw->fd < 0) - return sw->fd; - - switch (sw->bts->type) { - case GSM_BTS_TYPE_BS11: - sw->stream = fdopen(sw->fd, "r"); - if (!sw->stream) { - perror("fdopen"); - return -1; - } - /* read first line and parse file ID and VERSION */ - rc = fscanf(sw->stream, "@(#)%12s:%80s\r\n", - file_id, file_version); - if (rc != 2) { - perror("parsing header line of software file"); - return -1; - } - strcpy((char *)sw->file_id, file_id); - sw->file_id_len = strlen(file_id); - strcpy((char *)sw->file_version, file_version); - sw->file_version_len = strlen(file_version); - /* rewind to start of file */ - rewind(sw->stream); - break; - case GSM_BTS_TYPE_NANOBTS: - /* TODO: extract that from the filename or content */ - rc = parse_sdp_header(sw); - if (rc < 0) { - fprintf(stderr, "Could not parse the ipaccess SDP header\n"); - return -1; - } - - strcpy((char *)sw->file_id, "id"); - sw->file_id_len = 3; - strcpy((char *)sw->file_version, "version"); - sw->file_version_len = 8; - break; - default: - /* We don't know how to treat them yet */ - close(sw->fd); - return -EINVAL; - } - - return 0; -} - -static void sw_close_file(struct abis_nm_sw *sw) -{ - switch (sw->bts->type) { - case GSM_BTS_TYPE_BS11: - fclose(sw->stream); - break; - default: - close(sw->fd); - break; - } -} - -/* Fill the window */ -static int sw_fill_window(struct abis_nm_sw *sw) -{ - int rc; - - while (sw->seg_in_window < sw->window_size) { - rc = sw_load_segment(sw); - if (rc < 0) - return rc; - if (sw->last_seg) - break; - } - return 0; -} - -/* callback function from abis_nm_rcvmsg() handler */ -static int abis_nm_rcvmsg_sw(struct msgb *mb) -{ - struct abis_om_fom_hdr *foh = msgb_l3(mb); - struct e1inp_sign_link *sign_link = mb->dst; - int rc = -1; - struct abis_nm_sw *sw = &g_sw; - enum sw_state old_state = sw->state; - - //DEBUGP(DNM, "state %u, NM MT 0x%02x\n", sw->state, foh->msg_type); - - switch (sw->state) { - case SW_STATE_WAIT_INITACK: - switch (foh->msg_type) { - case NM_MT_LOAD_INIT_ACK: - /* fill window with segments */ - if (sw->cbfn) - sw->cbfn(GSM_HOOK_NM_SWLOAD, - NM_MT_LOAD_INIT_ACK, mb, - sw->cb_data, NULL); - rc = sw_fill_window(sw); - sw->state = SW_STATE_WAIT_SEGACK; - abis_nm_queue_send_next(sign_link->trx->bts); - break; - case NM_MT_LOAD_INIT_NACK: - if (sw->forced) { - DEBUGP(DNM, "FORCED: Ignoring Software Load " - "Init NACK\n"); - if (sw->cbfn) - sw->cbfn(GSM_HOOK_NM_SWLOAD, - NM_MT_LOAD_INIT_ACK, mb, - sw->cb_data, NULL); - rc = sw_fill_window(sw); - sw->state = SW_STATE_WAIT_SEGACK; - } else { - DEBUGP(DNM, "Software Load Init NACK\n"); - /* FIXME: cause */ - if (sw->cbfn) - sw->cbfn(GSM_HOOK_NM_SWLOAD, - NM_MT_LOAD_INIT_NACK, mb, - sw->cb_data, NULL); - sw->state = SW_STATE_ERROR; - } - abis_nm_queue_send_next(sign_link->trx->bts); - break; - } - break; - case SW_STATE_WAIT_SEGACK: - switch (foh->msg_type) { - case NM_MT_LOAD_SEG_ACK: - if (sw->cbfn) - sw->cbfn(GSM_HOOK_NM_SWLOAD, - NM_MT_LOAD_SEG_ACK, mb, - sw->cb_data, NULL); - sw->seg_in_window = 0; - if (!sw->last_seg) { - /* fill window with more segments */ - rc = sw_fill_window(sw); - sw->state = SW_STATE_WAIT_SEGACK; - } else { - /* end the transfer */ - sw->state = SW_STATE_WAIT_ENDACK; - rc = sw_load_end(sw); - } - abis_nm_queue_send_next(sign_link->trx->bts); - break; - case NM_MT_LOAD_ABORT: - if (sw->cbfn) - sw->cbfn(GSM_HOOK_NM_SWLOAD, - NM_MT_LOAD_ABORT, mb, - sw->cb_data, NULL); - break; - } - break; - case SW_STATE_WAIT_ENDACK: - switch (foh->msg_type) { - case NM_MT_LOAD_END_ACK: - sw_close_file(sw); - DEBUGP(DNM, "Software Load End (BTS %u)\n", - sw->bts->nr); - sw->state = SW_STATE_NONE; - if (sw->cbfn) - sw->cbfn(GSM_HOOK_NM_SWLOAD, - NM_MT_LOAD_END_ACK, mb, - sw->cb_data, NULL); - rc = 0; - abis_nm_queue_send_next(sign_link->trx->bts); - break; - case NM_MT_LOAD_END_NACK: - if (sw->forced) { - DEBUGP(DNM, "FORCED: Ignoring Software Load" - "End NACK\n"); - sw->state = SW_STATE_NONE; - if (sw->cbfn) - sw->cbfn(GSM_HOOK_NM_SWLOAD, - NM_MT_LOAD_END_ACK, mb, - sw->cb_data, NULL); - } else { - DEBUGP(DNM, "Software Load End NACK\n"); - /* FIXME: cause */ - sw->state = SW_STATE_ERROR; - if (sw->cbfn) - sw->cbfn(GSM_HOOK_NM_SWLOAD, - NM_MT_LOAD_END_NACK, mb, - sw->cb_data, NULL); - } - abis_nm_queue_send_next(sign_link->trx->bts); - break; - } - case SW_STATE_WAIT_ACTACK: - switch (foh->msg_type) { - case NM_MT_ACTIVATE_SW_ACK: - /* we're done */ - DEBUGP(DNM, "Activate Software DONE!\n"); - sw->state = SW_STATE_NONE; - rc = 0; - if (sw->cbfn) - sw->cbfn(GSM_HOOK_NM_SWLOAD, - NM_MT_ACTIVATE_SW_ACK, mb, - sw->cb_data, NULL); - abis_nm_queue_send_next(sign_link->trx->bts); - break; - case NM_MT_ACTIVATE_SW_NACK: - DEBUGP(DNM, "Activate Software NACK\n"); - /* FIXME: cause */ - sw->state = SW_STATE_ERROR; - if (sw->cbfn) - sw->cbfn(GSM_HOOK_NM_SWLOAD, - NM_MT_ACTIVATE_SW_NACK, mb, - sw->cb_data, NULL); - abis_nm_queue_send_next(sign_link->trx->bts); - break; - } - case SW_STATE_NONE: - switch (foh->msg_type) { - case NM_MT_ACTIVATE_SW_ACK: - rc = 0; - break; - } - break; - case SW_STATE_ERROR: - break; - } - - if (rc) - DEBUGP(DNM, "unexpected NM MT 0x%02x in state %u -> %u\n", - foh->msg_type, old_state, sw->state); - - return rc; -} - -/* Load the specified software into the BTS */ -int abis_nm_software_load(struct gsm_bts *bts, int trx_nr, const char *fname, - uint8_t win_size, int forced, - gsm_cbfn *cbfn, void *cb_data) -{ - struct abis_nm_sw *sw = &g_sw; - int rc; - - DEBUGP(DNM, "Software Load (BTS %u, File \"%s\")\n", - bts->nr, fname); - - if (sw->state != SW_STATE_NONE) - return -EBUSY; - - sw->bts = bts; - sw->trx_nr = trx_nr; - - switch (bts->type) { - case GSM_BTS_TYPE_BS11: - sw->obj_class = NM_OC_SITE_MANAGER; - sw->obj_instance[0] = 0xff; - sw->obj_instance[1] = 0xff; - sw->obj_instance[2] = 0xff; - break; - case GSM_BTS_TYPE_NANOBTS: - sw->obj_class = NM_OC_BASEB_TRANSC; - sw->obj_instance[0] = sw->bts->nr; - sw->obj_instance[1] = sw->trx_nr; - sw->obj_instance[2] = 0xff; - break; - case GSM_BTS_TYPE_UNKNOWN: - default: - LOGPC(DNM, LOGL_ERROR, "Software Load not properly implemented.\n"); - return -1; - break; - } - sw->window_size = win_size; - sw->state = SW_STATE_WAIT_INITACK; - sw->cbfn = cbfn; - sw->cb_data = cb_data; - sw->forced = forced; - - rc = sw_open_file(sw, fname); - if (rc < 0) { - sw->state = SW_STATE_NONE; - return rc; - } - - return sw_load_init(sw); -} - -int abis_nm_software_load_status(struct gsm_bts *bts) -{ - struct abis_nm_sw *sw = &g_sw; - struct stat st; - int rc, percent; - - rc = fstat(sw->fd, &st); - if (rc < 0) { - perror("ERROR during stat"); - return rc; - } - - if (sw->stream) - percent = (ftell(sw->stream) * 100) / st.st_size; - else - percent = (lseek(sw->fd, 0, SEEK_CUR) * 100) / st.st_size; - return percent; -} - -/* Activate the specified software into the BTS */ -int abis_nm_software_activate(struct gsm_bts *bts, const char *fname, - gsm_cbfn *cbfn, void *cb_data) -{ - struct abis_nm_sw *sw = &g_sw; - int rc; - - DEBUGP(DNM, "Activating Software (BTS %u, File \"%s\")\n", - bts->nr, fname); - - if (sw->state != SW_STATE_NONE) - return -EBUSY; - - sw->bts = bts; - sw->obj_class = NM_OC_SITE_MANAGER; - sw->obj_instance[0] = 0xff; - sw->obj_instance[1] = 0xff; - sw->obj_instance[2] = 0xff; - sw->state = SW_STATE_WAIT_ACTACK; - sw->cbfn = cbfn; - sw->cb_data = cb_data; - - /* Open the file in order to fill some sw struct members */ - rc = sw_open_file(sw, fname); - if (rc < 0) { - sw->state = SW_STATE_NONE; - return rc; - } - sw_close_file(sw); - - return sw_activate(sw); -} - -static void fill_nm_channel(struct abis_nm_channel *ch, uint8_t bts_port, - uint8_t ts_nr, uint8_t subslot_nr) -{ - ch->attrib = NM_ATT_ABIS_CHANNEL; - ch->bts_port = bts_port; - ch->timeslot = ts_nr; - ch->subslot = subslot_nr; -} - -int abis_nm_establish_tei(struct gsm_bts *bts, uint8_t trx_nr, - uint8_t e1_port, uint8_t e1_timeslot, uint8_t e1_subslot, - uint8_t tei) -{ - struct abis_om_hdr *oh; - struct abis_nm_channel *ch; - uint8_t len = sizeof(*ch) + 2; - struct msgb *msg = nm_msgb_alloc(); - - oh = (struct abis_om_hdr *) msgb_put(msg, ABIS_OM_FOM_HDR_SIZE); - fill_om_fom_hdr(oh, len, NM_MT_ESTABLISH_TEI, NM_OC_RADIO_CARRIER, - bts->bts_nr, trx_nr, 0xff); - - msgb_tv_put(msg, NM_ATT_TEI, tei); - - ch = (struct abis_nm_channel *) msgb_put(msg, sizeof(*ch)); - fill_nm_channel(ch, e1_port, e1_timeslot, e1_subslot); - - return abis_nm_sendmsg(bts, msg); -} - -/* connect signalling of one (BTS,TRX) to a particular timeslot on the E1 */ -int abis_nm_conn_terr_sign(struct gsm_bts_trx *trx, - uint8_t e1_port, uint8_t e1_timeslot, uint8_t e1_subslot) -{ - struct gsm_bts *bts = trx->bts; - struct abis_om_hdr *oh; - struct abis_nm_channel *ch; - struct msgb *msg = nm_msgb_alloc(); - - oh = (struct abis_om_hdr *) msgb_put(msg, ABIS_OM_FOM_HDR_SIZE); - fill_om_fom_hdr(oh, sizeof(*ch), NM_MT_CONN_TERR_SIGN, - NM_OC_RADIO_CARRIER, bts->bts_nr, trx->nr, 0xff); - - ch = (struct abis_nm_channel *) msgb_put(msg, sizeof(*ch)); - fill_nm_channel(ch, e1_port, e1_timeslot, e1_subslot); - - return abis_nm_sendmsg(bts, msg); -} - -#if 0 -int abis_nm_disc_terr_sign(struct abis_nm_h *h, struct abis_om_obj_inst *inst, - struct abis_nm_abis_channel *chan) -{ -} -#endif - -int abis_nm_conn_terr_traf(struct gsm_bts_trx_ts *ts, - uint8_t e1_port, uint8_t e1_timeslot, - uint8_t e1_subslot) -{ - struct gsm_bts *bts = ts->trx->bts; - struct abis_om_hdr *oh; - struct abis_nm_channel *ch; - struct msgb *msg = nm_msgb_alloc(); - - oh = (struct abis_om_hdr *) msgb_put(msg, ABIS_OM_FOM_HDR_SIZE); - fill_om_fom_hdr(oh, sizeof(*ch), NM_MT_CONN_TERR_TRAF, - NM_OC_CHANNEL, bts->bts_nr, ts->trx->nr, ts->nr); - - ch = (struct abis_nm_channel *) msgb_put(msg, sizeof(*ch)); - fill_nm_channel(ch, e1_port, e1_timeslot, e1_subslot); - - DEBUGP(DNM, "CONNECT TERR TRAF Um=%s E1=(%u,%u,%u)\n", - gsm_ts_name(ts), - e1_port, e1_timeslot, e1_subslot); - - return abis_nm_sendmsg(bts, msg); -} - -#if 0 -int abis_nm_disc_terr_traf(struct abis_nm_h *h, struct abis_om_obj_inst *inst, - struct abis_nm_abis_channel *chan, - uint8_t subchan) -{ -} -#endif - -/* 3GPP TS 52.021 § 8.11.1 */ -int abis_nm_get_attr(struct gsm_bts *bts, uint8_t obj_class, uint8_t bts_nr, uint8_t trx_nr, uint8_t ts_nr, - const uint8_t *attr, uint8_t attr_len) -{ - struct abis_om_hdr *oh; - struct msgb *msg = nm_msgb_alloc(); - - DEBUGP(DNM, "Get Attr (bts=%d)\n", bts->nr); - - oh = (struct abis_om_hdr *) msgb_put(msg, ABIS_OM_FOM_HDR_SIZE); - fill_om_fom_hdr(oh, attr_len, NM_MT_GET_ATTR, obj_class, - bts_nr, trx_nr, ts_nr); - msgb_tl16v_put(msg, NM_ATT_LIST_REQ_ATTR, attr_len, attr); - - return abis_nm_sendmsg(bts, msg); -} - -/* Chapter 8.6.1 */ -int abis_nm_set_bts_attr(struct gsm_bts *bts, uint8_t *attr, int attr_len) -{ - struct abis_om_hdr *oh; - struct msgb *msg = nm_msgb_alloc(); - uint8_t *cur; - - DEBUGP(DNM, "Set BTS Attr (bts=%d)\n", bts->nr); - - oh = (struct abis_om_hdr *) msgb_put(msg, ABIS_OM_FOM_HDR_SIZE); - fill_om_fom_hdr(oh, attr_len, NM_MT_SET_BTS_ATTR, NM_OC_BTS, bts->bts_nr, 0xff, 0xff); - cur = msgb_put(msg, attr_len); - memcpy(cur, attr, attr_len); - - return abis_nm_sendmsg(bts, msg); -} - -/* Chapter 8.6.2 */ -int abis_nm_set_radio_attr(struct gsm_bts_trx *trx, uint8_t *attr, int attr_len) -{ - struct abis_om_hdr *oh; - struct msgb *msg = nm_msgb_alloc(); - uint8_t *cur; - - DEBUGP(DNM, "Set TRX Attr (bts=%d,trx=%d)\n", trx->bts->nr, trx->nr); - - oh = (struct abis_om_hdr *) msgb_put(msg, ABIS_OM_FOM_HDR_SIZE); - fill_om_fom_hdr(oh, attr_len, NM_MT_SET_RADIO_ATTR, NM_OC_RADIO_CARRIER, - trx->bts->bts_nr, trx->nr, 0xff); - cur = msgb_put(msg, attr_len); - memcpy(cur, attr, attr_len); - - return abis_nm_sendmsg(trx->bts, msg); -} - -int abis_nm_update_max_power_red(struct gsm_bts_trx *trx) -{ - uint8_t attr[] = { NM_ATT_RF_MAXPOWR_R, trx->max_power_red / 2 }; - return abis_nm_set_radio_attr(trx, attr, ARRAY_SIZE(attr)); -} - -static int verify_chan_comb(struct gsm_bts_trx_ts *ts, uint8_t chan_comb, - const char **reason) -{ - int i; - - *reason = "Reason unknown"; - - /* As it turns out, the BS-11 has some very peculiar restrictions - * on the channel combinations it allows */ - switch (ts->trx->bts->type) { - case GSM_BTS_TYPE_BS11: - switch (chan_comb) { - case NM_CHANC_TCHHalf: - case NM_CHANC_TCHHalf2: - case NM_CHANC_OSMO_TCHFull_TCHHalf_PDCH: - /* not supported */ - *reason = "TCH/H is not supported."; - return -EINVAL; - case NM_CHANC_SDCCH: - /* only one SDCCH/8 per TRX */ - for (i = 0; i < TRX_NR_TS; i++) { - if (i == ts->nr) - continue; - if (ts->trx->ts[i].nm_chan_comb == - NM_CHANC_SDCCH) { - *reason = "Only one SDCCH/8 per TRX allowed."; - return -EINVAL; - } - } - /* not allowed for TS0 of BCCH-TRX */ - if (ts->trx == ts->trx->bts->c0 && - ts->nr == 0) { - *reason = "SDCCH/8 must be on TS0."; - return -EINVAL; - } - - /* not on the same TRX that has a BCCH+SDCCH4 - * combination */ - if (ts->trx != ts->trx->bts->c0 && - (ts->trx->ts[0].nm_chan_comb == 5 || - ts->trx->ts[0].nm_chan_comb == 8)) { - *reason = "SDCCH/8 and BCCH must be on the same TRX."; - return -EINVAL; - } - break; - case NM_CHANC_mainBCCH: - case NM_CHANC_BCCHComb: - /* allowed only for TS0 of C0 */ - if (ts->trx != ts->trx->bts->c0 || ts->nr != 0) { - *reason = "Main BCCH must be on TS0."; - return -EINVAL; - } - break; - case NM_CHANC_BCCH: - /* allowed only for TS 2/4/6 of C0 */ - if (ts->trx != ts->trx->bts->c0) { - *reason = "BCCH must be on C0."; - return -EINVAL; - } - if (ts->nr != 2 && ts->nr != 4 && ts->nr != 6) { - *reason = "BCCH must be on TS 2/4/6."; - return -EINVAL; - } - break; - case 8: /* this is not like 08.58, but in fact - * FCCH+SCH+BCCH+CCCH+SDCCH/4+SACCH/C4+CBCH */ - /* FIXME: only one CBCH allowed per cell */ - break; - } - break; - case GSM_BTS_TYPE_NANOBTS: - switch (ts->nr) { - case 0: - if (ts->trx->nr == 0) { - /* only on TRX0 */ - switch (chan_comb) { - case NM_CHANC_BCCH: - case NM_CHANC_mainBCCH: - case NM_CHANC_BCCHComb: - return 0; - break; - default: - *reason = "TS0 of TRX0 must carry a BCCH."; - return -EINVAL; - } - } else { - switch (chan_comb) { - case NM_CHANC_TCHFull: - case NM_CHANC_TCHHalf: - case NM_CHANC_IPAC_TCHFull_TCHHalf: - return 0; - default: - *reason = "TS0 must carry a TCH/F or TCH/H."; - return -EINVAL; - } - } - break; - case 1: - if (ts->trx->nr == 0) { - switch (chan_comb) { - case NM_CHANC_SDCCH_CBCH: - if (ts->trx->ts[0].nm_chan_comb == - NM_CHANC_mainBCCH) - return 0; - *reason = "TS0 must be the main BCCH for CBCH."; - return -EINVAL; - case NM_CHANC_SDCCH: - case NM_CHANC_TCHFull: - case NM_CHANC_TCHHalf: - case NM_CHANC_IPAC_TCHFull_TCHHalf: - case NM_CHANC_IPAC_TCHFull_PDCH: - case NM_CHANC_OSMO_TCHFull_TCHHalf_PDCH: - return 0; - default: - *reason = "TS1 must carry a CBCH, SDCCH or TCH."; - return -EINVAL; - } - } else { - switch (chan_comb) { - case NM_CHANC_SDCCH: - case NM_CHANC_TCHFull: - case NM_CHANC_TCHHalf: - case NM_CHANC_IPAC_TCHFull_TCHHalf: - return 0; - default: - *reason = "TS1 must carry a SDCCH or TCH."; - return -EINVAL; - } - } - break; - case 2: - case 3: - case 4: - case 5: - case 6: - case 7: - switch (chan_comb) { - case NM_CHANC_TCHFull: - case NM_CHANC_TCHHalf: - case NM_CHANC_IPAC_TCHFull_TCHHalf: - return 0; - case NM_CHANC_IPAC_PDCH: - case NM_CHANC_IPAC_TCHFull_PDCH: - case NM_CHANC_OSMO_TCHFull_TCHHalf_PDCH: - if (ts->trx->nr == 0) - return 0; - else { - *reason = "PDCH must be on TRX0."; - return -EINVAL; - } - } - break; - } - *reason = "Unknown combination"; - return -EINVAL; - case GSM_BTS_TYPE_OSMOBTS: - /* no known restrictions */ - return 0; - default: - /* unknown BTS type */ - return 0; - } - return 0; -} - -/* Chapter 8.6.3 */ -int abis_nm_set_channel_attr(struct gsm_bts_trx_ts *ts, uint8_t chan_comb) -{ - struct gsm_bts *bts = ts->trx->bts; - struct abis_om_hdr *oh; - uint8_t zero = 0x00; - struct msgb *msg = nm_msgb_alloc(); - uint8_t len = 2 + 2; - const char *reason = NULL; - - if (bts->type == GSM_BTS_TYPE_BS11) - len += 4 + 2 + 2 + 3; - - DEBUGP(DNM, "Set Chan Attr %s\n", gsm_ts_name(ts)); - if (verify_chan_comb(ts, chan_comb, &reason) < 0) { - msgb_free(msg); - LOGP(DNM, LOGL_ERROR, - "Invalid Channel Combination %d on %s. Reason: %s\n", - chan_comb, gsm_ts_name(ts), reason); - return -EINVAL; - } - ts->nm_chan_comb = chan_comb; - - oh = (struct abis_om_hdr *) msgb_put(msg, ABIS_OM_FOM_HDR_SIZE); - fill_om_fom_hdr(oh, len, NM_MT_SET_CHAN_ATTR, - NM_OC_CHANNEL, bts->bts_nr, - ts->trx->nr, ts->nr); - msgb_tv_put(msg, NM_ATT_CHAN_COMB, chan_comb); - if (ts->hopping.enabled) { - unsigned int i; - uint8_t *len; - - msgb_tv_put(msg, NM_ATT_HSN, ts->hopping.hsn); - msgb_tv_put(msg, NM_ATT_MAIO, ts->hopping.maio); - - /* build the ARFCN list */ - msgb_put_u8(msg, NM_ATT_ARFCN_LIST); - len = msgb_put(msg, 1); - *len = 0; - for (i = 0; i < ts->hopping.arfcns.data_len*8; i++) { - if (bitvec_get_bit_pos(&ts->hopping.arfcns, i)) { - msgb_put_u16(msg, i); - /* At least BS-11 wants a TLV16 here */ - if (bts->type == GSM_BTS_TYPE_BS11) - *len += 1; - else - *len += sizeof(uint16_t); - } - } - } - msgb_tv_put(msg, NM_ATT_TSC, gsm_ts_tsc(ts)); /* training sequence */ - if (bts->type == GSM_BTS_TYPE_BS11) - msgb_tlv_put(msg, 0x59, 1, &zero); - - return abis_nm_sendmsg(bts, msg); -} - -int abis_nm_sw_act_req_ack(struct gsm_bts *bts, uint8_t obj_class, uint8_t i1, - uint8_t i2, uint8_t i3, int nack, uint8_t *attr, int att_len) -{ - struct abis_om_hdr *oh; - struct msgb *msg = nm_msgb_alloc(); - uint8_t msgtype = NM_MT_SW_ACT_REQ_ACK; - uint8_t len = att_len; - - if (nack) { - len += 2; - msgtype = NM_MT_SW_ACT_REQ_NACK; - } - - oh = (struct abis_om_hdr *) msgb_put(msg, ABIS_OM_FOM_HDR_SIZE); - fill_om_fom_hdr(oh, att_len, msgtype, obj_class, i1, i2, i3); - - if (attr) { - uint8_t *ptr = msgb_put(msg, att_len); - memcpy(ptr, attr, att_len); - } - if (nack) - msgb_tv_put(msg, NM_ATT_NACK_CAUSES, NM_NACK_OBJCLASS_NOTSUPP); - - return abis_nm_sendmsg_direct(bts, msg); -} - -int abis_nm_raw_msg(struct gsm_bts *bts, int len, uint8_t *rawmsg) -{ - struct msgb *msg = nm_msgb_alloc(); - struct abis_om_hdr *oh; - uint8_t *data; - - oh = (struct abis_om_hdr *) msgb_put(msg, sizeof(*oh)); - fill_om_hdr(oh, len); - data = msgb_put(msg, len); - memcpy(data, rawmsg, len); - - return abis_nm_sendmsg(bts, msg); -} - -/* Siemens specific commands */ -static int __simple_cmd(struct gsm_bts *bts, uint8_t msg_type) -{ - struct abis_om_hdr *oh; - struct msgb *msg = nm_msgb_alloc(); - - oh = (struct abis_om_hdr *) msgb_put(msg, ABIS_OM_FOM_HDR_SIZE); - fill_om_fom_hdr(oh, 0, msg_type, NM_OC_SITE_MANAGER, - 0xff, 0xff, 0xff); - - return abis_nm_sendmsg(bts, msg); -} - -/* Chapter 8.9.2 */ -int abis_nm_opstart(struct gsm_bts *bts, uint8_t obj_class, uint8_t i0, uint8_t i1, uint8_t i2) -{ - struct abis_om_hdr *oh; - struct abis_om_fom_hdr *foh; - struct msgb *msg = nm_msgb_alloc(); - - oh = (struct abis_om_hdr *) msgb_put(msg, ABIS_OM_FOM_HDR_SIZE); - foh = fill_om_fom_hdr(oh, 0, NM_MT_OPSTART, obj_class, i0, i1, i2); - - abis_nm_debugp_foh(DNM, foh); - DEBUGPC(DNM, "Sending OPSTART\n"); - - return abis_nm_sendmsg(bts, msg); -} - -/* Chapter 8.8.5 */ -int abis_nm_chg_adm_state(struct gsm_bts *bts, uint8_t obj_class, uint8_t i0, - uint8_t i1, uint8_t i2, enum abis_nm_adm_state adm_state) -{ - struct abis_om_hdr *oh; - struct msgb *msg = nm_msgb_alloc(); - - oh = (struct abis_om_hdr *) msgb_put(msg, ABIS_OM_FOM_HDR_SIZE); - fill_om_fom_hdr(oh, 2, NM_MT_CHG_ADM_STATE, obj_class, i0, i1, i2); - msgb_tv_put(msg, NM_ATT_ADM_STATE, adm_state); - - return abis_nm_sendmsg(bts, msg); -} - -int abis_nm_conn_mdrop_link(struct gsm_bts *bts, uint8_t e1_port0, uint8_t ts0, - uint8_t e1_port1, uint8_t ts1) -{ - struct abis_om_hdr *oh; - struct msgb *msg = nm_msgb_alloc(); - uint8_t *attr; - - DEBUGP(DNM, "CONNECT MDROP LINK E1=(%u,%u) -> E1=(%u, %u)\n", - e1_port0, ts0, e1_port1, ts1); - - oh = (struct abis_om_hdr *) msgb_put(msg, ABIS_OM_FOM_HDR_SIZE); - fill_om_fom_hdr(oh, 6, NM_MT_CONN_MDROP_LINK, - NM_OC_SITE_MANAGER, 0x00, 0x00, 0x00); - - attr = msgb_put(msg, 3); - attr[0] = NM_ATT_MDROP_LINK; - attr[1] = e1_port0; - attr[2] = ts0; - - attr = msgb_put(msg, 3); - attr[0] = NM_ATT_MDROP_NEXT; - attr[1] = e1_port1; - attr[2] = ts1; - - return abis_nm_sendmsg(bts, msg); -} - -/* Chapter 8.7.1 */ -int abis_nm_perform_test(struct gsm_bts *bts, uint8_t obj_class, - uint8_t bts_nr, uint8_t trx_nr, uint8_t ts_nr, - uint8_t test_nr, uint8_t auton_report, struct msgb *msg) -{ - struct abis_om_hdr *oh; - - DEBUGP(DNM, "PEFORM TEST %s\n", abis_nm_test_name(test_nr)); - - if (!msg) - msg = nm_msgb_alloc(); - - msgb_tv_push(msg, NM_ATT_AUTON_REPORT, auton_report); - msgb_tv_push(msg, NM_ATT_TEST_NO, test_nr); - oh = (struct abis_om_hdr *) msgb_push(msg, ABIS_OM_FOM_HDR_SIZE); - fill_om_fom_hdr(oh, msgb_l3len(msg), NM_MT_PERF_TEST, - obj_class, bts_nr, trx_nr, ts_nr); - - return abis_nm_sendmsg(bts, msg); -} - -int abis_nm_event_reports(struct gsm_bts *bts, int on) -{ - if (on == 0) - return __simple_cmd(bts, NM_MT_STOP_EVENT_REP); - else - return __simple_cmd(bts, NM_MT_REST_EVENT_REP); -} - -/* Siemens (or BS-11) specific commands */ - -int abis_nm_bs11_bsc_disconnect(struct gsm_bts *bts, int reconnect) -{ - if (reconnect == 0) - return __simple_cmd(bts, NM_MT_BS11_DISCONNECT); - else - return __simple_cmd(bts, NM_MT_BS11_RECONNECT); -} - -int abis_nm_bs11_restart(struct gsm_bts *bts) -{ - return __simple_cmd(bts, NM_MT_BS11_RESTART); -} - - -struct bs11_date_time { - uint16_t year; - uint8_t month; - uint8_t day; - uint8_t hour; - uint8_t min; - uint8_t sec; -} __attribute__((packed)); - - -void get_bs11_date_time(struct bs11_date_time *aet) -{ - time_t t; - struct tm *tm; - - t = time(NULL); - tm = localtime(&t); - aet->sec = tm->tm_sec; - aet->min = tm->tm_min; - aet->hour = tm->tm_hour; - aet->day = tm->tm_mday; - aet->month = tm->tm_mon; - aet->year = htons(1900 + tm->tm_year); -} - -int abis_nm_bs11_reset_resource(struct gsm_bts *bts) -{ - return __simple_cmd(bts, NM_MT_BS11_RESET_RESOURCE); -} - -int abis_nm_bs11_db_transmission(struct gsm_bts *bts, int begin) -{ - if (begin) - return __simple_cmd(bts, NM_MT_BS11_BEGIN_DB_TX); - else - return __simple_cmd(bts, NM_MT_BS11_END_DB_TX); -} - -int abis_nm_bs11_create_object(struct gsm_bts *bts, - enum abis_bs11_objtype type, uint8_t idx, - uint8_t attr_len, const uint8_t *attr) -{ - struct abis_om_hdr *oh; - struct msgb *msg = nm_msgb_alloc(); - uint8_t *cur; - - oh = (struct abis_om_hdr *) msgb_put(msg, ABIS_OM_FOM_HDR_SIZE); - fill_om_fom_hdr(oh, attr_len, NM_MT_BS11_CREATE_OBJ, - NM_OC_BS11, type, 0, idx); - cur = msgb_put(msg, attr_len); - memcpy(cur, attr, attr_len); - - return abis_nm_sendmsg(bts, msg); -} - -int abis_nm_bs11_delete_object(struct gsm_bts *bts, - enum abis_bs11_objtype type, uint8_t idx) -{ - struct abis_om_hdr *oh; - struct msgb *msg = nm_msgb_alloc(); - - oh = (struct abis_om_hdr *) msgb_put(msg, ABIS_OM_FOM_HDR_SIZE); - fill_om_fom_hdr(oh, 0, NM_MT_BS11_DELETE_OBJ, - NM_OC_BS11, type, 0, idx); - - return abis_nm_sendmsg(bts, msg); -} - -int abis_nm_bs11_create_envaBTSE(struct gsm_bts *bts, uint8_t idx) -{ - struct abis_om_hdr *oh; - struct msgb *msg = nm_msgb_alloc(); - uint8_t zero = 0x00; - - oh = (struct abis_om_hdr *) msgb_put(msg, ABIS_OM_FOM_HDR_SIZE); - fill_om_fom_hdr(oh, 3, NM_MT_BS11_CREATE_OBJ, - NM_OC_BS11_ENVABTSE, 0, idx, 0xff); - msgb_tlv_put(msg, 0x99, 1, &zero); - - return abis_nm_sendmsg(bts, msg); -} - -int abis_nm_bs11_create_bport(struct gsm_bts *bts, uint8_t idx) -{ - struct abis_om_hdr *oh; - struct msgb *msg = nm_msgb_alloc(); - - oh = (struct abis_om_hdr *) msgb_put(msg, ABIS_OM_FOM_HDR_SIZE); - fill_om_fom_hdr(oh, 0, NM_MT_BS11_CREATE_OBJ, NM_OC_BS11_BPORT, - idx, 0xff, 0xff); - - return abis_nm_sendmsg(bts, msg); -} - -int abis_nm_bs11_delete_bport(struct gsm_bts *bts, uint8_t idx) -{ - struct abis_om_hdr *oh; - struct msgb *msg = nm_msgb_alloc(); - - oh = (struct abis_om_hdr *) msgb_put(msg, ABIS_OM_FOM_HDR_SIZE); - fill_om_fom_hdr(oh, 0, NM_MT_BS11_DELETE_OBJ, NM_OC_BS11_BPORT, - idx, 0xff, 0xff); - - return abis_nm_sendmsg(bts, msg); -} - -static const uint8_t sm_attr[] = { NM_ATT_TEI, NM_ATT_ABIS_CHANNEL }; -int abis_nm_bs11_get_oml_tei_ts(struct gsm_bts *bts) -{ - struct abis_om_hdr *oh; - struct msgb *msg = nm_msgb_alloc(); - - oh = (struct abis_om_hdr *) msgb_put(msg, ABIS_OM_FOM_HDR_SIZE); - fill_om_fom_hdr(oh, 2+sizeof(sm_attr), NM_MT_GET_ATTR, NM_OC_SITE_MANAGER, - 0xff, 0xff, 0xff); - msgb_tlv_put(msg, NM_ATT_LIST_REQ_ATTR, sizeof(sm_attr), sm_attr); - - return abis_nm_sendmsg(bts, msg); -} - -/* like abis_nm_conn_terr_traf + set_tei */ -int abis_nm_bs11_conn_oml_tei(struct gsm_bts *bts, uint8_t e1_port, - uint8_t e1_timeslot, uint8_t e1_subslot, - uint8_t tei) -{ - struct abis_om_hdr *oh; - struct abis_nm_channel *ch; - struct msgb *msg = nm_msgb_alloc(); - - oh = (struct abis_om_hdr *) msgb_put(msg, ABIS_OM_FOM_HDR_SIZE); - fill_om_fom_hdr(oh, sizeof(*ch)+2, NM_MT_BS11_SET_ATTR, - NM_OC_SITE_MANAGER, 0xff, 0xff, 0xff); - - ch = (struct abis_nm_channel *) msgb_put(msg, sizeof(*ch)); - fill_nm_channel(ch, e1_port, e1_timeslot, e1_subslot); - msgb_tv_put(msg, NM_ATT_TEI, tei); - - return abis_nm_sendmsg(bts, msg); -} - -int abis_nm_bs11_set_trx_power(struct gsm_bts_trx *trx, uint8_t level) -{ - struct abis_om_hdr *oh; - struct msgb *msg = nm_msgb_alloc(); - - oh = (struct abis_om_hdr *) msgb_put(msg, ABIS_OM_FOM_HDR_SIZE); - fill_om_fom_hdr(oh, 3, NM_MT_BS11_SET_ATTR, - NM_OC_BS11, BS11_OBJ_PA, 0x00, trx->nr); - msgb_tlv_put(msg, NM_ATT_BS11_TXPWR, 1, &level); - - return abis_nm_sendmsg(trx->bts, msg); -} - -int abis_nm_bs11_get_trx_power(struct gsm_bts_trx *trx) -{ - struct abis_om_hdr *oh; - struct msgb *msg = nm_msgb_alloc(); - uint8_t attr = NM_ATT_BS11_TXPWR; - - oh = (struct abis_om_hdr *) msgb_put(msg, ABIS_OM_FOM_HDR_SIZE); - fill_om_fom_hdr(oh, 2+sizeof(attr), NM_MT_GET_ATTR, - NM_OC_BS11, BS11_OBJ_PA, 0x00, trx->nr); - msgb_tlv_put(msg, NM_ATT_LIST_REQ_ATTR, sizeof(attr), &attr); - - return abis_nm_sendmsg(trx->bts, msg); -} - -int abis_nm_bs11_get_pll_mode(struct gsm_bts *bts) -{ - struct abis_om_hdr *oh; - struct msgb *msg = nm_msgb_alloc(); - uint8_t attr[] = { NM_ATT_BS11_PLL_MODE }; - - oh = (struct abis_om_hdr *) msgb_put(msg, ABIS_OM_FOM_HDR_SIZE); - fill_om_fom_hdr(oh, 2+sizeof(attr), NM_MT_GET_ATTR, - NM_OC_BS11, BS11_OBJ_LI, 0x00, 0x00); - msgb_tlv_put(msg, NM_ATT_LIST_REQ_ATTR, sizeof(attr), attr); - - return abis_nm_sendmsg(bts, msg); -} - -int abis_nm_bs11_get_cclk(struct gsm_bts *bts) -{ - struct abis_om_hdr *oh; - struct msgb *msg = nm_msgb_alloc(); - uint8_t attr[] = { NM_ATT_BS11_CCLK_ACCURACY, - NM_ATT_BS11_CCLK_TYPE }; - - oh = (struct abis_om_hdr *) msgb_put(msg, ABIS_OM_FOM_HDR_SIZE); - fill_om_fom_hdr(oh, 2+sizeof(attr), NM_MT_GET_ATTR, - NM_OC_BS11, BS11_OBJ_CCLK, 0x00, 0x00); - msgb_tlv_put(msg, NM_ATT_LIST_REQ_ATTR, sizeof(attr), attr); - - return abis_nm_sendmsg(bts, msg); - -} - -//static const uint8_t bs11_logon_c7[] = { 0x07, 0xd9, 0x01, 0x11, 0x0d, 0x10, 0x20 }; - -int abis_nm_bs11_factory_logon(struct gsm_bts *bts, int on) -{ - return abis_nm_bs11_logon(bts, 0x02, "FACTORY", on); -} - -int abis_nm_bs11_infield_logon(struct gsm_bts *bts, int on) -{ - return abis_nm_bs11_logon(bts, 0x03, "FIELD ", on); -} - -int abis_nm_bs11_logon(struct gsm_bts *bts, uint8_t level, const char *name, int on) -{ - struct abis_om_hdr *oh; - struct msgb *msg = nm_msgb_alloc(); - struct bs11_date_time bdt; - - get_bs11_date_time(&bdt); - - oh = (struct abis_om_hdr *) msgb_put(msg, ABIS_OM_FOM_HDR_SIZE); - if (on) { - uint8_t len = 3*2 + sizeof(bdt) - + 1 + strlen(name); - fill_om_fom_hdr(oh, len, NM_MT_BS11_LMT_LOGON, - NM_OC_BS11_BTSE, 0xff, 0xff, 0xff); - msgb_tlv_put(msg, NM_ATT_BS11_LMT_LOGIN_TIME, - sizeof(bdt), (uint8_t *) &bdt); - msgb_tlv_put(msg, NM_ATT_BS11_LMT_USER_ACC_LEV, - 1, &level); - msgb_tlv_put(msg, NM_ATT_BS11_LMT_USER_NAME, - strlen(name), (uint8_t *)name); - } else { - fill_om_fom_hdr(oh, 0, NM_MT_BS11_LMT_LOGOFF, - NM_OC_BS11_BTSE, 0xff, 0xff, 0xff); - } - - return abis_nm_sendmsg(bts, msg); -} - -int abis_nm_bs11_set_trx1_pw(struct gsm_bts *bts, const char *password) -{ - struct abis_om_hdr *oh; - struct msgb *msg; - - if (strlen(password) != 10) - return -EINVAL; - - msg = nm_msgb_alloc(); - oh = (struct abis_om_hdr *) msgb_put(msg, ABIS_OM_FOM_HDR_SIZE); - fill_om_fom_hdr(oh, 2+strlen(password), NM_MT_BS11_SET_ATTR, - NM_OC_BS11, BS11_OBJ_TRX1, 0x00, 0x00); - msgb_tlv_put(msg, NM_ATT_BS11_PASSWORD, 10, (const uint8_t *)password); - - return abis_nm_sendmsg(bts, msg); -} - -/* change the BS-11 PLL Mode to either locked (E1 derived) or standalone */ -int abis_nm_bs11_set_pll_locked(struct gsm_bts *bts, int locked) -{ - struct abis_om_hdr *oh; - struct msgb *msg; - uint8_t tlv_value; - - msg = nm_msgb_alloc(); - oh = (struct abis_om_hdr *) msgb_put(msg, ABIS_OM_FOM_HDR_SIZE); - fill_om_fom_hdr(oh, 3, NM_MT_BS11_SET_ATTR, NM_OC_BS11, - BS11_OBJ_LI, 0x00, 0x00); - - if (locked) - tlv_value = BS11_LI_PLL_LOCKED; - else - tlv_value = BS11_LI_PLL_STANDALONE; - - msgb_tlv_put(msg, NM_ATT_BS11_PLL_MODE, 1, &tlv_value); - - return abis_nm_sendmsg(bts, msg); -} - -/* Set the calibration value of the PLL (work value/set value) - * It depends on the login which one is changed */ -int abis_nm_bs11_set_pll(struct gsm_bts *bts, int value) -{ - struct abis_om_hdr *oh; - struct msgb *msg; - uint8_t tlv_value[2]; - - msg = nm_msgb_alloc(); - oh = (struct abis_om_hdr *) msgb_put(msg, ABIS_OM_FOM_HDR_SIZE); - fill_om_fom_hdr(oh, 3, NM_MT_BS11_SET_ATTR, NM_OC_BS11, - BS11_OBJ_TRX1, 0x00, 0x00); - - tlv_value[0] = value>>8; - tlv_value[1] = value&0xff; - - msgb_tlv_put(msg, NM_ATT_BS11_PLL, 2, tlv_value); - - return abis_nm_sendmsg(bts, msg); -} - -int abis_nm_bs11_get_state(struct gsm_bts *bts) -{ - return __simple_cmd(bts, NM_MT_BS11_GET_STATE); -} - -/* BS11 SWL */ - -void *tall_fle_ctx; - -struct abis_nm_bs11_sw { - struct gsm_bts *bts; - char swl_fname[PATH_MAX]; - uint8_t win_size; - int forced; - struct llist_head file_list; - gsm_cbfn *user_cb; /* specified by the user */ -}; -static struct abis_nm_bs11_sw _g_bs11_sw, *g_bs11_sw = &_g_bs11_sw; - -struct file_list_entry { - struct llist_head list; - char fname[PATH_MAX]; -}; - -struct file_list_entry *fl_dequeue(struct llist_head *queue) -{ - struct llist_head *lh; - - if (llist_empty(queue)) - return NULL; - - lh = queue->next; - llist_del(lh); - - return llist_entry(lh, struct file_list_entry, list); -} - -static int bs11_read_swl_file(struct abis_nm_bs11_sw *bs11_sw) -{ - char linebuf[255]; - struct llist_head *lh, *lh2; - FILE *swl; - int rc = 0; - - swl = fopen(bs11_sw->swl_fname, "r"); - if (!swl) - return -ENODEV; - - /* zero the stale file list, if any */ - llist_for_each_safe(lh, lh2, &bs11_sw->file_list) { - llist_del(lh); - talloc_free(lh); - } - - while (fgets(linebuf, sizeof(linebuf), swl)) { - char file_id[12+1]; - char file_version[80+1]; - struct file_list_entry *fle; - static char dir[PATH_MAX]; - - if (strlen(linebuf) < 4) - continue; - - rc = sscanf(linebuf+4, "%12s:%80s\r\n", file_id, file_version); - if (rc < 0) { - perror("ERR parsing SWL file"); - rc = -EINVAL; - goto out; - } - if (rc < 2) - continue; - - fle = talloc_zero(tall_fle_ctx, struct file_list_entry); - if (!fle) { - rc = -ENOMEM; - goto out; - } - - /* construct new filename */ - osmo_strlcpy(dir, bs11_sw->swl_fname, sizeof(dir)); - strncat(fle->fname, dirname(dir), sizeof(fle->fname) - 1); - strcat(fle->fname, "/"); - strncat(fle->fname, file_id, sizeof(fle->fname) - 1 -strlen(fle->fname)); - - llist_add_tail(&fle->list, &bs11_sw->file_list); - } - -out: - fclose(swl); - return rc; -} - -/* bs11 swload specific callback, passed to abis_nm core swload */ -static int bs11_swload_cbfn(unsigned int hook, unsigned int event, - struct msgb *msg, void *data, void *param) -{ - struct abis_nm_bs11_sw *bs11_sw = data; - struct file_list_entry *fle; - int rc = 0; - - switch (event) { - case NM_MT_LOAD_END_ACK: - fle = fl_dequeue(&bs11_sw->file_list); - if (fle) { - /* start download the next file of our file list */ - rc = abis_nm_software_load(bs11_sw->bts, 0xff, fle->fname, - bs11_sw->win_size, - bs11_sw->forced, - &bs11_swload_cbfn, bs11_sw); - talloc_free(fle); - } else { - /* activate the SWL */ - rc = abis_nm_software_activate(bs11_sw->bts, - bs11_sw->swl_fname, - bs11_swload_cbfn, - bs11_sw); - } - break; - case NM_MT_LOAD_SEG_ACK: - case NM_MT_LOAD_END_NACK: - case NM_MT_LOAD_INIT_ACK: - case NM_MT_LOAD_INIT_NACK: - case NM_MT_ACTIVATE_SW_NACK: - case NM_MT_ACTIVATE_SW_ACK: - default: - /* fallthrough to the user callback */ - if (bs11_sw->user_cb) - rc = bs11_sw->user_cb(hook, event, msg, NULL, NULL); - break; - } - - return rc; -} - -/* Siemens provides a SWL file that is a mere listing of all the other - * files that are part of a software release. We need to upload first - * the list file, and then each file that is listed in the list file */ -int abis_nm_bs11_load_swl(struct gsm_bts *bts, const char *fname, - uint8_t win_size, int forced, gsm_cbfn *cbfn) -{ - struct abis_nm_bs11_sw *bs11_sw = g_bs11_sw; - struct file_list_entry *fle; - int rc = 0; - - INIT_LLIST_HEAD(&bs11_sw->file_list); - bs11_sw->bts = bts; - bs11_sw->win_size = win_size; - bs11_sw->user_cb = cbfn; - bs11_sw->forced = forced; - - osmo_strlcpy(bs11_sw->swl_fname, fname, sizeof(bs11_sw->swl_fname)); - rc = bs11_read_swl_file(bs11_sw); - if (rc < 0) - return rc; - - /* dequeue next item in file list */ - fle = fl_dequeue(&bs11_sw->file_list); - if (!fle) - return -EINVAL; - - /* start download the next file of our file list */ - rc = abis_nm_software_load(bts, 0xff, fle->fname, win_size, forced, - bs11_swload_cbfn, bs11_sw); - talloc_free(fle); - return rc; -} - -#if 0 -static uint8_t req_attr_btse[] = { - NM_ATT_ADM_STATE, NM_ATT_BS11_LMT_LOGON_SESSION, - NM_ATT_BS11_LMT_LOGIN_TIME, NM_ATT_BS11_LMT_USER_ACC_LEV, - NM_ATT_BS11_LMT_USER_NAME, - - 0xaf, NM_ATT_BS11_RX_OFFSET, NM_ATT_BS11_VENDOR_NAME, - - NM_ATT_BS11_SW_LOAD_INTENDED, NM_ATT_BS11_SW_LOAD_SAFETY, - - NM_ATT_BS11_SW_LOAD_STORED }; - -static uint8_t req_attr_btsm[] = { - NM_ATT_ABIS_CHANNEL, NM_ATT_TEI, NM_ATT_BS11_ABIS_EXT_TIME, - NM_ATT_ADM_STATE, NM_ATT_AVAIL_STATUS, 0xce, NM_ATT_FILE_ID, - NM_ATT_FILE_VERSION, NM_ATT_OPER_STATE, 0xe8, NM_ATT_BS11_ALL_TEST_CATG, - NM_ATT_SW_DESCR, NM_ATT_GET_ARI }; -#endif - -static uint8_t req_attr[] = { - NM_ATT_ADM_STATE, NM_ATT_AVAIL_STATUS, 0xa8, NM_ATT_OPER_STATE, - 0xd5, 0xa1, NM_ATT_BS11_ESN_FW_CODE_NO, NM_ATT_BS11_ESN_HW_CODE_NO, - 0x42, NM_ATT_BS11_ESN_PCB_SERIAL, NM_ATT_BS11_PLL }; - -int abis_nm_bs11_get_serno(struct gsm_bts *bts) -{ - struct abis_om_hdr *oh; - struct msgb *msg = nm_msgb_alloc(); - - oh = (struct abis_om_hdr *) msgb_put(msg, ABIS_OM_FOM_HDR_SIZE); - /* SiemensHW CCTRL object */ - fill_om_fom_hdr(oh, 2+sizeof(req_attr), NM_MT_GET_ATTR, NM_OC_BS11, - 0x03, 0x00, 0x00); - msgb_tlv_put(msg, NM_ATT_LIST_REQ_ATTR, sizeof(req_attr), req_attr); - - return abis_nm_sendmsg(bts, msg); -} - -int abis_nm_bs11_set_ext_time(struct gsm_bts *bts) -{ - struct abis_om_hdr *oh; - struct msgb *msg = nm_msgb_alloc(); - struct bs11_date_time aet; - - get_bs11_date_time(&aet); - oh = (struct abis_om_hdr *) msgb_put(msg, ABIS_OM_FOM_HDR_SIZE); - /* SiemensHW CCTRL object */ - fill_om_fom_hdr(oh, 2+sizeof(aet), NM_MT_BS11_SET_ATTR, NM_OC_SITE_MANAGER, - 0xff, 0xff, 0xff); - msgb_tlv_put(msg, NM_ATT_BS11_ABIS_EXT_TIME, sizeof(aet), (uint8_t *) &aet); - - return abis_nm_sendmsg(bts, msg); -} - -int abis_nm_bs11_get_bport_line_cfg(struct gsm_bts *bts, uint8_t bport) -{ - struct abis_om_hdr *oh; - struct msgb *msg = nm_msgb_alloc(); - uint8_t attr = NM_ATT_BS11_LINE_CFG; - - oh = (struct abis_om_hdr *) msgb_put(msg, ABIS_OM_FOM_HDR_SIZE); - fill_om_fom_hdr(oh, 2+sizeof(attr), NM_MT_GET_ATTR, - NM_OC_BS11_BPORT, bport, 0xff, 0x02); - msgb_tlv_put(msg, NM_ATT_LIST_REQ_ATTR, sizeof(attr), &attr); - - return abis_nm_sendmsg(bts, msg); -} - -int abis_nm_bs11_set_bport_line_cfg(struct gsm_bts *bts, uint8_t bport, enum abis_bs11_line_cfg line_cfg) -{ - struct abis_om_hdr *oh; - struct msgb *msg = nm_msgb_alloc(); - struct bs11_date_time aet; - - get_bs11_date_time(&aet); - oh = (struct abis_om_hdr *) msgb_put(msg, ABIS_OM_FOM_HDR_SIZE); - fill_om_fom_hdr(oh, 2, NM_MT_BS11_SET_ATTR, NM_OC_BS11_BPORT, - bport, 0xff, 0x02); - msgb_tv_put(msg, NM_ATT_BS11_LINE_CFG, line_cfg); - - return abis_nm_sendmsg(bts, msg); -} - -/* ip.access nanoBTS specific commands */ -static const char ipaccess_magic[] = "com.ipaccess"; - - -static int abis_nm_rx_ipacc(struct msgb *msg) -{ - struct in_addr addr; - struct abis_om_hdr *oh = msgb_l2(msg); - struct abis_om_fom_hdr *foh; - uint8_t idstrlen = oh->data[0]; - struct tlv_parsed tp; - struct ipacc_ack_signal_data signal; - struct e1inp_sign_link *sign_link = msg->dst; - - if (strncmp((char *)&oh->data[1], ipaccess_magic, idstrlen)) { - LOGP(DNM, LOGL_ERROR, "id string is not com.ipaccess !?!\n"); - return -EINVAL; - } - - foh = (struct abis_om_fom_hdr *) (oh->data + 1 + idstrlen); - abis_nm_tlv_parse(&tp, sign_link->trx->bts, foh->data, oh->length-sizeof(*foh)); - - abis_nm_debugp_foh(DNM, foh); - - DEBUGPC(DNM, "IPACCESS(0x%02x): ", foh->msg_type); - - switch (foh->msg_type) { - case NM_MT_IPACC_RSL_CONNECT_ACK: - DEBUGPC(DNM, "RSL CONNECT ACK "); - if (TLVP_PRESENT(&tp, NM_ATT_IPACC_DST_IP)) { - memcpy(&addr, - TLVP_VAL(&tp, NM_ATT_IPACC_DST_IP), sizeof(addr)); - - DEBUGPC(DNM, "IP=%s ", inet_ntoa(addr)); - } - if (TLVP_PRESENT(&tp, NM_ATT_IPACC_DST_IP_PORT)) - DEBUGPC(DNM, "PORT=%u ", - ntohs(*((uint16_t *) - TLVP_VAL(&tp, NM_ATT_IPACC_DST_IP_PORT)))); - if (TLVP_PRESENT(&tp, NM_ATT_IPACC_STREAM_ID)) - DEBUGPC(DNM, "STREAM=0x%02x ", - *TLVP_VAL(&tp, NM_ATT_IPACC_STREAM_ID)); - DEBUGPC(DNM, "\n"); - break; - case NM_MT_IPACC_RSL_CONNECT_NACK: - LOGP(DNM, LOGL_ERROR, "RSL CONNECT NACK "); - if (TLVP_PRESENT(&tp, NM_ATT_NACK_CAUSES)) - LOGPC(DNM, LOGL_ERROR, " CAUSE=%s\n", - abis_nm_nack_cause_name(*TLVP_VAL(&tp, NM_ATT_NACK_CAUSES))); - else - LOGPC(DNM, LOGL_ERROR, "\n"); - break; - case NM_MT_IPACC_SET_NVATTR_ACK: - DEBUGPC(DNM, "SET NVATTR ACK\n"); - /* FIXME: decode and show the actual attributes */ - break; - case NM_MT_IPACC_SET_NVATTR_NACK: - LOGP(DNM, LOGL_ERROR, "SET NVATTR NACK "); - if (TLVP_PRESENT(&tp, NM_ATT_NACK_CAUSES)) - LOGPC(DNM, LOGL_ERROR, " CAUSE=%s\n", - abis_nm_nack_cause_name(*TLVP_VAL(&tp, NM_ATT_NACK_CAUSES))); - else - LOGPC(DNM, LOGL_ERROR, "\n"); - break; - case NM_MT_IPACC_GET_NVATTR_ACK: - DEBUGPC(DNM, "GET NVATTR ACK\n"); - /* FIXME: decode and show the actual attributes */ - break; - case NM_MT_IPACC_GET_NVATTR_NACK: - LOGPC(DNM, LOGL_ERROR, "GET NVATTR NACK "); - if (TLVP_PRESENT(&tp, NM_ATT_NACK_CAUSES)) - LOGPC(DNM, LOGL_ERROR, " CAUSE=%s\n", - abis_nm_nack_cause_name(*TLVP_VAL(&tp, NM_ATT_NACK_CAUSES))); - else - LOGPC(DNM, LOGL_ERROR, "\n"); - break; - case NM_MT_IPACC_SET_ATTR_ACK: - DEBUGPC(DNM, "SET ATTR ACK\n"); - break; - case NM_MT_IPACC_SET_ATTR_NACK: - LOGPC(DNM, LOGL_ERROR, "SET ATTR NACK "); - if (TLVP_PRESENT(&tp, NM_ATT_NACK_CAUSES)) - LOGPC(DNM, LOGL_ERROR, " CAUSE=%s\n", - abis_nm_nack_cause_name(*TLVP_VAL(&tp, NM_ATT_NACK_CAUSES))); - else - LOGPC(DNM, LOGL_ERROR, "\n"); - break; - default: - DEBUGPC(DNM, "unknown\n"); - break; - } - - /* signal handling */ - switch (foh->msg_type) { - case NM_MT_IPACC_RSL_CONNECT_NACK: - case NM_MT_IPACC_SET_NVATTR_NACK: - case NM_MT_IPACC_GET_NVATTR_NACK: - signal.trx = gsm_bts_trx_by_nr(sign_link->trx->bts, foh->obj_inst.trx_nr); - signal.msg_type = foh->msg_type; - osmo_signal_dispatch(SS_NM, S_NM_IPACC_NACK, &signal); - break; - case NM_MT_IPACC_SET_NVATTR_ACK: - signal.trx = gsm_bts_trx_by_nr(sign_link->trx->bts, foh->obj_inst.trx_nr); - signal.msg_type = foh->msg_type; - osmo_signal_dispatch(SS_NM, S_NM_IPACC_ACK, &signal); - break; - default: - break; - } - - return 0; -} - -/* send an ip-access manufacturer specific message */ -int abis_nm_ipaccess_msg(struct gsm_bts *bts, uint8_t msg_type, - uint8_t obj_class, uint8_t bts_nr, - uint8_t trx_nr, uint8_t ts_nr, - uint8_t *attr, int attr_len) -{ - struct msgb *msg = nm_msgb_alloc(); - struct abis_om_hdr *oh; - struct abis_om_fom_hdr *foh; - uint8_t *data; - - /* construct the 12.21 OM header, observe the erroneous length */ - oh = (struct abis_om_hdr *) msgb_put(msg, sizeof(*oh)); - fill_om_hdr(oh, sizeof(*foh) + attr_len); - oh->mdisc = ABIS_OM_MDISC_MANUF; - - /* add the ip.access magic */ - data = msgb_put(msg, sizeof(ipaccess_magic)+1); - *data++ = sizeof(ipaccess_magic); - memcpy(data, ipaccess_magic, sizeof(ipaccess_magic)); - - /* fill the 12.21 FOM header */ - foh = (struct abis_om_fom_hdr *) msgb_put(msg, sizeof(*foh)); - foh->msg_type = msg_type; - foh->obj_class = obj_class; - foh->obj_inst.bts_nr = bts_nr; - foh->obj_inst.trx_nr = trx_nr; - foh->obj_inst.ts_nr = ts_nr; - - if (attr && attr_len) { - data = msgb_put(msg, attr_len); - memcpy(data, attr, attr_len); - } - - return abis_nm_sendmsg(bts, msg); -} - -/* set some attributes in NVRAM */ -int abis_nm_ipaccess_set_nvattr(struct gsm_bts_trx *trx, uint8_t *attr, - int attr_len) -{ - return abis_nm_ipaccess_msg(trx->bts, NM_MT_IPACC_SET_NVATTR, - NM_OC_BASEB_TRANSC, 0, trx->nr, 0xff, attr, - attr_len); -} - -int abis_nm_ipaccess_rsl_connect(struct gsm_bts_trx *trx, - uint32_t ip, uint16_t port, uint8_t stream) -{ - struct in_addr ia; - uint8_t attr[] = { NM_ATT_IPACC_STREAM_ID, 0, - NM_ATT_IPACC_DST_IP_PORT, 0, 0, - NM_ATT_IPACC_DST_IP, 0, 0, 0, 0 }; - - int attr_len = sizeof(attr); - - ia.s_addr = htonl(ip); - attr[1] = stream; - attr[3] = port >> 8; - attr[4] = port & 0xff; - *(uint32_t *)(attr+6) = ia.s_addr; - - /* if ip == 0, we use the default IP */ - if (ip == 0) - attr_len -= 5; - - DEBUGP(DNM, "ip.access RSL CONNECT IP=%s PORT=%u STREAM=0x%02x\n", - inet_ntoa(ia), port, stream); - - return abis_nm_ipaccess_msg(trx->bts, NM_MT_IPACC_RSL_CONNECT, - NM_OC_BASEB_TRANSC, trx->bts->bts_nr, - trx->nr, 0xff, attr, attr_len); -} - -/* restart / reboot an ip.access nanoBTS */ -int abis_nm_ipaccess_restart(struct gsm_bts_trx *trx) -{ - struct abis_om_hdr *oh; - struct msgb *msg = nm_msgb_alloc(); - - oh = (struct abis_om_hdr *) msgb_put(msg, ABIS_OM_FOM_HDR_SIZE); - fill_om_fom_hdr(oh, 0, NM_MT_IPACC_RESTART, NM_OC_BASEB_TRANSC, - trx->bts->nr, trx->nr, 0xff); - - return abis_nm_sendmsg_direct(trx->bts, msg); -} - -int abis_nm_ipaccess_set_attr(struct gsm_bts *bts, uint8_t obj_class, - uint8_t bts_nr, uint8_t trx_nr, uint8_t ts_nr, - uint8_t *attr, uint8_t attr_len) -{ - return abis_nm_ipaccess_msg(bts, NM_MT_IPACC_SET_ATTR, - obj_class, bts_nr, trx_nr, ts_nr, - attr, attr_len); -} - -void abis_nm_ipaccess_cgi(uint8_t *buf, struct gsm_bts *bts) -{ - /* we simply reuse the GSM48 function and overwrite the RAC - * with the Cell ID */ - gsm48_ra_id_by_bts(buf, bts); - *((uint16_t *)(buf + 5)) = htons(bts->cell_identity); -} - -void gsm_trx_lock_rf(struct gsm_bts_trx *trx, int locked) -{ - int new_state = locked ? NM_STATE_LOCKED : NM_STATE_UNLOCKED; - - trx->mo.nm_state.administrative = new_state; - if (!trx->bts || !trx->bts->oml_link) - return; - - abis_nm_chg_adm_state(trx->bts, NM_OC_RADIO_CARRIER, - trx->bts->bts_nr, trx->nr, 0xff, - new_state); -} - -static const struct value_string ipacc_testres_names[] = { - { NM_IPACC_TESTRES_SUCCESS, "SUCCESS" }, - { NM_IPACC_TESTRES_TIMEOUT, "TIMEOUT" }, - { NM_IPACC_TESTRES_NO_CHANS, "NO CHANNELS" }, - { NM_IPACC_TESTRES_PARTIAL, "PARTIAL" }, - { NM_IPACC_TESTRES_STOPPED, "STOPPED" }, - { 0, NULL } -}; - -const char *ipacc_testres_name(uint8_t res) -{ - return get_value_string(ipacc_testres_names, res); -} - -void ipac_parse_cgi(struct cell_global_id *cid, const uint8_t *buf) -{ - cid->mcc = (buf[0] & 0xf) * 100; - cid->mcc += (buf[0] >> 4) * 10; - cid->mcc += (buf[1] & 0xf) * 1; - - if (buf[1] >> 4 == 0xf) { - cid->mnc = (buf[2] & 0xf) * 10; - cid->mnc += (buf[2] >> 4) * 1; - } else { - cid->mnc = (buf[2] & 0xf) * 100; - cid->mnc += (buf[2] >> 4) * 10; - cid->mnc += (buf[1] >> 4) * 1; - } - - cid->lac = ntohs(*((uint16_t *)&buf[3])); - cid->ci = ntohs(*((uint16_t *)&buf[5])); -} - -/* parse BCCH information IEI from wire format to struct ipac_bcch_info */ -int ipac_parse_bcch_info(struct ipac_bcch_info *binf, uint8_t *buf) -{ - uint8_t *cur = buf; - uint16_t len __attribute__((unused)); - - memset(binf, 0, sizeof(*binf)); - - if (cur[0] != NM_IPAC_EIE_BCCH_INFO) - return -EINVAL; - cur++; - - len = ntohs(*(uint16_t *)cur); - cur += 2; - - binf->info_type = ntohs(*(uint16_t *)cur); - cur += 2; - - if (binf->info_type & IPAC_BINF_FREQ_ERR_QUAL) - binf->freq_qual = *cur >> 2; - - binf->arfcn = (*cur++ & 3) << 8; - binf->arfcn |= *cur++; - - if (binf->info_type & IPAC_BINF_RXLEV) - binf->rx_lev = *cur & 0x3f; - cur++; - - if (binf->info_type & IPAC_BINF_RXQUAL) - binf->rx_qual = *cur & 0x7; - cur++; - - if (binf->info_type & IPAC_BINF_FREQ_ERR_QUAL) - binf->freq_err = ntohs(*(uint16_t *)cur); - cur += 2; - - if (binf->info_type & IPAC_BINF_FRAME_OFFSET) - binf->frame_offset = ntohs(*(uint16_t *)cur); - cur += 2; - - if (binf->info_type & IPAC_BINF_FRAME_NR_OFFSET) - binf->frame_nr_offset = ntohl(*(uint32_t *)cur); - cur += 4; - -#if 0 - /* Somehow this is not set correctly */ - if (binf->info_type & IPAC_BINF_BSIC) -#endif - binf->bsic = *cur & 0x3f; - cur++; - - ipac_parse_cgi(&binf->cgi, cur); - cur += 7; - - if (binf->info_type & IPAC_BINF_NEIGH_BA_SI2) { - memcpy(binf->ba_list_si2, cur, sizeof(binf->ba_list_si2)); - cur += sizeof(binf->ba_list_si2); - } - - if (binf->info_type & IPAC_BINF_NEIGH_BA_SI2bis) { - memcpy(binf->ba_list_si2bis, cur, - sizeof(binf->ba_list_si2bis)); - cur += sizeof(binf->ba_list_si2bis); - } - - if (binf->info_type & IPAC_BINF_NEIGH_BA_SI2ter) { - memcpy(binf->ba_list_si2ter, cur, - sizeof(binf->ba_list_si2ter)); - cur += sizeof(binf->ba_list_si2ter); - } - - return 0; -} - -void abis_nm_clear_queue(struct gsm_bts *bts) -{ - struct msgb *msg; - - while (!llist_empty(&bts->abis_queue)) { - msg = msgb_dequeue(&bts->abis_queue); - msgb_free(msg); - } - - bts->abis_nm_pend = 0; -} diff --git a/openbsc/src/libbsc/abis_nm_ipaccess.c b/openbsc/src/libbsc/abis_nm_ipaccess.c deleted file mode 100644 index b8225383..00000000 --- a/openbsc/src/libbsc/abis_nm_ipaccess.c +++ /dev/null @@ -1,87 +0,0 @@ -/* GSM Network Management (OML) messages on the A-bis interface - * Extensions for the ip.access A-bis over IP protocol*/ - -/* (C) 2008-2009 by Harald Welte - * - * All Rights Reserved - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation; either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - * - */ - -/* A list of all the 'embedded' attributes of ip.access */ -enum ipa_embedded_att { - IPA_ATT_ARFCN_WHITELIST = 0x01, - IPA_ATT_ARFCN_BLACKLIST = 0x02, - IPA_ATT_FREQ_ERR_LIST = 0x03, - IPA_ATT_CHAN_USAGE_LIST = 0x04, - IPA_ATT_BCCH_INF_TYPE = 0x05, - IPA_ATT_BCCH_INF = 0x06, - IPA_ATT_CONFIG = 0x07, - IPA_ATT_RESULT_DETAILS = 0x08, - IPA_ATT_RXLEV_THRESH = 0x09, - IPA_ATT_FREQ_SYNC_OPT = 0x0a, - IPA_ATT_MAC_ADDR = 0x0b, - IPA_ATT_HW_SW_COMPAT_NR = 0x0c, - IPA_ATT_MANUF_SER_NR = 0x0d, - IPA_ATT_OEM_ID = 0x0e, - IPA_ATT_DATETIME_MANUF = 0x0f, - IPA_ATT_DATETIME_CALIB = 0x10, - IPA_ATT_BEACON_INF = 0x11, - IPA_ATT_FREQ_ERR = 0x12, - IPA_ATT_SNMP_COMM_STRING = 0x13, - IPA_ATT_SNMP_TRAP_ADDR = 0x14, - IPA_ATT_SNMP_TRAP_PORT = 0x15, - IPA_ATT_SNMP_MAN_ADDR = 0x16, - IPA_ATT_SNMP_SYS_CONTACT = 0x17, - IPA_ATT_FACTORY_ID = 0x18, - IPA_ATT_FACTORY_SERIAL = 0x19, - IPA_ATT_LOGGED_EVT_IND = 0x1a, - IPA_ATT_LOCAL_ADD_TEXT = 0x1b, - IPA_ATT_FREQ_BANDS = 0x1c, - IPA_ATT_MAX_TA = 0x1d, - IPA_ATT_CIPH_ALG = 0x1e, - IPA_ATT_CHAN_TYPES = 0x1f, - IPA_ATT_CHAN_MODES = 0x20, - IPA_ATT_GPRS_CODING_SCHEMES = 0x21, - IPA_ATT_RTP_FEATURES = 0x22, - IPA_ATT_RSL_FEATURES = 0x23, - IPA_ATT_BTS_HW_CLASS = 0x24, - IPA_ATT_BTS_ID = 0x25, - IPA_ATT_BCAST_L2_MSG = 0x26, -}; - -/* append an ip.access channel list to the given msgb */ -static int ipa_chan_list_append(struct msgb *msg, uint8_t ie, - uint16_t *arfcns, int arfcn_count) -{ - int i; - uint8_t *u8; - uint16_t *u16; - - /* tag */ - u8 = msgb_push(msg, 1); - *u8 = ie; - - /* length in octets */ - u16 = msgb_push(msg, 2); - *u16 = htons(arfcn_count * 2); - - for (i = 0; i < arfcn_count; i++) { - u16 = msgb_push(msg, 2); - *u16 = htons(arfcns[i]); - } - - return 0; -} diff --git a/openbsc/src/libbsc/abis_nm_vty.c b/openbsc/src/libbsc/abis_nm_vty.c deleted file mode 100644 index 6ec0a4a2..00000000 --- a/openbsc/src/libbsc/abis_nm_vty.c +++ /dev/null @@ -1,191 +0,0 @@ -/* VTY interface for A-bis OML (Netowrk Management) */ - -/* (C) 2009-2010 by Harald Welte - * - * All Rights Reserved - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation; either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - * - */ - -#include -#include -#include -#include - -#include - -#include - -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include - -extern struct gsm_network *bsc_gsmnet; - -static struct cmd_node oml_node = { - OML_NODE, - "%s(oml)# ", - 1, -}; - -struct oml_node_state { - struct gsm_bts *bts; - uint8_t obj_class; - uint8_t obj_inst[3]; -}; - -static int dummy_config_write(struct vty *v) -{ - return CMD_SUCCESS; -} - -/* FIXME: auto-generate those strings from the value_string lists */ -#define NM_OBJCLASS_VTY "(site-manager|bts|radio-carrier|baseband-transceiver|channel|adjc|handover|power-contorl|btse|rack|test|envabtse|bport|gprs-nse|gprs-cell|gprs-nsvc|siemenshw)" -#define NM_OBJCLASS_VTY_HELP "Site Manager Object\n" \ - "BTS Object\n" \ - "Radio Carrier Object\n" \ - "Baseband Transceiver Object\n" \ - "Channel (Timeslot) Object\n" \ - "Adjacent Object (Siemens)\n" \ - "Handover Object (Siemens)\n" \ - "Power Control Object (Siemens)\n" \ - "BTSE Object (Siemens)\n" \ - "Rack Object (Siemens)\n" \ - "Test Object (Siemens)\n" \ - "ENVABTSE Object (Siemens)\n" \ - "BPORT Object (Siemens)\n" \ - "GPRS NSE Object (ip.access/osmo-bts)\n" \ - "GPRS Cell Object (ip.acecss/osmo-bts)\n" \ - "GPRS NSVC Object (ip.acecss/osmo-bts)\n" \ - "SIEMENSHW Object (Siemens)\n" - - -DEFUN(oml_class_inst, oml_class_inst_cmd, - "bts <0-255> oml class " NM_OBJCLASS_VTY - " instance <0-255> <0-255> <0-255>", - "BTS related commands\n" "BTS Number\n" - "Manipulate the OML managed objects\n" - "Object Class\n" NM_OBJCLASS_VTY_HELP - "Object Instance\n" "BTS Number\n" "TRX Number\n" "TS Number\n") -{ - struct gsm_bts *bts; - struct oml_node_state *oms; - int bts_nr = atoi(argv[0]); - - bts = gsm_bts_num(gsmnet_from_vty(vty), bts_nr); - if (!bts) { - vty_out(vty, "%% No such BTS (%d)%s", bts_nr, VTY_NEWLINE); - return CMD_WARNING; - } - - oms = talloc_zero(tall_bsc_ctx, struct oml_node_state); - if (!oms) - return CMD_WARNING; - - oms->bts = bts; - oms->obj_class = get_string_value(abis_nm_obj_class_names, argv[1]); - oms->obj_inst[0] = atoi(argv[2]); - oms->obj_inst[1] = atoi(argv[3]); - oms->obj_inst[2] = atoi(argv[4]); - - vty->index = oms; - vty->node = OML_NODE; - - return CMD_SUCCESS; - -} - -DEFUN(oml_classnum_inst, oml_classnum_inst_cmd, - "bts <0-255> oml class <0-255> instance <0-255> <0-255> <0-255>", - "BTS related commands\n" "BTS Number\n" - "Manipulate the OML managed objects\n" - "Object Class\n" "Object Class\n" - "Object Instance\n" "BTS Number\n" "TRX Number\n" "TS Number\n") -{ - struct gsm_bts *bts; - struct oml_node_state *oms; - int bts_nr = atoi(argv[0]); - - bts = gsm_bts_num(gsmnet_from_vty(vty), bts_nr); - if (!bts) { - vty_out(vty, "%% No such BTS (%d)%s", bts_nr, VTY_NEWLINE); - return CMD_WARNING; - } - - oms = talloc_zero(tall_bsc_ctx, struct oml_node_state); - if (!oms) - return CMD_WARNING; - - oms->bts = bts; - oms->obj_class = atoi(argv[1]); - oms->obj_inst[0] = atoi(argv[2]); - oms->obj_inst[1] = atoi(argv[3]); - oms->obj_inst[2] = atoi(argv[4]); - - vty->index = oms; - vty->node = OML_NODE; - - return CMD_SUCCESS; -} - -DEFUN(oml_chg_adm_state, oml_chg_adm_state_cmd, - "change-adm-state (locked|unlocked|shutdown|null)", - "Change the Administrative State\n" - "Locked\n" "Unlocked\n" "Shutdown\n" "NULL\n") -{ - struct oml_node_state *oms = vty->index; - enum abis_nm_adm_state state; - - state = get_string_value(abis_nm_adm_state_names, argv[0]); - - abis_nm_chg_adm_state(oms->bts, oms->obj_class, oms->obj_inst[0], - oms->obj_inst[1], oms->obj_inst[2], state); - - return CMD_SUCCESS; -} - -DEFUN(oml_opstart, oml_opstart_cmd, - "opstart", "Send an OPSTART message to the object") -{ - struct oml_node_state *oms = vty->index; - - abis_nm_opstart(oms->bts, oms->obj_class, oms->obj_inst[0], - oms->obj_inst[1], oms->obj_inst[2]); - - return CMD_SUCCESS; -} - -int abis_nm_vty_init(void) -{ - install_element(ENABLE_NODE, &oml_class_inst_cmd); - install_element(ENABLE_NODE, &oml_classnum_inst_cmd); - install_node(&oml_node, dummy_config_write); - - vty_install_default(OML_NODE); - install_element(OML_NODE, &oml_chg_adm_state_cmd); - install_element(OML_NODE, &oml_opstart_cmd); - - return 0; -} diff --git a/openbsc/src/libbsc/abis_om2000.c b/openbsc/src/libbsc/abis_om2000.c deleted file mode 100644 index 82a14b26..00000000 --- a/openbsc/src/libbsc/abis_om2000.c +++ /dev/null @@ -1,2776 +0,0 @@ -/* Ericsson RBS 2xxx GSM O&M (OM2000) messages on the A-bis interface - * implemented based on protocol trace analysis, no formal documentation */ - -/* (C) 2010-2011,2016 by Harald Welte - * - * All Rights Reserved - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation; either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public 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 - -/* FIXME: move to libosmocore */ -struct osmo_fsm_inst *osmo_fsm_inst_alloc_child_id(struct osmo_fsm *fsm, - struct osmo_fsm_inst *parent, - uint32_t parent_term_event, - const char *id) -{ - struct osmo_fsm_inst *fi; - - fi = osmo_fsm_inst_alloc(fsm, parent, NULL, parent->log_level, - id ? id : parent->id); - if (!fi) { - /* indicate immediate termination to caller */ - osmo_fsm_inst_dispatch(parent, parent_term_event, NULL); - return NULL; - } - - LOGPFSM(fi, "is child of %s\n", osmo_fsm_inst_name(parent)); - - fi->proc.parent = parent; - fi->proc.parent_term_event = parent_term_event; - llist_add(&fi->proc.child, &parent->proc.children); - - return fi; -} - - -#define OM_ALLOC_SIZE 1024 -#define OM_HEADROOM_SIZE 128 - -#define OM2K_TIMEOUT 10 -#define TRX_FSM_TIMEOUT 60 -#define BTS_FSM_TIMEOUT 60 - -/* use following functions from abis_nm.c: - * om2k_msgb_alloc() - * abis_om2k_sendmsg() - */ - -struct abis_om2k_hdr { - struct abis_om_hdr om; - uint16_t msg_type; - struct abis_om2k_mo mo; - uint8_t data[0]; -} __attribute__ ((packed)); - -enum abis_om2k_msgtype { - OM2K_MSGT_ABORT_SP_CMD = 0x0000, - OM2K_MSGT_ABORT_SP_COMPL = 0x0002, - OM2K_MSGT_ALARM_REP_ACK = 0x0004, - OM2K_MSGT_ALARM_REP_NACK = 0x0005, - OM2K_MSGT_ALARM_REP = 0x0006, - OM2K_MSGT_ALARM_STATUS_REQ = 0x0008, - OM2K_MSGT_ALARM_STATUS_REQ_ACK = 0x000a, - OM2K_MSGT_ALARM_STATUS_REQ_REJ = 0x000b, - OM2K_MSGT_ALARM_STATUS_RES_ACK = 0x000c, - OM2K_MSGT_ALARM_STATUS_RES_NACK = 0x000d, - OM2K_MSGT_ALARM_STATUS_RES = 0x000e, - OM2K_MSGT_CAL_TIME_RESP = 0x0010, - OM2K_MSGT_CAL_TIME_REJ = 0x0011, - OM2K_MSGT_CAL_TIME_REQ = 0x0012, - - OM2K_MSGT_CON_CONF_REQ = 0x0014, - OM2K_MSGT_CON_CONF_REQ_ACK = 0x0016, - OM2K_MSGT_CON_CONF_REQ_REJ = 0x0017, - OM2K_MSGT_CON_CONF_RES_ACK = 0x0018, - OM2K_MSGT_CON_CONF_RES_NACK = 0x0019, - OM2K_MSGT_CON_CONF_RES = 0x001a, - - OM2K_MSGT_CONNECT_CMD = 0x001c, - OM2K_MSGT_CONNECT_COMPL = 0x001e, - OM2K_MSGT_CONNECT_REJ = 0x001f, - - OM2K_MSGT_DISABLE_REQ = 0x0028, - OM2K_MSGT_DISABLE_REQ_ACK = 0x002a, - OM2K_MSGT_DISABLE_REQ_REJ = 0x002b, - OM2K_MSGT_DISABLE_RES_ACK = 0x002c, - OM2K_MSGT_DISABLE_RES_NACK = 0x002d, - OM2K_MSGT_DISABLE_RES = 0x002e, - OM2K_MSGT_DISCONNECT_CMD = 0x0030, - OM2K_MSGT_DISCONNECT_COMPL = 0x0032, - OM2K_MSGT_DISCONNECT_REJ = 0x0033, - OM2K_MSGT_ENABLE_REQ = 0x0034, - OM2K_MSGT_ENABLE_REQ_ACK = 0x0036, - OM2K_MSGT_ENABLE_REQ_REJ = 0x0037, - OM2K_MSGT_ENABLE_RES_ACK = 0x0038, - OM2K_MSGT_ENABLE_RES_NACK = 0x0039, - OM2K_MSGT_ENABLE_RES = 0x003a, - - OM2K_MSGT_FAULT_REP_ACK = 0x0040, - OM2K_MSGT_FAULT_REP_NACK = 0x0041, - OM2K_MSGT_FAULT_REP = 0x0042, - - OM2K_MSGT_IS_CONF_REQ = 0x0060, - OM2K_MSGT_IS_CONF_REQ_ACK = 0x0062, - OM2K_MSGT_IS_CONF_REQ_REJ = 0x0063, - OM2K_MSGT_IS_CONF_RES_ACK = 0x0064, - OM2K_MSGT_IS_CONF_RES_NACK = 0x0065, - OM2K_MSGT_IS_CONF_RES = 0x0066, - - OM2K_MSGT_OP_INFO = 0x0074, - OM2K_MSGT_OP_INFO_ACK = 0x0076, - OM2K_MSGT_OP_INFO_REJ = 0x0077, - OM2K_MSGT_RESET_CMD = 0x0078, - OM2K_MSGT_RESET_COMPL = 0x007a, - OM2K_MSGT_RESET_REJ = 0x007b, - OM2K_MSGT_RX_CONF_REQ = 0x007c, - OM2K_MSGT_RX_CONF_REQ_ACK = 0x007e, - OM2K_MSGT_RX_CONF_REQ_REJ = 0x007f, - OM2K_MSGT_RX_CONF_RES_ACK = 0x0080, - OM2K_MSGT_RX_CONF_RES_NACK = 0x0081, - OM2K_MSGT_RX_CONF_RES = 0x0082, - OM2K_MSGT_START_REQ = 0x0084, - OM2K_MSGT_START_REQ_ACK = 0x0086, - OM2K_MSGT_START_REQ_REJ = 0x0087, - OM2K_MSGT_START_RES_ACK = 0x0088, - OM2K_MSGT_START_RES_NACK = 0x0089, - OM2K_MSGT_START_RES = 0x008a, - OM2K_MSGT_STATUS_REQ = 0x008c, - OM2K_MSGT_STATUS_RESP = 0x008e, - OM2K_MSGT_STATUS_REJ = 0x008f, - - OM2K_MSGT_TEST_REQ = 0x0094, - OM2K_MSGT_TEST_REQ_ACK = 0x0096, - OM2K_MSGT_TEST_REQ_REJ = 0x0097, - OM2K_MSGT_TEST_RES_ACK = 0x0098, - OM2K_MSGT_TEST_RES_NACK = 0x0099, - OM2K_MSGT_TEST_RES = 0x009a, - - OM2K_MSGT_TF_CONF_REQ = 0x00a0, - OM2K_MSGT_TF_CONF_REQ_ACK = 0x00a2, - OM2K_MSGT_TF_CONF_REQ_REJ = 0x00a3, - OM2K_MSGT_TF_CONF_RES_ACK = 0x00a4, - OM2K_MSGT_TF_CONF_RES_NACK = 0x00a5, - OM2K_MSGT_TF_CONF_RES = 0x00a6, - OM2K_MSGT_TS_CONF_REQ = 0x00a8, - OM2K_MSGT_TS_CONF_REQ_ACK = 0x00aa, - OM2K_MSGT_TS_CONF_REQ_REJ = 0x00ab, - OM2K_MSGT_TS_CONF_RES_ACK = 0x00ac, - OM2K_MSGT_TS_CONF_RES_NACK = 0x00ad, - OM2K_MSGT_TS_CONF_RES = 0x00ae, - OM2K_MSGT_TX_CONF_REQ = 0x00b0, - OM2K_MSGT_TX_CONF_REQ_ACK = 0x00b2, - OM2K_MSGT_TX_CONF_REQ_REJ = 0x00b3, - OM2K_MSGT_TX_CONF_RES_ACK = 0x00b4, - OM2K_MSGT_TX_CONF_RES_NACK = 0x00b5, - OM2K_MSGT_TX_CONF_RES = 0x00b6, - - OM2K_MSGT_CAPA_REQ = 0x00e8, - OM2K_MSGT_CAPA_REQ_ACK = 0x00ea, - OM2K_MSGT_CAPA_REQ_REJ = 0x00eb, - OM2K_MSGT_CAPA_RES = 0x00ee, - OM2K_MSGT_CAPA_RES_ACK = 0x00ec, - OM2K_MSGT_CAPA_RES_NACK = 0x00ed, - - OM2K_MSGT_NEGOT_REQ_ACK = 0x0104, - OM2K_MSGT_NEGOT_REQ_NACK = 0x0105, - OM2K_MSGT_NEGOT_REQ = 0x0106, -}; - -enum abis_om2k_dei { - OM2K_DEI_ACCORDANCE_IND = 0x00, - OM2K_DEI_BCC = 0x06, - OM2K_DEI_BS_AG_BKS_RES = 0x07, - OM2K_DEI_BSIC = 0x09, - OM2K_DEI_BA_PA_MFRMS = 0x0a, - OM2K_DEI_CBCH_INDICATOR = 0x0b, - OM2K_DEI_CCCH_OPTIONS = 0x0c, - OM2K_DEI_CAL_TIME = 0x0d, - OM2K_DEI_COMBINATION = 0x0f, - OM2K_DEI_CON_CONN_LIST = 0x10, - OM2K_DEI_DRX_DEV_MAX = 0x12, - OM2K_DEI_END_LIST_NR = 0x13, - OM2K_DEI_EXT_COND_MAP_1 = 0x14, - OM2K_DEI_EXT_COND_MAP_2 = 0x15, - OM2K_DEI_FILLING_MARKER = 0x1c, - OM2K_DEI_FN_OFFSET = 0x1d, - OM2K_DEI_FREQ_LIST = 0x1e, - OM2K_DEI_FREQ_SPEC_RX = 0x1f, - OM2K_DEI_FREQ_SPEC_TX = 0x20, - OM2K_DEI_HSN = 0x21, - OM2K_DEI_ICM_INDICATOR = 0x22, - OM2K_DEI_INT_FAULT_MAP_1A = 0x23, - OM2K_DEI_INT_FAULT_MAP_1B = 0x24, - OM2K_DEI_INT_FAULT_MAP_2A = 0x25, - OM2K_DEI_INT_FAULT_MAP_2A_EXT = 0x26, - OM2K_DEI_IS_CONN_LIST = 0x27, - OM2K_DEI_LIST_NR = 0x28, - OM2K_DEI_LOCAL_ACCESS = 0x2a, - OM2K_DEI_MAIO = 0x2b, - OM2K_DEI_MO_STATE = 0x2c, - OM2K_DEI_NY1 = 0x2d, - OM2K_DEI_OP_INFO = 0x2e, - OM2K_DEI_POWER = 0x2f, - OM2K_DEI_REASON_CODE = 0x32, - OM2K_DEI_RX_DIVERSITY = 0x33, - OM2K_DEI_REPL_UNIT_MAP = 0x34, - OM2K_DEI_RESULT_CODE = 0x35, - OM2K_DEI_T3105 = 0x38, - OM2K_DEI_TF_MODE = 0x3a, - OM2K_DEI_TS_NR = 0x3c, - OM2K_DEI_TSC = 0x3d, - OM2K_DEI_BTS_VERSION = 0x40, - OM2K_DEI_OML_IWD_VERSION = 0x41, - OM2K_DEI_RSL_IWD_VERSION = 0x42, - OM2K_DEI_OML_FUNC_MAP_1 = 0x43, - OM2K_DEI_OML_FUNC_MAP_2 = 0x44, - OM2K_DEI_RSL_FUNC_MAP_1 = 0x45, - OM2K_DEI_RSL_FUNC_MAP_2 = 0x46, - OM2K_DEI_EXT_RANGE = 0x47, - OM2K_DEI_REQ_IND = 0x48, - OM2K_DEI_REPL_UNIT_MAP_EXT = 0x50, - OM2K_DEI_ICM_BOUND_PARAMS = 0x74, - OM2K_DEI_LSC = 0x79, - OM2K_DEI_LSC_FILT_TIME = 0x7a, - OM2K_DEI_CALL_SUPV_TIME = 0x7b, - OM2K_DEI_ICM_CHAN_RATE = 0x7e, - OM2K_DEI_HW_INFO_SIG = 0x84, - OM2K_DEI_TF_SYNC_SRC = 0x86, - OM2K_DEI_TTA = 0x87, - OM2K_DEI_CAPA_SIG = 0x8a, - OM2K_DEI_NEGOT_REC1 = 0x90, - OM2K_DEI_NEGOT_REC2 = 0x91, - OM2K_DEI_ENCR_ALG = 0x92, - OM2K_DEI_INTERF_REJ_COMB = 0x94, - OM2K_DEI_FS_OFFSET = 0x98, - OM2K_DEI_EXT_COND_MAP_2_EXT = 0x9c, - OM2K_DEI_TSS_MO_STATE = 0x9d, -}; - -const struct tlv_definition om2k_att_tlvdef = { - .def = { - [OM2K_DEI_ACCORDANCE_IND] = { TLV_TYPE_TV }, - [OM2K_DEI_BCC] = { TLV_TYPE_TV }, - [OM2K_DEI_BS_AG_BKS_RES] = { TLV_TYPE_TV }, - [OM2K_DEI_BSIC] = { TLV_TYPE_TV }, - [OM2K_DEI_BA_PA_MFRMS] = { TLV_TYPE_TV }, - [OM2K_DEI_CBCH_INDICATOR] = { TLV_TYPE_TV }, - [OM2K_DEI_INT_FAULT_MAP_1A] = { TLV_TYPE_FIXED, 6 }, - [OM2K_DEI_INT_FAULT_MAP_1B] = { TLV_TYPE_FIXED, 6 }, - [OM2K_DEI_INT_FAULT_MAP_2A] = { TLV_TYPE_FIXED, 6 }, - [OM2K_DEI_INT_FAULT_MAP_2A_EXT]={ TLV_TYPE_FIXED, 6 }, - [OM2K_DEI_CCCH_OPTIONS] = { TLV_TYPE_TV }, - [OM2K_DEI_CAL_TIME] = { TLV_TYPE_FIXED, 6 }, - [OM2K_DEI_COMBINATION] = { TLV_TYPE_TV }, - [OM2K_DEI_CON_CONN_LIST] = { TLV_TYPE_TLV }, - [OM2K_DEI_DRX_DEV_MAX] = { TLV_TYPE_TV }, - [OM2K_DEI_END_LIST_NR] = { TLV_TYPE_TV }, - [OM2K_DEI_EXT_COND_MAP_1] = { TLV_TYPE_FIXED, 2 }, - [OM2K_DEI_EXT_COND_MAP_2] = { TLV_TYPE_FIXED, 2 }, - [OM2K_DEI_FILLING_MARKER] = { TLV_TYPE_TV }, - [OM2K_DEI_FN_OFFSET] = { TLV_TYPE_FIXED, 2 }, - [OM2K_DEI_FREQ_LIST] = { TLV_TYPE_TLV }, - [OM2K_DEI_FREQ_SPEC_RX] = { TLV_TYPE_FIXED, 2 }, - [OM2K_DEI_FREQ_SPEC_TX] = { TLV_TYPE_FIXED, 2 }, - [OM2K_DEI_HSN] = { TLV_TYPE_TV }, - [OM2K_DEI_ICM_INDICATOR] = { TLV_TYPE_TV }, - [OM2K_DEI_IS_CONN_LIST] = { TLV_TYPE_TLV }, - [OM2K_DEI_LIST_NR] = { TLV_TYPE_TV }, - [OM2K_DEI_LOCAL_ACCESS] = { TLV_TYPE_TV }, - [OM2K_DEI_MAIO] = { TLV_TYPE_TV }, - [OM2K_DEI_MO_STATE] = { TLV_TYPE_TV }, - [OM2K_DEI_NY1] = { TLV_TYPE_TV }, - [OM2K_DEI_OP_INFO] = { TLV_TYPE_TV }, - [OM2K_DEI_POWER] = { TLV_TYPE_TV }, - [OM2K_DEI_REASON_CODE] = { TLV_TYPE_TV }, - [OM2K_DEI_RX_DIVERSITY] = { TLV_TYPE_TV }, - [OM2K_DEI_RESULT_CODE] = { TLV_TYPE_TV }, - [OM2K_DEI_T3105] = { TLV_TYPE_TV }, - [OM2K_DEI_TF_MODE] = { TLV_TYPE_TV }, - [OM2K_DEI_TS_NR] = { TLV_TYPE_TV }, - [OM2K_DEI_TSC] = { TLV_TYPE_TV }, - [OM2K_DEI_BTS_VERSION] = { TLV_TYPE_FIXED, 12 }, - [OM2K_DEI_OML_IWD_VERSION] = { TLV_TYPE_FIXED, 6 }, - [OM2K_DEI_RSL_IWD_VERSION] = { TLV_TYPE_FIXED, 6 }, - [OM2K_DEI_OML_FUNC_MAP_1] = { TLV_TYPE_TLV }, - [OM2K_DEI_OML_FUNC_MAP_2] = { TLV_TYPE_TLV }, - [OM2K_DEI_RSL_FUNC_MAP_1] = { TLV_TYPE_TLV }, - [OM2K_DEI_RSL_FUNC_MAP_2] = { TLV_TYPE_TLV }, - [OM2K_DEI_EXT_RANGE] = { TLV_TYPE_TV }, - [OM2K_DEI_REQ_IND] = { TLV_TYPE_TV }, - [OM2K_DEI_REPL_UNIT_MAP] = { TLV_TYPE_FIXED, 6 }, - [OM2K_DEI_REPL_UNIT_MAP_EXT] = {TLV_TYPE_FIXED, 6}, - [OM2K_DEI_ICM_BOUND_PARAMS] = { TLV_TYPE_FIXED, 5 }, - [OM2K_DEI_LSC] = { TLV_TYPE_TV }, - [OM2K_DEI_LSC_FILT_TIME] = { TLV_TYPE_TV }, - [OM2K_DEI_CALL_SUPV_TIME] = { TLV_TYPE_TV }, - [OM2K_DEI_ICM_CHAN_RATE] = { TLV_TYPE_TV }, - [OM2K_DEI_HW_INFO_SIG] = { TLV_TYPE_FIXED, 2 }, - [OM2K_DEI_TF_SYNC_SRC] = { TLV_TYPE_TV }, - [OM2K_DEI_TTA] = { TLV_TYPE_TV }, - [OM2K_DEI_CAPA_SIG] = { TLV_TYPE_FIXED, 2 }, - [OM2K_DEI_NEGOT_REC1] = { TLV_TYPE_TLV }, - [OM2K_DEI_NEGOT_REC2] = { TLV_TYPE_TLV }, - [OM2K_DEI_ENCR_ALG] = { TLV_TYPE_TV }, - [OM2K_DEI_INTERF_REJ_COMB] = { TLV_TYPE_TV }, - [OM2K_DEI_FS_OFFSET] = { TLV_TYPE_FIXED, 5 }, - [OM2K_DEI_EXT_COND_MAP_2_EXT] = { TLV_TYPE_FIXED, 4 }, - [OM2K_DEI_TSS_MO_STATE] = { TLV_TYPE_FIXED, 4 }, - }, -}; - -static const struct value_string om2k_msgcode_vals[] = { - { 0x0000, "Abort SP Command" }, - { 0x0002, "Abort SP Complete" }, - { 0x0004, "Alarm Report ACK" }, - { 0x0005, "Alarm Report NACK" }, - { 0x0006, "Alarm Report" }, - { 0x0008, "Alarm Status Request" }, - { 0x000a, "Alarm Status Request Accept" }, - { 0x000b, "Alarm Status Request Reject" }, - { 0x000c, "Alarm Status Result ACK" }, - { 0x000d, "Alarm Status Result NACK" }, - { 0x000e, "Alarm Status Result" }, - { 0x0010, "Calendar Time Response" }, - { 0x0011, "Calendar Time Reject" }, - { 0x0012, "Calendar Time Request" }, - { 0x0014, "CON Configuration Request" }, - { 0x0016, "CON Configuration Request Accept" }, - { 0x0017, "CON Configuration Request Reject" }, - { 0x0018, "CON Configuration Result ACK" }, - { 0x0019, "CON Configuration Result NACK" }, - { 0x001a, "CON Configuration Result" }, - { 0x001c, "Connect Command" }, - { 0x001e, "Connect Complete" }, - { 0x001f, "Connect Reject" }, - { 0x0028, "Disable Request" }, - { 0x002a, "Disable Request Accept" }, - { 0x002b, "Disable Request Reject" }, - { 0x002c, "Disable Result ACK" }, - { 0x002d, "Disable Result NACK" }, - { 0x002e, "Disable Result" }, - { 0x0030, "Disconnect Command" }, - { 0x0032, "Disconnect Complete" }, - { 0x0033, "Disconnect Reject" }, - { 0x0034, "Enable Request" }, - { 0x0036, "Enable Request Accept" }, - { 0x0037, "Enable Request Reject" }, - { 0x0038, "Enable Result ACK" }, - { 0x0039, "Enable Result NACK" }, - { 0x003a, "Enable Result" }, - { 0x003c, "Escape Downlink Normal" }, - { 0x003d, "Escape Downlink NACK" }, - { 0x003e, "Escape Uplink Normal" }, - { 0x003f, "Escape Uplink NACK" }, - { 0x0040, "Fault Report ACK" }, - { 0x0041, "Fault Report NACK" }, - { 0x0042, "Fault Report" }, - { 0x0044, "File Package End Command" }, - { 0x0046, "File Package End Result" }, - { 0x0047, "File Package End Reject" }, - { 0x0048, "File Relation Request" }, - { 0x004a, "File Relation Response" }, - { 0x004b, "File Relation Request Reject" }, - { 0x004c, "File Segment Transfer" }, - { 0x004e, "File Segment Transfer Complete" }, - { 0x004f, "File Segment Transfer Reject" }, - { 0x0050, "HW Information Request" }, - { 0x0052, "HW Information Request Accept" }, - { 0x0053, "HW Information Request Reject" }, - { 0x0054, "HW Information Result ACK" }, - { 0x0055, "HW Information Result NACK" }, - { 0x0056, "HW Information Result" }, - { 0x0060, "IS Configuration Request" }, - { 0x0062, "IS Configuration Request Accept" }, - { 0x0063, "IS Configuration Request Reject" }, - { 0x0064, "IS Configuration Result ACK" }, - { 0x0065, "IS Configuration Result NACK" }, - { 0x0066, "IS Configuration Result" }, - { 0x0068, "Load Data End" }, - { 0x006a, "Load Data End Result" }, - { 0x006b, "Load Data End Reject" }, - { 0x006c, "Load Data Init" }, - { 0x006e, "Load Data Init Accept" }, - { 0x006f, "Load Data Init Reject" }, - { 0x0070, "Loop Control Command" }, - { 0x0072, "Loop Control Complete" }, - { 0x0073, "Loop Control Reject" }, - { 0x0074, "Operational Information" }, - { 0x0076, "Operational Information Accept" }, - { 0x0077, "Operational Information Reject" }, - { 0x0078, "Reset Command" }, - { 0x007a, "Reset Complete" }, - { 0x007b, "Reset Reject" }, - { 0x007c, "RX Configuration Request" }, - { 0x007e, "RX Configuration Request Accept" }, - { 0x007f, "RX Configuration Request Reject" }, - { 0x0080, "RX Configuration Result ACK" }, - { 0x0081, "RX Configuration Result NACK" }, - { 0x0082, "RX Configuration Result" }, - { 0x0084, "Start Request" }, - { 0x0086, "Start Request Accept" }, - { 0x0087, "Start Request Reject" }, - { 0x0088, "Start Result ACK" }, - { 0x0089, "Start Result NACK" }, - { 0x008a, "Start Result" }, - { 0x008c, "Status Request" }, - { 0x008e, "Status Response" }, - { 0x008f, "Status Reject" }, - { 0x0094, "Test Request" }, - { 0x0096, "Test Request Accept" }, - { 0x0097, "Test Request Reject" }, - { 0x0098, "Test Result ACK" }, - { 0x0099, "Test Result NACK" }, - { 0x009a, "Test Result" }, - { 0x00a0, "TF Configuration Request" }, - { 0x00a2, "TF Configuration Request Accept" }, - { 0x00a3, "TF Configuration Request Reject" }, - { 0x00a4, "TF Configuration Result ACK" }, - { 0x00a5, "TF Configuration Result NACK" }, - { 0x00a6, "TF Configuration Result" }, - { 0x00a8, "TS Configuration Request" }, - { 0x00aa, "TS Configuration Request Accept" }, - { 0x00ab, "TS Configuration Request Reject" }, - { 0x00ac, "TS Configuration Result ACK" }, - { 0x00ad, "TS Configuration Result NACK" }, - { 0x00ae, "TS Configuration Result" }, - { 0x00b0, "TX Configuration Request" }, - { 0x00b2, "TX Configuration Request Accept" }, - { 0x00b3, "TX Configuration Request Reject" }, - { 0x00b4, "TX Configuration Result ACK" }, - { 0x00b5, "TX Configuration Result NACK" }, - { 0x00b6, "TX Configuration Result" }, - { 0x00bc, "DIP Alarm Report ACK" }, - { 0x00bd, "DIP Alarm Report NACK" }, - { 0x00be, "DIP Alarm Report" }, - { 0x00c0, "DIP Alarm Status Request" }, - { 0x00c2, "DIP Alarm Status Response" }, - { 0x00c3, "DIP Alarm Status Reject" }, - { 0x00c4, "DIP Quality Report I ACK" }, - { 0x00c5, "DIP Quality Report I NACK" }, - { 0x00c6, "DIP Quality Report I" }, - { 0x00c8, "DIP Quality Report II ACK" }, - { 0x00c9, "DIP Quality Report II NACK" }, - { 0x00ca, "DIP Quality Report II" }, - { 0x00dc, "DP Configuration Request" }, - { 0x00de, "DP Configuration Request Accept" }, - { 0x00df, "DP Configuration Request Reject" }, - { 0x00e0, "DP Configuration Result ACK" }, - { 0x00e1, "DP Configuration Result NACK" }, - { 0x00e2, "DP Configuration Result" }, - { 0x00e4, "Capabilities HW Info Report ACK" }, - { 0x00e5, "Capabilities HW Info Report NACK" }, - { 0x00e6, "Capabilities HW Info Report" }, - { 0x00e8, "Capabilities Request" }, - { 0x00ea, "Capabilities Request Accept" }, - { 0x00eb, "Capabilities Request Reject" }, - { 0x00ec, "Capabilities Result ACK" }, - { 0x00ed, "Capabilities Result NACK" }, - { 0x00ee, "Capabilities Result" }, - { 0x00f0, "FM Configuration Request" }, - { 0x00f2, "FM Configuration Request Accept" }, - { 0x00f3, "FM Configuration Request Reject" }, - { 0x00f4, "FM Configuration Result ACK" }, - { 0x00f5, "FM Configuration Result NACK" }, - { 0x00f6, "FM Configuration Result" }, - { 0x00f8, "FM Report Request" }, - { 0x00fa, "FM Report Response" }, - { 0x00fb, "FM Report Reject" }, - { 0x00fc, "FM Start Command" }, - { 0x00fe, "FM Start Complete" }, - { 0x00ff, "FM Start Reject" }, - { 0x0100, "FM Stop Command" }, - { 0x0102, "FM Stop Complete" }, - { 0x0103, "FM Stop Reject" }, - { 0x0104, "Negotiation Request ACK" }, - { 0x0105, "Negotiation Request NACK" }, - { 0x0106, "Negotiation Request" }, - { 0x0108, "BTS Initiated Request ACK" }, - { 0x0109, "BTS Initiated Request NACK" }, - { 0x010a, "BTS Initiated Request" }, - { 0x010c, "Radio Channels Release Command" }, - { 0x010e, "Radio Channels Release Complete" }, - { 0x010f, "Radio Channels Release Reject" }, - { 0x0118, "Feature Control Command" }, - { 0x011a, "Feature Control Complete" }, - { 0x011b, "Feature Control Reject" }, - - { 0, NULL } -}; - -/* TS 12.21 Section 9.4: Attributes */ -static const struct value_string om2k_attr_vals[] = { - { 0x00, "Accordance indication" }, - { 0x01, "Alarm Id" }, - { 0x02, "Alarm Data" }, - { 0x03, "Alarm Severity" }, - { 0x04, "Alarm Status" }, - { 0x05, "Alarm Status Type" }, - { 0x06, "BCC" }, - { 0x07, "BS_AG_BKS_RES" }, - { 0x09, "BSIC" }, - { 0x0a, "BA_PA_MFRMS" }, - { 0x0b, "CBCH Indicator" }, - { 0x0c, "CCCH Options" }, - { 0x0d, "Calendar Time" }, - { 0x0f, "Channel Combination" }, - { 0x10, "CON Connection List" }, - { 0x11, "Data End Indication" }, - { 0x12, "DRX_DEV_MAX" }, - { 0x13, "End List Number" }, - { 0x14, "External Condition Map Class 1" }, - { 0x15, "External Condition Map Class 2" }, - { 0x16, "File Relation Indication" }, - { 0x17, "File Revision" }, - { 0x18, "File Segment Data" }, - { 0x19, "File Segment Length" }, - { 0x1a, "File Segment Sequence Number" }, - { 0x1b, "File Size" }, - { 0x1c, "Filling Marker" }, - { 0x1d, "FN Offset" }, - { 0x1e, "Frequency List" }, - { 0x1f, "Frequency Specifier RX" }, - { 0x20, "Frequency Specifier TX" }, - { 0x21, "HSN" }, - { 0x22, "ICM Indicator" }, - { 0x23, "Internal Fault Map Class 1A" }, - { 0x24, "Internal Fault Map Class 1B" }, - { 0x25, "Internal Fault Map Class 2A" }, - { 0x26, "Internal Fault Map Class 2A Extension" }, - { 0x27, "IS Connection List" }, - { 0x28, "List Number" }, - { 0x29, "File Package State Indication" }, - { 0x2a, "Local Access State" }, - { 0x2b, "MAIO" }, - { 0x2c, "MO State" }, - { 0x2d, "Ny1" }, - { 0x2e, "Operational Information" }, - { 0x2f, "Power" }, - { 0x30, "RU Position Data" }, - { 0x31, "Protocol Error" }, - { 0x32, "Reason Code" }, - { 0x33, "Receiver Diversity" }, - { 0x34, "Replacement Unit Map" }, - { 0x35, "Result Code" }, - { 0x36, "RU Revision Data" }, - { 0x38, "T3105" }, - { 0x39, "Test Loop Setting" }, - { 0x3a, "TF Mode" }, - { 0x3b, "TF Compensation Value" }, - { 0x3c, "Time Slot Number" }, - { 0x3d, "TSC" }, - { 0x3e, "RU Logical Id" }, - { 0x3f, "RU Serial Number Data" }, - { 0x40, "BTS Version" }, - { 0x41, "OML IWD Version" }, - { 0x42, "RWL IWD Version" }, - { 0x43, "OML Function Map 1" }, - { 0x44, "OML Function Map 2" }, - { 0x45, "RSL Function Map 1" }, - { 0x46, "RSL Function Map 2" }, - { 0x47, "Extended Range Indicator" }, - { 0x48, "Request Indicators" }, - { 0x49, "DIP Alarm Condition Map" }, - { 0x4a, "ES Incoming" }, - { 0x4b, "ES Outgoing" }, - { 0x4e, "SES Incoming" }, - { 0x4f, "SES Outgoing" }, - { 0x50, "Replacement Unit Map Extension" }, - { 0x52, "UAS Incoming" }, - { 0x53, "UAS Outgoing" }, - { 0x58, "DF Incoming" }, - { 0x5a, "DF Outgoing" }, - { 0x5c, "SF" }, - { 0x60, "S Bits Setting" }, - { 0x61, "CRC-4 Use Option" }, - { 0x62, "T Parameter" }, - { 0x63, "N Parameter" }, - { 0x64, "N1 Parameter" }, - { 0x65, "N3 Parameter" }, - { 0x66, "N4 Parameter" }, - { 0x67, "P Parameter" }, - { 0x68, "Q Parameter" }, - { 0x69, "BI_Q1" }, - { 0x6a, "BI_Q2" }, - { 0x74, "ICM Boundary Parameters" }, - { 0x77, "AFT" }, - { 0x78, "AFT RAI" }, - { 0x79, "Link Supervision Control" }, - { 0x7a, "Link Supervision Filtering Time" }, - { 0x7b, "Call Supervision Time" }, - { 0x7c, "Interval Length UAS Incoming" }, - { 0x7d, "Interval Length UAS Outgoing" }, - { 0x7e, "ICM Channel Rate" }, - { 0x7f, "Attribute Identifier" }, - { 0x80, "FM Frequency List" }, - { 0x81, "FM Frequency Report" }, - { 0x82, "FM Percentile" }, - { 0x83, "FM Clear Indication" }, - { 0x84, "HW Info Signature" }, - { 0x85, "MO Record" }, - { 0x86, "TF Synchronisation Source" }, - { 0x87, "TTA" }, - { 0x88, "End Segment Number" }, - { 0x89, "Segment Number" }, - { 0x8a, "Capabilities Signature" }, - { 0x8c, "File Relation List" }, - { 0x90, "Negotiation Record I" }, - { 0x91, "Negotiation Record II" }, - { 0x92, "Encryption Algorithm" }, - { 0x94, "Interference Rejection Combining" }, - { 0x95, "Dedication Information" }, - { 0x97, "Feature Code" }, - { 0x98, "FS Offset" }, - { 0x99, "ESB Timeslot" }, - { 0x9a, "Master TG Instance" }, - { 0x9b, "Master TX Chain Delay" }, - { 0x9c, "External Condition Class 2 Extension" }, - { 0x9d, "TSs MO State" }, - { 0, NULL } -}; - -const struct value_string om2k_mo_class_short_vals[] = { - { 0x01, "TRXC" }, - { 0x03, "TS" }, - { 0x04, "TF" }, - { 0x05, "IS" }, - { 0x06, "CON" }, - { 0x07, "DP" }, - { 0x0a, "CF" }, - { 0x0b, "TX" }, - { 0x0c, "RX" }, - { 0, NULL } -}; - -const struct value_string om2k_result_strings[] = { - { 0x02, "Wrong state or out of sequence" }, - { 0x03, "File error" }, - { 0x04, "Fault, unspecified" }, - { 0x05, "Tuning fault" }, - { 0x06, "Protocol error" }, - { 0x07, "MO not connected" }, - { 0x08, "Parameter error" }, - { 0x09, "Optional function not supported" }, - { 0x0a, "Local access state LOCALLY DISCONNECTED" }, - { 0, NULL } -}; - -const struct value_string om2k_accordance_strings[] = { - { 0x00, "Data according to request" }, - { 0x01, "Data not according to request" }, - { 0x02, "Inconsistent MO data" }, - { 0x03, "Capability constraint violation" }, - { 0, NULL } -}; - -const struct value_string om2k_mostate_vals[] = { - { 0x00, "RESET" }, - { 0x01, "STARTED" }, - { 0x02, "ENABLED" }, - { 0x03, "DISABLED" }, - { 0, NULL } -}; - -/* entire decoded OM2K message (header + parsed TLV) */ -struct om2k_decoded_msg { - struct abis_om2k_hdr o2h; - uint16_t msg_type; - struct tlv_parsed tp; -}; - -/* resolve the OM2000 Managed Object by BTS + MO Address */ -static struct om2k_mo * -get_om2k_mo(struct gsm_bts *bts, const struct abis_om2k_mo *abis_mo) -{ - struct om2k_mo *mo = NULL; - struct gsm_bts_trx *trx; - - switch (abis_mo->class) { - case OM2K_MO_CLS_CF: - mo = &bts->rbs2000.cf.om2k_mo; - break; - case OM2K_MO_CLS_CON: - mo = &bts->rbs2000.con.om2k_mo; - break; - case OM2K_MO_CLS_IS: - mo = &bts->rbs2000.is.om2k_mo; - break; - case OM2K_MO_CLS_TF: - mo = &bts->rbs2000.tf.om2k_mo; - break; - - case OM2K_MO_CLS_TRXC: - trx = gsm_bts_trx_num(bts, abis_mo->inst); - if (!trx) - return NULL; - mo = &trx->rbs2000.trxc.om2k_mo; - break; - case OM2K_MO_CLS_TX: - trx = gsm_bts_trx_num(bts, abis_mo->inst); - if (!trx) - return NULL; - mo = &trx->rbs2000.tx.om2k_mo; - break; - case OM2K_MO_CLS_RX: - trx = gsm_bts_trx_num(bts, abis_mo->inst); - if (!trx) - return NULL; - mo = &trx->rbs2000.rx.om2k_mo; - break; - case OM2K_MO_CLS_TS: - trx = gsm_bts_trx_num(bts, abis_mo->assoc_so); - if (!trx) - return NULL; - if (abis_mo->inst >= ARRAY_SIZE(trx->ts)) - return NULL; - mo = &trx->ts[abis_mo->inst].rbs2000.om2k_mo; - break; - default: - return NULL; - }; - - return mo; -} - -static struct msgb *om2k_msgb_alloc(void) -{ - return msgb_alloc_headroom(OM_ALLOC_SIZE, OM_HEADROOM_SIZE, - "OM2000"); -} - -static int abis_om2k_tlv_parse(struct tlv_parsed *tp, const uint8_t *buf, int len) -{ - return tlv_parse(tp, &om2k_att_tlvdef, buf, len, 0, 0); -} - -static int abis_om2k_msg_tlv_parse(struct tlv_parsed *tp, struct abis_om2k_hdr *oh) -{ - return abis_om2k_tlv_parse(tp, oh->data, oh->om.length - 6); -} - -/* decode/parse the message */ -static int om2k_decode_msg(struct om2k_decoded_msg *odm, struct msgb *msg) -{ - struct abis_om2k_hdr *o2h = msgb_l2(msg); - odm->msg_type = ntohs(o2h->msg_type); - odm->o2h = *o2h; - return abis_om2k_msg_tlv_parse(&odm->tp, o2h); -} - -static char *om2k_mo_name(const struct abis_om2k_mo *mo) -{ - static char mo_buf[64]; - - memset(mo_buf, 0, sizeof(mo_buf)); - snprintf(mo_buf, sizeof(mo_buf), "%s/%02x/%02x/%02x", - get_value_string(om2k_mo_class_short_vals, mo->class), - mo->bts, mo->assoc_so, mo->inst); - return mo_buf; -} - -/* resolve the gsm_nm_state data structure for a given MO */ -static struct gsm_nm_state * -mo2nm_state(struct gsm_bts *bts, const struct abis_om2k_mo *mo) -{ - struct gsm_bts_trx *trx; - struct gsm_nm_state *nm_state = NULL; - - switch (mo->class) { - case OM2K_MO_CLS_TRXC: - trx = gsm_bts_trx_num(bts, mo->inst); - if (!trx) - return NULL; - nm_state = &trx->mo.nm_state; - break; - case OM2K_MO_CLS_TS: - trx = gsm_bts_trx_num(bts, mo->assoc_so); - if (!trx) - return NULL; - if (mo->inst >= ARRAY_SIZE(trx->ts)) - return NULL; - nm_state = &trx->ts[mo->inst].mo.nm_state; - break; - case OM2K_MO_CLS_TF: - nm_state = &bts->rbs2000.tf.mo.nm_state; - break; - case OM2K_MO_CLS_IS: - nm_state = &bts->rbs2000.is.mo.nm_state; - break; - case OM2K_MO_CLS_CON: - nm_state = &bts->rbs2000.con.mo.nm_state; - break; - case OM2K_MO_CLS_DP: - nm_state = &bts->rbs2000.con.mo.nm_state; - break; - case OM2K_MO_CLS_CF: - nm_state = &bts->mo.nm_state; - break; - case OM2K_MO_CLS_TX: - trx = gsm_bts_trx_num(bts, mo->inst); - if (!trx) - return NULL; - /* FIXME */ - break; - case OM2K_MO_CLS_RX: - trx = gsm_bts_trx_num(bts, mo->inst); - if (!trx) - return NULL; - /* FIXME */ - break; - } - - return nm_state; -} - -static void *mo2obj(struct gsm_bts *bts, struct abis_om2k_mo *mo) -{ - struct gsm_bts_trx *trx; - - switch (mo->class) { - case OM2K_MO_CLS_TX: - case OM2K_MO_CLS_RX: - case OM2K_MO_CLS_TRXC: - return gsm_bts_trx_num(bts, mo->inst); - case OM2K_MO_CLS_TS: - trx = gsm_bts_trx_num(bts, mo->assoc_so); - if (!trx) - return NULL; - if (mo->inst >= ARRAY_SIZE(trx->ts)) - return NULL; - return &trx->ts[mo->inst]; - case OM2K_MO_CLS_TF: - case OM2K_MO_CLS_IS: - case OM2K_MO_CLS_CON: - case OM2K_MO_CLS_DP: - case OM2K_MO_CLS_CF: - return bts; - } - - return NULL; -} - -static void update_mo_state(struct gsm_bts *bts, struct abis_om2k_mo *mo, - uint8_t mo_state) -{ - struct gsm_nm_state *nm_state = mo2nm_state(bts, mo); - struct gsm_nm_state new_state; - struct nm_statechg_signal_data nsd; - - if (!nm_state) - return; - - new_state = *nm_state; - /* NOTICE: 12.21 Availability state values != OM2000 */ - new_state.availability = mo_state; - - memset(&nsd, 0, sizeof(nsd)); - - nsd.bts = bts; - nsd.obj = mo2obj(bts, mo); - nsd.old_state = nm_state; - nsd.new_state = &new_state; - nsd.om2k_mo = mo; - - osmo_signal_dispatch(SS_NM, S_NM_STATECHG_ADM, &nsd); - - nm_state->availability = new_state.availability; -} - -static void update_op_state(struct gsm_bts *bts, const struct abis_om2k_mo *mo, - uint8_t op_state) -{ - struct gsm_nm_state *nm_state = mo2nm_state(bts, mo); - struct gsm_nm_state new_state; - - if (!nm_state) - return; - - new_state = *nm_state; - switch (op_state) { - case 1: - new_state.operational = NM_OPSTATE_ENABLED; - break; - case 0: - new_state.operational = NM_OPSTATE_DISABLED; - break; - default: - new_state.operational = NM_OPSTATE_NULL; - break; - } - - nm_state->operational = new_state.operational; -} - -static int abis_om2k_sendmsg(struct gsm_bts *bts, struct msgb *msg) -{ - struct abis_om2k_hdr *o2h; - struct gsm_bts_trx *trx; - - msg->l2h = msg->data; - o2h = (struct abis_om2k_hdr *) msg->l2h; - - /* Compute the length in the OML header */ - o2h->om.length = 6 + msgb_l2len(msg)-sizeof(*o2h); - - switch (o2h->mo.class) { - case OM2K_MO_CLS_TRXC: - case OM2K_MO_CLS_TX: - case OM2K_MO_CLS_RX: - /* Route through per-TRX OML Link to the appropriate TRX */ - trx = gsm_bts_trx_by_nr(bts, o2h->mo.inst); - if (!trx) { - LOGP(DNM, LOGL_ERROR, "MO=%s Tx Dropping msg to " - "non-existing TRX\n", om2k_mo_name(&o2h->mo)); - return -ENODEV; - } - msg->dst = trx->oml_link; - break; - case OM2K_MO_CLS_TS: - /* Route through per-TRX OML Link to the appropriate TRX */ - trx = gsm_bts_trx_by_nr(bts, o2h->mo.assoc_so); - if (!trx) { - LOGP(DNM, LOGL_ERROR, "MO=%s Tx Dropping msg to " - "non-existing TRX\n", om2k_mo_name(&o2h->mo)); - return -ENODEV; - } - msg->dst = trx->oml_link; - break; - default: - /* Route through the IXU/DXU OML Link */ - msg->dst = bts->oml_link; - break; - } - - return _abis_nm_sendmsg(msg); -} - -static void fill_om2k_hdr(struct abis_om2k_hdr *o2h, const struct abis_om2k_mo *mo, - uint16_t msg_type) -{ - o2h->om.mdisc = ABIS_OM_MDISC_FOM; - o2h->om.placement = ABIS_OM_PLACEMENT_ONLY; - o2h->om.sequence = 0; - /* We fill o2h->om.length later during om2k_sendmsg() */ - o2h->msg_type = htons(msg_type); - memcpy(&o2h->mo, mo, sizeof(o2h->mo)); -} - -static int abis_om2k_cal_time_resp(struct gsm_bts *bts) -{ - struct msgb *msg = om2k_msgb_alloc(); - struct abis_om2k_hdr *o2k; - time_t tm_t; - struct tm *tm; - - o2k = (struct abis_om2k_hdr *) msgb_put(msg, sizeof(*o2k)); - fill_om2k_hdr(o2k, &bts->rbs2000.cf.om2k_mo.addr, - OM2K_MSGT_CAL_TIME_RESP); - - tm_t = time(NULL); - tm = localtime(&tm_t); - - msgb_put_u8(msg, OM2K_DEI_CAL_TIME); - msgb_put_u8(msg, tm->tm_year % 100); - msgb_put_u8(msg, tm->tm_mon + 1); - msgb_put_u8(msg, tm->tm_mday); - msgb_put_u8(msg, tm->tm_hour); - msgb_put_u8(msg, tm->tm_min); - msgb_put_u8(msg, tm->tm_sec); - - return abis_om2k_sendmsg(bts, msg); -} - -static int abis_om2k_tx_simple(struct gsm_bts *bts, const struct abis_om2k_mo *mo, - uint8_t msg_type) -{ - struct msgb *msg = om2k_msgb_alloc(); - struct abis_om2k_hdr *o2k; - - o2k = (struct abis_om2k_hdr *) msgb_put(msg, sizeof(*o2k)); - fill_om2k_hdr(o2k, mo, msg_type); - - DEBUGP(DNM, "Tx MO=%s %s\n", om2k_mo_name(mo), - get_value_string(om2k_msgcode_vals, msg_type)); - - return abis_om2k_sendmsg(bts, msg); -} - -int abis_om2k_tx_reset_cmd(struct gsm_bts *bts, const struct abis_om2k_mo *mo) -{ - return abis_om2k_tx_simple(bts, mo, OM2K_MSGT_RESET_CMD); -} - -int abis_om2k_tx_start_req(struct gsm_bts *bts, const struct abis_om2k_mo *mo) -{ - return abis_om2k_tx_simple(bts, mo, OM2K_MSGT_START_REQ); -} - -int abis_om2k_tx_status_req(struct gsm_bts *bts, const struct abis_om2k_mo *mo) -{ - return abis_om2k_tx_simple(bts, mo, OM2K_MSGT_STATUS_REQ); -} - -int abis_om2k_tx_connect_cmd(struct gsm_bts *bts, const struct abis_om2k_mo *mo) -{ - return abis_om2k_tx_simple(bts, mo, OM2K_MSGT_CONNECT_CMD); -} - -int abis_om2k_tx_disconnect_cmd(struct gsm_bts *bts, const struct abis_om2k_mo *mo) -{ - return abis_om2k_tx_simple(bts, mo, OM2K_MSGT_DISCONNECT_CMD); -} - -int abis_om2k_tx_test_req(struct gsm_bts *bts, const struct abis_om2k_mo *mo) -{ - return abis_om2k_tx_simple(bts, mo, OM2K_MSGT_TEST_REQ); -} - -int abis_om2k_tx_enable_req(struct gsm_bts *bts, const struct abis_om2k_mo *mo) -{ - return abis_om2k_tx_simple(bts, mo, OM2K_MSGT_ENABLE_REQ); -} - -int abis_om2k_tx_disable_req(struct gsm_bts *bts, const struct abis_om2k_mo *mo) -{ - return abis_om2k_tx_simple(bts, mo, OM2K_MSGT_DISABLE_REQ); -} - -int abis_om2k_tx_op_info(struct gsm_bts *bts, const struct abis_om2k_mo *mo, - uint8_t operational) -{ - struct msgb *msg = om2k_msgb_alloc(); - struct abis_om2k_hdr *o2k; - - o2k = (struct abis_om2k_hdr *) msgb_put(msg, sizeof(*o2k)); - fill_om2k_hdr(o2k, mo, OM2K_MSGT_OP_INFO); - - msgb_tv_put(msg, OM2K_DEI_OP_INFO, operational); - - DEBUGP(DNM, "Tx MO=%s %s\n", om2k_mo_name(mo), - get_value_string(om2k_msgcode_vals, OM2K_MSGT_OP_INFO)); - - /* we update the state here... and send the signal at ACK */ - update_op_state(bts, mo, operational); - - return abis_om2k_sendmsg(bts, msg); -} - -int abis_om2k_tx_cap_req(struct gsm_bts *bts, const struct abis_om2k_mo *mo) -{ - return abis_om2k_tx_simple(bts, mo, OM2K_MSGT_CAPA_REQ); -} - -static void om2k_fill_is_conn_grp(struct om2k_is_conn_grp *grp, uint16_t icp1, - uint16_t icp2, uint8_t cont_idx) -{ - grp->icp1 = htons(icp1); - grp->icp2 = htons(icp2); - grp->cont_idx = cont_idx; -} - -int abis_om2k_tx_is_conf_req(struct gsm_bts *bts) -{ - struct msgb *msg = om2k_msgb_alloc(); - struct abis_om2k_hdr *o2k; - struct is_conn_group *grp; - unsigned int num_grps = 0, i = 0; - struct om2k_is_conn_grp *cg; - - /* count number of groups in linked list */ - llist_for_each_entry(grp, &bts->rbs2000.is.conn_groups, list) - num_grps++; - - if (!num_grps) - return -EINVAL; - - /* allocate buffer for oml group array */ - cg = talloc_zero_array(bts, struct om2k_is_conn_grp, num_grps); - - /* fill array with data from linked list */ - llist_for_each_entry(grp, &bts->rbs2000.is.conn_groups, list) - om2k_fill_is_conn_grp(&cg[i++], grp->icp1, grp->icp2, grp->ci); - - o2k = (struct abis_om2k_hdr *) msgb_put(msg, sizeof(*o2k)); - fill_om2k_hdr(o2k, &bts->rbs2000.is.om2k_mo.addr, - OM2K_MSGT_IS_CONF_REQ); - - msgb_tv_put(msg, OM2K_DEI_LIST_NR, 1); - msgb_tv_put(msg, OM2K_DEI_END_LIST_NR, 1); - - msgb_tlv_put(msg, OM2K_DEI_IS_CONN_LIST, - num_grps * sizeof(*cg), (uint8_t *)cg); - - talloc_free(cg); - - DEBUGP(DNM, "Tx MO=%s %s\n", - om2k_mo_name(&bts->rbs2000.is.om2k_mo.addr), - get_value_string(om2k_msgcode_vals, OM2K_MSGT_IS_CONF_REQ)); - - return abis_om2k_sendmsg(bts, msg); -} - -int abis_om2k_tx_con_conf_req(struct gsm_bts *bts) -{ - struct msgb *msg = om2k_msgb_alloc(); - struct abis_om2k_hdr *o2k; - struct con_group *grp; - unsigned int num_grps = 0; - - /* count number of groups in linked list */ - llist_for_each_entry(grp, &bts->rbs2000.con.conn_groups, list) - num_grps++; - - if (!num_grps) - return -EINVAL; - - /* first build the value part of the OM2K_DEI_CON_CONN_LIST DEI */ - msgb_put_u8(msg, num_grps); - llist_for_each_entry(grp, &bts->rbs2000.con.conn_groups, list) { - struct con_path *cp; - unsigned int num_paths = 0; - llist_for_each_entry(cp, &grp->paths, list) - num_paths++; - msgb_put_u8(msg, num_paths); - llist_for_each_entry(cp, &grp->paths, list) { - struct om2k_con_path *om2k_cp; - om2k_cp = (struct om2k_con_path *) msgb_put(msg, sizeof(*om2k_cp)); - om2k_cp->ccp = htons(cp->ccp); - om2k_cp->ci = cp->ci; - om2k_cp->tag = cp->tag; - om2k_cp->tei = cp->tei; - } - } - msgb_push_u8(msg, msgb_length(msg)); - msgb_push_u8(msg, OM2K_DEI_CON_CONN_LIST); - - /* pre-pend the list number DEIs */ - msgb_tv_push(msg, OM2K_DEI_END_LIST_NR, 1); - msgb_tv_push(msg, OM2K_DEI_LIST_NR, 1); - - /* pre-pend the OM2K header */ - o2k = (struct abis_om2k_hdr *) msgb_push(msg, sizeof(*o2k)); - fill_om2k_hdr(o2k, &bts->rbs2000.con.om2k_mo.addr, - OM2K_MSGT_CON_CONF_REQ); - - DEBUGP(DNM, "Tx MO=%s %s\n", - om2k_mo_name(&bts->rbs2000.con.om2k_mo.addr), - get_value_string(om2k_msgcode_vals, OM2K_MSGT_CON_CONF_REQ)); - - return abis_om2k_sendmsg(bts, msg); -} - -static void om2k_trx_to_mo(struct abis_om2k_mo *mo, - const struct gsm_bts_trx *trx, - enum abis_om2k_mo_cls cls) -{ - mo->class = cls; - mo->bts = 0; - mo->inst = trx->nr; - mo->assoc_so = 255; -} - -static void om2k_ts_to_mo(struct abis_om2k_mo *mo, - const struct gsm_bts_trx_ts *ts) -{ - mo->class = OM2K_MO_CLS_TS; - mo->bts = 0; - mo->inst = ts->nr; - mo->assoc_so = ts->trx->nr; -} - -/* Configure a Receiver MO */ -int abis_om2k_tx_rx_conf_req(struct gsm_bts_trx *trx) -{ - struct msgb *msg = om2k_msgb_alloc(); - struct abis_om2k_hdr *o2k; - struct abis_om2k_mo mo; - - om2k_trx_to_mo(&mo, trx, OM2K_MO_CLS_RX); - - o2k = (struct abis_om2k_hdr *) msgb_put(msg, sizeof(*o2k)); - fill_om2k_hdr(o2k, &mo, OM2K_MSGT_RX_CONF_REQ); - - msgb_tv16_put(msg, OM2K_DEI_FREQ_SPEC_RX, trx->arfcn); - msgb_tv_put(msg, OM2K_DEI_RX_DIVERSITY, 0x02); /* A */ - - return abis_om2k_sendmsg(trx->bts, msg); -} - -/* Configure a Transmitter MO */ -int abis_om2k_tx_tx_conf_req(struct gsm_bts_trx *trx) -{ - struct msgb *msg = om2k_msgb_alloc(); - struct abis_om2k_hdr *o2k; - struct abis_om2k_mo mo; - - om2k_trx_to_mo(&mo, trx, OM2K_MO_CLS_TX); - - o2k = (struct abis_om2k_hdr *) msgb_put(msg, sizeof(*o2k)); - fill_om2k_hdr(o2k, &mo, OM2K_MSGT_TX_CONF_REQ); - - msgb_tv16_put(msg, OM2K_DEI_FREQ_SPEC_TX, trx->arfcn); - msgb_tv_put(msg, OM2K_DEI_POWER, trx->nominal_power-trx->max_power_red); - msgb_tv_put(msg, OM2K_DEI_FILLING_MARKER, 0); /* Filling enabled */ - msgb_tv_put(msg, OM2K_DEI_BCC, trx->bts->bsic & 0x7); - /* Dedication Information is optional */ - - return abis_om2k_sendmsg(trx->bts, msg); -} - -enum abis_om2k_tf_mode { - OM2K_TF_MODE_MASTER = 0x00, - OM2K_TF_MODE_STANDALONE = 0x01, - OM2K_TF_MODE_SLAVE = 0x02, - OM2K_TF_MODE_UNDEFINED = 0xff, -}; - -static const uint8_t fs_offset_undef[5] = { 0xff, 0xff, 0xff, 0xff, 0xff }; - -int abis_om2k_tx_tf_conf_req(struct gsm_bts *bts) -{ - struct msgb *msg = om2k_msgb_alloc(); - struct abis_om2k_hdr *o2k; - - o2k = (struct abis_om2k_hdr *) msgb_put(msg, sizeof(*o2k)); - fill_om2k_hdr(o2k, &bts->rbs2000.tf.om2k_mo.addr, - OM2K_MSGT_TF_CONF_REQ); - - msgb_tv_put(msg, OM2K_DEI_TF_MODE, OM2K_TF_MODE_STANDALONE); - msgb_tv_put(msg, OM2K_DEI_TF_SYNC_SRC, 0x00); - msgb_tv_fixed_put(msg, OM2K_DEI_FS_OFFSET, - sizeof(fs_offset_undef), fs_offset_undef); - - DEBUGP(DNM, "Tx MO=%s %s\n", - om2k_mo_name(&bts->rbs2000.tf.om2k_mo.addr), - get_value_string(om2k_msgcode_vals, OM2K_MSGT_TF_CONF_REQ)); - - return abis_om2k_sendmsg(bts, msg); -} - -static uint8_t pchan2comb(enum gsm_phys_chan_config pchan) -{ - switch (pchan) { - case GSM_PCHAN_CCCH: - return 4; - case GSM_PCHAN_CCCH_SDCCH4: - return 5; - case GSM_PCHAN_SDCCH8_SACCH8C: - return 3; - case GSM_PCHAN_TCH_F: - case GSM_PCHAN_TCH_H: - case GSM_PCHAN_PDCH: - case GSM_PCHAN_TCH_F_PDCH: - case GSM_PCHAN_TCH_F_TCH_H_PDCH: - return 8; - default: - return 0; - } -} - -static uint8_t ts2comb(struct gsm_bts_trx_ts *ts) -{ - switch (ts->pchan) { - case GSM_PCHAN_TCH_F_PDCH: - LOGP(DNM, LOGL_ERROR, "%s pchan %s not intended for use" - " with OM2000, use %s instead\n", - gsm_ts_and_pchan_name(ts), - gsm_pchan_name(GSM_PCHAN_TCH_F_PDCH), - gsm_pchan_name(GSM_PCHAN_TCH_F_TCH_H_PDCH)); - /* If we allowed initialization of TCH/F_PDCH, it would fail - * when we try to send the ip.access specific RSL PDCH Act - * message for it. Rather fail completely right now: */ - return 0; - case GSM_PCHAN_TCH_F_TCH_H_PDCH: - return pchan2comb(GSM_PCHAN_TCH_F); - default: - return pchan2comb(ts->pchan); - } -} - -static int put_freq_list(uint8_t *buf, uint16_t arfcn) -{ - buf[0] = 0x00; /* TX/RX address */ - buf[1] = (arfcn >> 8); - buf[2] = (arfcn & 0xff); - - return 3; -} - -/* Compute a frequency list in OM2000 fomrmat */ -static int om2k_gen_freq_list(uint8_t *list, struct gsm_bts_trx_ts *ts) -{ - uint8_t *cur = list; - int len; - - if (ts->hopping.enabled) { - unsigned int i; - for (i = 0; i < ts->hopping.arfcns.data_len*8; i++) { - if (bitvec_get_bit_pos(&ts->hopping.arfcns, i)) - cur += put_freq_list(cur, i); - } - } else - cur += put_freq_list(cur, ts->trx->arfcn); - - len = cur - list; - - return len; -} - -const uint8_t icm_bound_params[] = { 0x02, 0x06, 0x0c, 0x16, 0x06 }; - -int abis_om2k_tx_ts_conf_req(struct gsm_bts_trx_ts *ts) -{ - struct msgb *msg = om2k_msgb_alloc(); - struct abis_om2k_hdr *o2k; - struct abis_om2k_mo mo; - uint8_t freq_list[64*3]; /* BA max size: 64 ARFCN */ - int freq_list_len; - - om2k_ts_to_mo(&mo, ts); - - memset(freq_list, 0, sizeof(freq_list)); - freq_list_len = om2k_gen_freq_list(freq_list, ts); - if (freq_list_len < 0) - return freq_list_len; - - o2k = (struct abis_om2k_hdr *) msgb_put(msg, sizeof(*o2k)); - fill_om2k_hdr(o2k, &mo, OM2K_MSGT_TS_CONF_REQ); - - msgb_tv_put(msg, OM2K_DEI_COMBINATION, ts2comb(ts)); - msgb_tv_put(msg, OM2K_DEI_TS_NR, ts->nr); - msgb_tlv_put(msg, OM2K_DEI_FREQ_LIST, freq_list_len, freq_list); - msgb_tv_put(msg, OM2K_DEI_HSN, ts->hopping.hsn); - msgb_tv_put(msg, OM2K_DEI_MAIO, ts->hopping.maio); - msgb_tv_put(msg, OM2K_DEI_BSIC, ts->trx->bts->bsic); - msgb_tv_put(msg, OM2K_DEI_RX_DIVERSITY, 0x02); /* A */ - msgb_tv16_put(msg, OM2K_DEI_FN_OFFSET, 0); - msgb_tv_put(msg, OM2K_DEI_EXT_RANGE, 0); /* Off */ - /* Optional: Interference Rejection Combining */ - msgb_tv_put(msg, OM2K_DEI_INTERF_REJ_COMB, 0x00); - switch (ts->pchan) { - case GSM_PCHAN_CCCH: - msgb_tv_put(msg, OM2K_DEI_BA_PA_MFRMS, 0x06); - msgb_tv_put(msg, OM2K_DEI_BS_AG_BKS_RES, 0x01); - msgb_tv_put(msg, OM2K_DEI_DRX_DEV_MAX, 0x05); - /* Repeat Paging/IMM.ASS: True, Allow Paging Type 3: Yes, Page for 5 seconds (default) */ - msgb_tv_put(msg, OM2K_DEI_CCCH_OPTIONS, 0x01); - break; - case GSM_PCHAN_CCCH_SDCCH4: - msgb_tv_put(msg, OM2K_DEI_T3105, ts->trx->bts->network->T3105 / 10); - msgb_tv_put(msg, OM2K_DEI_NY1, 35); - msgb_tv_put(msg, OM2K_DEI_BA_PA_MFRMS, 0x06); - msgb_tv_put(msg, OM2K_DEI_CBCH_INDICATOR, 0); - msgb_tv_put(msg, OM2K_DEI_TSC, gsm_ts_tsc(ts)); - msgb_tv_put(msg, OM2K_DEI_BS_AG_BKS_RES, 0x01); - msgb_tv_put(msg, OM2K_DEI_ICM_INDICATOR, 0); - msgb_tv_put(msg, OM2K_DEI_DRX_DEV_MAX, 0x05); - /* Repeat Paging/IMM.ASS: True, Allow Paging Type 3: Yes, Page for 5 seconds (default) */ - msgb_tv_put(msg, OM2K_DEI_CCCH_OPTIONS, 0x01); - msgb_tv_fixed_put(msg, OM2K_DEI_ICM_BOUND_PARAMS, - sizeof(icm_bound_params), icm_bound_params); - break; - case GSM_PCHAN_SDCCH8_SACCH8C: - msgb_tv_put(msg, OM2K_DEI_T3105, ts->trx->bts->network->T3105 / 10); - msgb_tv_put(msg, OM2K_DEI_NY1, 35); - msgb_tv_put(msg, OM2K_DEI_CBCH_INDICATOR, 0); - msgb_tv_put(msg, OM2K_DEI_TSC, gsm_ts_tsc(ts)); - /* Disable RF RESOURCE INDICATION on idle channels */ - msgb_tv_put(msg, OM2K_DEI_ICM_INDICATOR, 0); - msgb_tv_fixed_put(msg, OM2K_DEI_ICM_BOUND_PARAMS, - sizeof(icm_bound_params), icm_bound_params); - break; - default: - msgb_tv_put(msg, OM2K_DEI_T3105, ts->trx->bts->network->T3105 / 10); - msgb_tv_put(msg, OM2K_DEI_NY1, 35); - msgb_tv_put(msg, OM2K_DEI_TSC, gsm_ts_tsc(ts)); - /* Disable RF RESOURCE INDICATION on idle channels */ - msgb_tv_put(msg, OM2K_DEI_ICM_INDICATOR, 0); - msgb_tv_fixed_put(msg, OM2K_DEI_ICM_BOUND_PARAMS, - sizeof(icm_bound_params), icm_bound_params); - msgb_tv_put(msg, OM2K_DEI_TTA, 10); /* Timer for Time Alignment */ - if (ts->pchan == GSM_PCHAN_TCH_H) - msgb_tv_put(msg, OM2K_DEI_ICM_CHAN_RATE, 1); /* TCH/H */ - else - msgb_tv_put(msg, OM2K_DEI_ICM_CHAN_RATE, 0); /* TCH/F */ - msgb_tv_put(msg, OM2K_DEI_LSC, 1); /* enabled */ - msgb_tv_put(msg, OM2K_DEI_LSC_FILT_TIME, 10); /* units of 100ms */ - msgb_tv_put(msg, OM2K_DEI_CALL_SUPV_TIME, 8); - msgb_tv_put(msg, OM2K_DEI_ENCR_ALG, 0x00); - /* Not sure what those below mean */ - msgb_tv_put(msg, 0x9e, 0x00); - msgb_tv_put(msg, 0x9f, 0x37); - msgb_tv_put(msg, 0xa0, 0x01); - break; - } - - DEBUGP(DNM, "Tx MO=%s %s\n", - om2k_mo_name(&mo), - get_value_string(om2k_msgcode_vals, OM2K_MSGT_TS_CONF_REQ)); - - return abis_om2k_sendmsg(ts->trx->bts, msg); -} - - -/*********************************************************************** - * OM2000 Managed Object (MO) FSM - ***********************************************************************/ - -#define S(x) (1 << (x)) - -enum om2k_event_name { - OM2K_MO_EVT_START, - OM2K_MO_EVT_RX_CONN_COMPL, - OM2K_MO_EVT_RX_RESET_COMPL, - OM2K_MO_EVT_RX_START_REQ_ACCEPT, - OM2K_MO_EVT_RX_START_RES, - OM2K_MO_EVT_RX_CFG_REQ_ACCEPT, - OM2K_MO_EVT_RX_CFG_RES, - OM2K_MO_EVT_RX_ENA_REQ_ACCEPT, - OM2K_MO_EVT_RX_ENA_RES, - OM2K_MO_EVT_RX_OPINFO_ACC, -}; - -static const struct value_string om2k_event_names[] = { - { OM2K_MO_EVT_START, "START" }, - { OM2K_MO_EVT_RX_CONN_COMPL, "RX-CONN-COMPL" }, - { OM2K_MO_EVT_RX_RESET_COMPL, "RX-RESET-COMPL" }, - { OM2K_MO_EVT_RX_START_REQ_ACCEPT, "RX-RESET-REQ-ACCEPT" }, - { OM2K_MO_EVT_RX_START_RES, "RX-START-RESULT" }, - { OM2K_MO_EVT_RX_CFG_REQ_ACCEPT, "RX-CFG-REQ-ACCEPT" }, - { OM2K_MO_EVT_RX_CFG_RES, "RX-CFG-RESULT" }, - { OM2K_MO_EVT_RX_ENA_REQ_ACCEPT, "RX-ENABLE-REQ-ACCEPT" }, - { OM2K_MO_EVT_RX_ENA_RES, "RX-ENABLE-RESULT" }, - { OM2K_MO_EVT_RX_OPINFO_ACC, "RX-OPINFO-ACCEPT" }, - { 0, NULL } -}; - -enum om2k_mo_fsm_state { - OM2K_ST_INIT, - OM2K_ST_WAIT_CONN_COMPL, - OM2K_ST_WAIT_RES_COMPL, - OM2K_ST_WAIT_START_ACCEPT, - OM2K_ST_WAIT_START_RES, - OM2K_ST_WAIT_CFG_ACCEPT, - OM2K_ST_WAIT_CFG_RES, - OM2K_ST_WAIT_ENABLE_ACCEPT, - OM2K_ST_WAIT_ENABLE_RES, - OM2K_ST_WAIT_OPINFO_ACCEPT, - OM2K_ST_DONE, - OM2K_ST_ERROR, -}; - -struct om2k_mo_fsm_priv { - struct gsm_bts_trx *trx; - struct om2k_mo *mo; - uint8_t ts_nr; -}; - -static void om2k_mo_st_init(struct osmo_fsm_inst *fi, uint32_t event, void *data) -{ - struct om2k_mo_fsm_priv *omfp = fi->priv; - - OSMO_ASSERT(event == OM2K_MO_EVT_START); - - switch (omfp->mo->addr.class) { - case OM2K_MO_CLS_CF: - /* no Connect required, is always connected */ - osmo_fsm_inst_state_chg(fi, OM2K_ST_WAIT_START_ACCEPT, - OM2K_TIMEOUT, 0); - abis_om2k_tx_start_req(omfp->trx->bts, &omfp->mo->addr); - break; - case OM2K_MO_CLS_TRXC: - /* no Connect required, start with Reset */ - osmo_fsm_inst_state_chg(fi, OM2K_ST_WAIT_RES_COMPL, - OM2K_TIMEOUT, 0); - abis_om2k_tx_reset_cmd(omfp->trx->bts, &omfp->mo->addr); - break; - default: - /* start with Connect */ - osmo_fsm_inst_state_chg(fi, OM2K_ST_WAIT_CONN_COMPL, - OM2K_TIMEOUT, 0); - abis_om2k_tx_connect_cmd(omfp->trx->bts, &omfp->mo->addr); - break; - } -} - -static void om2k_mo_st_wait_conn_compl(struct osmo_fsm_inst *fi, uint32_t event, void *data) -{ - struct om2k_mo_fsm_priv *omfp = fi->priv; - - switch (omfp->mo->addr.class) { -#if 0 - case OM2K_MO_CLS_TF: - /* skip the reset, hope that helps */ - osmo_fsm_inst_state_chg(fi, OM2K_ST_WAIT_START_ACCEPT, - OM2K_TIMEOUT, 0); - abis_om2k_tx_start_req(omfp->trx->bts, &omfp->mo->addr); - break; -#endif - default: - osmo_fsm_inst_state_chg(fi, OM2K_ST_WAIT_RES_COMPL, - OM2K_TIMEOUT, 0); - abis_om2k_tx_reset_cmd(omfp->trx->bts, &omfp->mo->addr); - break; - } -} - -static void om2k_mo_st_wait_res_compl(struct osmo_fsm_inst *fi, uint32_t event, void *data) -{ - struct om2k_mo_fsm_priv *omfp = fi->priv; - - osmo_fsm_inst_state_chg(fi, OM2K_ST_WAIT_START_ACCEPT, - OM2K_TIMEOUT, 0); - abis_om2k_tx_start_req(omfp->trx->bts, &omfp->mo->addr); -} - -static void om2k_mo_st_wait_start_accept(struct osmo_fsm_inst *fi, uint32_t event, void *data) -{ - struct om2k_decoded_msg *omd = data; - - switch (omd->msg_type) { - case OM2K_MSGT_START_REQ_ACK: - osmo_fsm_inst_state_chg(fi, OM2K_ST_WAIT_START_RES, - OM2K_TIMEOUT, 0); - break; - case OM2K_MSGT_START_REQ_REJ: - osmo_fsm_inst_state_chg(fi, OM2K_ST_ERROR, 0, 0); - break; - } -} - -static void om2k_mo_st_wait_start_res(struct osmo_fsm_inst *fi, uint32_t event, void *data) -{ - struct om2k_mo_fsm_priv *omfp = fi->priv; - struct gsm_bts_trx_ts *ts; - - switch (omfp->mo->addr.class) { - case OM2K_MO_CLS_CF: - case OM2K_MO_CLS_TRXC: - /* Transition directly to Operational Info */ - osmo_fsm_inst_state_chg(fi, OM2K_ST_WAIT_OPINFO_ACCEPT, - OM2K_TIMEOUT, 0); - abis_om2k_tx_op_info(omfp->trx->bts, &omfp->mo->addr, 1); - return; - case OM2K_MO_CLS_DP: - /* Transition directoy to WAIT_ENABLE_ACCEPT */ - osmo_fsm_inst_state_chg(fi, OM2K_ST_WAIT_ENABLE_ACCEPT, - OM2K_TIMEOUT, 0); - abis_om2k_tx_enable_req(omfp->trx->bts, &omfp->mo->addr); - return; -#if 0 - case OM2K_MO_CLS_TF: - /* skip the config, hope that helps speeding things up */ - osmo_fsm_inst_state_chg(fi, OM2K_ST_WAIT_ENABLE_ACCEPT, - OM2K_TIMEOUT, 0); - abis_om2k_tx_enable_req(omfp->trx->bts, &omfp->mo->addr); - return; -#endif - } - - osmo_fsm_inst_state_chg(fi, OM2K_ST_WAIT_CFG_ACCEPT, - OM2K_TIMEOUT, 0); - switch (omfp->mo->addr.class) { - case OM2K_MO_CLS_TF: - abis_om2k_tx_tf_conf_req(omfp->trx->bts); - break; - case OM2K_MO_CLS_IS: - abis_om2k_tx_is_conf_req(omfp->trx->bts); - break; - case OM2K_MO_CLS_CON: - abis_om2k_tx_con_conf_req(omfp->trx->bts); - break; - case OM2K_MO_CLS_TX: - abis_om2k_tx_tx_conf_req(omfp->trx); - break; - case OM2K_MO_CLS_RX: - abis_om2k_tx_rx_conf_req(omfp->trx); - break; - case OM2K_MO_CLS_TS: - ts = mo2obj(omfp->trx->bts, &omfp->mo->addr); - abis_om2k_tx_ts_conf_req(ts); - break; - } -} - -static void om2k_mo_st_wait_cfg_accept(struct osmo_fsm_inst *fi, uint32_t event, void *data) -{ - struct om2k_mo_fsm_priv *omfp = fi->priv; - uint32_t timeout = OM2K_TIMEOUT; - - if (omfp->mo->addr.class == OM2K_MO_CLS_TF) - timeout = 600; - - osmo_fsm_inst_state_chg(fi, OM2K_ST_WAIT_CFG_RES, timeout, 0); -} - -static void om2k_mo_st_wait_cfg_res(struct osmo_fsm_inst *fi, uint32_t event, void *data) -{ - struct om2k_mo_fsm_priv *omfp = fi->priv; - struct om2k_decoded_msg *omd = data; - uint8_t accordance; - - if (!TLVP_PRESENT(&omd->tp, OM2K_DEI_ACCORDANCE_IND)) { - osmo_fsm_inst_state_chg(fi, OM2K_ST_ERROR, 0, 0); - return; - } - accordance = *TLVP_VAL(&omd->tp, OM2K_DEI_ACCORDANCE_IND); - - if (accordance != 0) { - /* accordance not OK */ - osmo_fsm_inst_state_chg(fi, OM2K_ST_ERROR, 0, 0); - return; - } - - osmo_fsm_inst_state_chg(fi, OM2K_ST_WAIT_ENABLE_ACCEPT, - OM2K_TIMEOUT, 0); - abis_om2k_tx_enable_req(omfp->trx->bts, &omfp->mo->addr); -} - -static void om2k_mo_st_wait_enable_accept(struct osmo_fsm_inst *fi, uint32_t event, void *data) -{ - struct om2k_mo_fsm_priv *omfp = fi->priv; - struct om2k_decoded_msg *omd = data; - - switch (omd->msg_type) { - case OM2K_MSGT_ENABLE_REQ_REJ: - osmo_fsm_inst_state_chg(fi, OM2K_ST_ERROR, 0, 0); - break; - case OM2K_MSGT_ENABLE_REQ_ACK: - if (omfp->mo->addr.class == OM2K_MO_CLS_IS && - omfp->trx->bts->rbs2000.use_superchannel) - e1inp_ericsson_set_altc(omfp->trx->bts->oml_link->ts->line, 1); - osmo_fsm_inst_state_chg(fi, OM2K_ST_WAIT_ENABLE_RES, - OM2K_TIMEOUT, 0); - } -} - -static void om2k_mo_st_wait_enable_res(struct osmo_fsm_inst *fi, uint32_t event, void *data) -{ - struct om2k_mo_fsm_priv *omfp = fi->priv; - //struct om2k_decoded_msg *omd = data; - /* TODO: check if state is actually enabled now? */ - - osmo_fsm_inst_state_chg(fi, OM2K_ST_WAIT_OPINFO_ACCEPT, - OM2K_TIMEOUT, 0); - abis_om2k_tx_op_info(omfp->trx->bts, &omfp->mo->addr, 1); -} - -static void om2k_mo_st_wait_opinfo_accept(struct osmo_fsm_inst *fi, uint32_t event, void *data) -{ - struct om2k_mo_fsm_priv *omfp = fi->priv; - - /* if we have just received opinfo accept for the timeslot, - * start dynamic TCH switching procedures */ - if (omfp->mo->addr.class == OM2K_MO_CLS_TS) { - struct gsm_bts_trx_ts *ts; - ts = mo2obj(omfp->trx->bts, &omfp->mo->addr); - dyn_ts_init(ts); - } - osmo_fsm_inst_state_chg(fi, OM2K_ST_DONE, 0, 0); -} - -static void om2k_mo_s_done_onenter(struct osmo_fsm_inst *fi, uint32_t prev_state) -{ - struct om2k_mo_fsm_priv *omfp = fi->priv; - omfp->mo->fsm = NULL; - osmo_fsm_inst_term(fi, OSMO_FSM_TERM_REGULAR, NULL); -} - -static void om2k_mo_s_error_onenter(struct osmo_fsm_inst *fi, uint32_t prev_state) -{ - struct om2k_mo_fsm_priv *omfp = fi->priv; - - omfp->mo->fsm = NULL; - osmo_fsm_inst_term(fi, OSMO_FSM_TERM_ERROR, NULL); -} - -static const struct osmo_fsm_state om2k_is_states[] = { - [OM2K_ST_INIT] = { - .name = "INIT", - .in_event_mask = S(OM2K_MO_EVT_START), - .out_state_mask = S(OM2K_ST_DONE) | - S(OM2K_ST_ERROR) | - S(OM2K_ST_WAIT_CONN_COMPL) | - S(OM2K_ST_WAIT_START_ACCEPT) | - S(OM2K_ST_WAIT_RES_COMPL), - .action = om2k_mo_st_init, - }, - [OM2K_ST_WAIT_CONN_COMPL] = { - .name = "WAIT-CONN-COMPL", - .in_event_mask = S(OM2K_MO_EVT_RX_CONN_COMPL), - .out_state_mask = S(OM2K_ST_DONE) | - S(OM2K_ST_ERROR) | - S(OM2K_ST_WAIT_START_ACCEPT) | - S(OM2K_ST_WAIT_RES_COMPL), - .action = om2k_mo_st_wait_conn_compl, - }, - [OM2K_ST_WAIT_RES_COMPL] = { - .name = "WAIT-RES-COMPL", - .in_event_mask = S(OM2K_MO_EVT_RX_RESET_COMPL), - .out_state_mask = S(OM2K_ST_DONE) | - S(OM2K_ST_ERROR) | - S(OM2K_ST_WAIT_START_ACCEPT), - .action = om2k_mo_st_wait_res_compl, - }, - [OM2K_ST_WAIT_START_ACCEPT] = { - .name = "WAIT-START-ACCEPT", - .in_event_mask = S(OM2K_MO_EVT_RX_START_REQ_ACCEPT), - .out_state_mask = S(OM2K_ST_DONE) | - S(OM2K_ST_ERROR) | - S(OM2K_ST_WAIT_START_RES), - .action =om2k_mo_st_wait_start_accept, - }, - [OM2K_ST_WAIT_START_RES] = { - .name = "WAIT-START-RES", - .in_event_mask = S(OM2K_MO_EVT_RX_START_RES), - .out_state_mask = S(OM2K_ST_DONE) | - S(OM2K_ST_ERROR) | - S(OM2K_ST_WAIT_CFG_ACCEPT) | - S(OM2K_ST_WAIT_OPINFO_ACCEPT), - .action = om2k_mo_st_wait_start_res, - }, - [OM2K_ST_WAIT_CFG_ACCEPT] = { - .name = "WAIT-CFG-ACCEPT", - .in_event_mask = S(OM2K_MO_EVT_RX_CFG_REQ_ACCEPT), - .out_state_mask = S(OM2K_ST_DONE) | - S(OM2K_ST_ERROR) | - S(OM2K_ST_WAIT_CFG_RES), - .action = om2k_mo_st_wait_cfg_accept, - }, - [OM2K_ST_WAIT_CFG_RES] = { - .name = "WAIT-CFG-RES", - .in_event_mask = S(OM2K_MO_EVT_RX_CFG_RES), - .out_state_mask = S(OM2K_ST_DONE) | - S(OM2K_ST_ERROR) | - S(OM2K_ST_WAIT_ENABLE_ACCEPT), - .action = om2k_mo_st_wait_cfg_res, - }, - [OM2K_ST_WAIT_ENABLE_ACCEPT] = { - .name = "WAIT-ENABLE-ACCEPT", - .in_event_mask = S(OM2K_MO_EVT_RX_ENA_REQ_ACCEPT), - .out_state_mask = S(OM2K_ST_DONE) | - S(OM2K_ST_ERROR) | - S(OM2K_ST_WAIT_ENABLE_RES), - .action = om2k_mo_st_wait_enable_accept, - }, - [OM2K_ST_WAIT_ENABLE_RES] = { - .name = "WAIT-ENABLE-RES", - .in_event_mask = S(OM2K_MO_EVT_RX_ENA_RES), - .out_state_mask = S(OM2K_ST_DONE) | - S(OM2K_ST_ERROR) | - S(OM2K_ST_WAIT_OPINFO_ACCEPT), - .action = om2k_mo_st_wait_enable_res, - }, - [OM2K_ST_WAIT_OPINFO_ACCEPT] = { - .name = "WAIT-OPINFO-ACCEPT", - .in_event_mask = S(OM2K_MO_EVT_RX_OPINFO_ACC), - .out_state_mask = S(OM2K_ST_DONE) | - S(OM2K_ST_ERROR), - .action = om2k_mo_st_wait_opinfo_accept, - }, - [OM2K_ST_DONE] = { - .name = "DONE", - .in_event_mask = 0, - .out_state_mask = 0, - .onenter = om2k_mo_s_done_onenter, - }, - [OM2K_ST_ERROR] = { - .name = "ERROR", - .in_event_mask = 0, - .out_state_mask = 0, - .onenter = om2k_mo_s_error_onenter, - }, - -}; - -static int om2k_mo_timer_cb(struct osmo_fsm_inst *fi) -{ - osmo_fsm_inst_state_chg(fi, OM2K_ST_ERROR, 0, 0); - return 0; -} - -static struct osmo_fsm om2k_mo_fsm = { - .name = "OM2000-MO", - .states = om2k_is_states, - .num_states = ARRAY_SIZE(om2k_is_states), - .log_subsys = DNM, - .event_names = om2k_event_names, - .timer_cb = om2k_mo_timer_cb, -}; - -struct osmo_fsm_inst *om2k_mo_fsm_start(struct osmo_fsm_inst *parent, - uint32_t term_event, - struct gsm_bts_trx *trx, struct om2k_mo *mo) -{ - struct osmo_fsm_inst *fi; - struct om2k_mo_fsm_priv *omfp; - char idbuf[64]; - - snprintf(idbuf, sizeof(idbuf), "%s-%s", parent->id, - om2k_mo_name(&mo->addr)); - - fi = osmo_fsm_inst_alloc_child_id(&om2k_mo_fsm, parent, - term_event, idbuf); - if (!fi) - return NULL; - - mo->fsm = fi; - omfp = talloc_zero(fi, struct om2k_mo_fsm_priv); - omfp->mo = mo; - omfp->trx = trx; - fi->priv = omfp; - - osmo_fsm_inst_dispatch(fi, OM2K_MO_EVT_START, NULL); - - return fi; -} - -int om2k_mo_fsm_recvmsg(struct gsm_bts *bts, struct om2k_mo *mo, - struct om2k_decoded_msg *odm) -{ - switch (odm->msg_type) { - case OM2K_MSGT_CONNECT_COMPL: - case OM2K_MSGT_CONNECT_REJ: - osmo_fsm_inst_dispatch(mo->fsm, - OM2K_MO_EVT_RX_CONN_COMPL, odm); - break; - - case OM2K_MSGT_RESET_COMPL: - case OM2K_MSGT_RESET_REJ: - osmo_fsm_inst_dispatch(mo->fsm, - OM2K_MO_EVT_RX_RESET_COMPL, odm); - break; - - case OM2K_MSGT_START_REQ_ACK: - case OM2K_MSGT_START_REQ_REJ: - osmo_fsm_inst_dispatch(mo->fsm, - OM2K_MO_EVT_RX_START_REQ_ACCEPT, odm); - break; - - case OM2K_MSGT_START_RES: - osmo_fsm_inst_dispatch(mo->fsm, - OM2K_MO_EVT_RX_START_RES, odm); - break; - - case OM2K_MSGT_CON_CONF_REQ_ACK: - case OM2K_MSGT_IS_CONF_REQ_ACK: - case OM2K_MSGT_RX_CONF_REQ_ACK: - case OM2K_MSGT_TF_CONF_REQ_ACK: - case OM2K_MSGT_TS_CONF_REQ_ACK: - case OM2K_MSGT_TX_CONF_REQ_ACK: - osmo_fsm_inst_dispatch(mo->fsm, - OM2K_MO_EVT_RX_CFG_REQ_ACCEPT, odm); - break; - - case OM2K_MSGT_CON_CONF_RES: - case OM2K_MSGT_IS_CONF_RES: - case OM2K_MSGT_RX_CONF_RES: - case OM2K_MSGT_TF_CONF_RES: - case OM2K_MSGT_TS_CONF_RES: - case OM2K_MSGT_TX_CONF_RES: - osmo_fsm_inst_dispatch(mo->fsm, - OM2K_MO_EVT_RX_CFG_RES, odm); - break; - - case OM2K_MSGT_ENABLE_REQ_ACK: - case OM2K_MSGT_ENABLE_REQ_REJ: - osmo_fsm_inst_dispatch(mo->fsm, - OM2K_MO_EVT_RX_ENA_REQ_ACCEPT, odm); - break; - case OM2K_MSGT_ENABLE_RES: - osmo_fsm_inst_dispatch(mo->fsm, - OM2K_MO_EVT_RX_ENA_RES, odm); - break; - - case OM2K_MSGT_OP_INFO_ACK: - case OM2K_MSGT_OP_INFO_REJ: - osmo_fsm_inst_dispatch(mo->fsm, - OM2K_MO_EVT_RX_OPINFO_ACC, odm); - break; - default: - return -1; - } - - return 0; -} - -/*********************************************************************** - * OM2000 TRX Finite State Machine, initializes TRXC and all siblings - ***********************************************************************/ - -enum om2k_trx_event { - OM2K_TRX_EVT_START, - OM2K_TRX_EVT_TRXC_DONE, - OM2K_TRX_EVT_TX_DONE, - OM2K_TRX_EVT_RX_DONE, - OM2K_TRX_EVT_TS_DONE, - OM2K_TRX_EVT_STOP, -}; - -static struct value_string om2k_trx_events[] = { - { OM2K_TRX_EVT_START, "START" }, - { OM2K_TRX_EVT_TRXC_DONE, "TRXC-DONE" }, - { OM2K_TRX_EVT_TX_DONE, "TX-DONE" }, - { OM2K_TRX_EVT_RX_DONE, "RX-DONE" }, - { OM2K_TRX_EVT_TS_DONE, "TS-DONE" }, - { OM2K_TRX_EVT_STOP, "STOP" }, - { 0, NULL } -}; - -enum om2k_trx_state { - OM2K_TRX_S_INIT, - OM2K_TRX_S_WAIT_TRXC, - OM2K_TRX_S_WAIT_TX, - OM2K_TRX_S_WAIT_RX, - OM2K_TRX_S_WAIT_TS, - OM2K_TRX_S_DONE, - OM2K_TRX_S_ERROR -}; - -struct om2k_trx_fsm_priv { - struct gsm_bts_trx *trx; - uint8_t next_ts_nr; -}; - -static void om2k_trx_s_init(struct osmo_fsm_inst *fi, uint32_t event, void *data) -{ - struct om2k_trx_fsm_priv *otfp = fi->priv; - - /* First initialize TRXC */ - osmo_fsm_inst_state_chg(fi, OM2K_TRX_S_WAIT_TRXC, - TRX_FSM_TIMEOUT, 0); - om2k_mo_fsm_start(fi, OM2K_TRX_EVT_TRXC_DONE, otfp->trx, - &otfp->trx->rbs2000.trxc.om2k_mo); -} - -static void om2k_trx_s_wait_trxc(struct osmo_fsm_inst *fi, uint32_t event, void *data) -{ - struct om2k_trx_fsm_priv *otfp = fi->priv; - - /* Initialize TX after TRXC */ - osmo_fsm_inst_state_chg(fi, OM2K_TRX_S_WAIT_TX, - TRX_FSM_TIMEOUT, 0); - om2k_mo_fsm_start(fi, OM2K_TRX_EVT_TX_DONE, otfp->trx, - &otfp->trx->rbs2000.tx.om2k_mo); -} - -static void om2k_trx_s_wait_tx(struct osmo_fsm_inst *fi, uint32_t event, void *data) -{ - struct om2k_trx_fsm_priv *otfp = fi->priv; - - /* Initialize RX after TX */ - osmo_fsm_inst_state_chg(fi, OM2K_TRX_S_WAIT_RX, - TRX_FSM_TIMEOUT, 0); - om2k_mo_fsm_start(fi, OM2K_TRX_EVT_RX_DONE, otfp->trx, - &otfp->trx->rbs2000.rx.om2k_mo); -} - -static void om2k_trx_s_wait_rx(struct osmo_fsm_inst *fi, uint32_t event, void *data) -{ - struct om2k_trx_fsm_priv *otfp = fi->priv; - struct gsm_bts_trx_ts *ts; - - /* Initialize Timeslots after TX */ - osmo_fsm_inst_state_chg(fi, OM2K_TRX_S_WAIT_TS, - TRX_FSM_TIMEOUT, 0); - otfp->next_ts_nr = 0; - ts = &otfp->trx->ts[otfp->next_ts_nr++]; - om2k_mo_fsm_start(fi, OM2K_TRX_EVT_TS_DONE, otfp->trx, - &ts->rbs2000.om2k_mo); -} - -static void om2k_trx_s_wait_ts(struct osmo_fsm_inst *fi, uint32_t event, void *data) -{ - struct om2k_trx_fsm_priv *otfp = fi->priv; - struct gsm_bts_trx_ts *ts; - - if (otfp->next_ts_nr < 8) { - /* iterate to the next timeslot */ - ts = &otfp->trx->ts[otfp->next_ts_nr++]; - om2k_mo_fsm_start(fi, OM2K_TRX_EVT_TS_DONE, otfp->trx, - &ts->rbs2000.om2k_mo); - } else { - /* only after all 8 TS */ - osmo_fsm_inst_state_chg(fi, OM2K_TRX_S_DONE, 0, 0); - } -} - -static void om2k_trx_s_done_onenter(struct osmo_fsm_inst *fi, uint32_t prev_state) -{ - struct om2k_trx_fsm_priv *otfp = fi->priv; - gsm_bts_trx_set_system_infos(otfp->trx); - osmo_fsm_inst_term(fi, OSMO_FSM_TERM_REGULAR, NULL); -} - -static const struct osmo_fsm_state om2k_trx_states[] = { - [OM2K_TRX_S_INIT] = { - .in_event_mask = S(OM2K_TRX_EVT_START), - .out_state_mask = S(OM2K_TRX_S_WAIT_TRXC), - .name = "INIT", - .action = om2k_trx_s_init, - }, - [OM2K_TRX_S_WAIT_TRXC] = { - .in_event_mask = S(OM2K_TRX_EVT_TRXC_DONE), - .out_state_mask = S(OM2K_TRX_S_ERROR) | - S(OM2K_TRX_S_WAIT_TX), - .name = "WAIT-TRXC", - .action = om2k_trx_s_wait_trxc, - }, - [OM2K_TRX_S_WAIT_TX] = { - .in_event_mask = S(OM2K_TRX_EVT_TX_DONE), - .out_state_mask = S(OM2K_TRX_S_ERROR) | - S(OM2K_TRX_S_WAIT_RX), - .name = "WAIT-TX", - .action = om2k_trx_s_wait_tx, - }, - [OM2K_TRX_S_WAIT_RX] = { - .in_event_mask = S(OM2K_TRX_EVT_RX_DONE), - .out_state_mask = S(OM2K_TRX_S_ERROR) | - S(OM2K_TRX_S_WAIT_TS), - .name = "WAIT-RX", - .action = om2k_trx_s_wait_rx, - }, - [OM2K_TRX_S_WAIT_TS] = { - .in_event_mask = S(OM2K_TRX_EVT_TS_DONE), - .out_state_mask = S(OM2K_TRX_S_ERROR) | - S(OM2K_TRX_S_DONE), - .name = "WAIT-TS", - .action = om2k_trx_s_wait_ts, - }, - [OM2K_TRX_S_DONE] = { - .name = "DONE", - .onenter = om2k_trx_s_done_onenter, - }, - [OM2K_TRX_S_ERROR] = { - .name = "ERROR", - }, -}; - -static int om2k_trx_timer_cb(struct osmo_fsm_inst *fi) -{ - osmo_fsm_inst_state_chg(fi, OM2K_TRX_S_ERROR, 0, 0); - return 0; -} - -static struct osmo_fsm om2k_trx_fsm = { - .name = "OM2000-TRX", - .states = om2k_trx_states, - .num_states = ARRAY_SIZE(om2k_trx_states), - .log_subsys = DNM, - .event_names = om2k_trx_events, - .timer_cb = om2k_trx_timer_cb, -}; - -struct osmo_fsm_inst *om2k_trx_fsm_start(struct osmo_fsm_inst *parent, - struct gsm_bts_trx *trx, - uint32_t term_event) -{ - struct osmo_fsm_inst *fi; - struct om2k_trx_fsm_priv *otfp; - char idbuf[32]; - - snprintf(idbuf, sizeof(idbuf), "%u/%u", trx->bts->nr, trx->nr); - - fi = osmo_fsm_inst_alloc_child_id(&om2k_trx_fsm, parent, term_event, - idbuf); - if (!fi) - return NULL; - - otfp = talloc_zero(fi, struct om2k_trx_fsm_priv); - otfp->trx = trx; - fi->priv = otfp; - - osmo_fsm_inst_dispatch(fi, OM2K_TRX_EVT_START, NULL); - - return fi; -} - - -/*********************************************************************** - * OM2000 BTS Finite State Machine, initializes CF and all siblings - ***********************************************************************/ - -enum om2k_bts_event { - OM2K_BTS_EVT_START, - OM2K_BTS_EVT_CF_DONE, - OM2K_BTS_EVT_IS_DONE, - OM2K_BTS_EVT_CON_DONE, - OM2K_BTS_EVT_TF_DONE, - OM2K_BTS_EVT_TRX_DONE, - OM2K_BTS_EVT_STOP, -}; - -static const struct value_string om2k_bts_events[] = { - { OM2K_BTS_EVT_START, "START" }, - { OM2K_BTS_EVT_CF_DONE, "CF-DONE" }, - { OM2K_BTS_EVT_IS_DONE, "IS-DONE" }, - { OM2K_BTS_EVT_CON_DONE, "CON-DONE" }, - { OM2K_BTS_EVT_TF_DONE, "TF-DONE" }, - { OM2K_BTS_EVT_TRX_DONE, "TRX-DONE" }, - { OM2K_BTS_EVT_STOP, "STOP" }, - { 0, NULL } -}; - -enum om2k_bts_state { - OM2K_BTS_S_INIT, - OM2K_BTS_S_WAIT_CF, - OM2K_BTS_S_WAIT_IS, - OM2K_BTS_S_WAIT_CON, - OM2K_BTS_S_WAIT_TF, - OM2K_BTS_S_WAIT_TRX, - OM2K_BTS_S_DONE, - OM2K_BTS_S_ERROR, -}; - -struct om2k_bts_fsm_priv { - struct gsm_bts *bts; - uint8_t next_trx_nr; -}; - -static void om2k_bts_s_init(struct osmo_fsm_inst *fi, uint32_t event, void *data) -{ - struct om2k_bts_fsm_priv *obfp = fi->priv; - struct gsm_bts *bts = obfp->bts; - - OSMO_ASSERT(event == OM2K_BTS_EVT_START); - osmo_fsm_inst_state_chg(fi, OM2K_BTS_S_WAIT_CF, - BTS_FSM_TIMEOUT, 0); - om2k_mo_fsm_start(fi, OM2K_BTS_EVT_CF_DONE, bts->c0, - &bts->rbs2000.cf.om2k_mo); -} - -static void om2k_bts_s_wait_cf(struct osmo_fsm_inst *fi, uint32_t event, void *data) -{ - struct om2k_bts_fsm_priv *obfp = fi->priv; - struct gsm_bts *bts = obfp->bts; - - OSMO_ASSERT(event == OM2K_BTS_EVT_CF_DONE); - /* TF can take a long time to initialize, wait for 10min */ - osmo_fsm_inst_state_chg(fi, OM2K_BTS_S_WAIT_TF, 600, 0); - om2k_mo_fsm_start(fi, OM2K_BTS_EVT_TF_DONE, bts->c0, - &bts->rbs2000.tf.om2k_mo); -} - -static void om2k_bts_s_wait_tf(struct osmo_fsm_inst *fi, uint32_t event, void *data) -{ - struct om2k_bts_fsm_priv *obfp = fi->priv; - struct gsm_bts *bts = obfp->bts; - - OSMO_ASSERT(event == OM2K_BTS_EVT_TF_DONE); - - osmo_fsm_inst_state_chg(fi, OM2K_BTS_S_WAIT_CON, - BTS_FSM_TIMEOUT, 0); - om2k_mo_fsm_start(fi, OM2K_BTS_EVT_CON_DONE, bts->c0, - &bts->rbs2000.con.om2k_mo); -} - -static void om2k_bts_s_wait_con(struct osmo_fsm_inst *fi, uint32_t event, void *data) -{ - struct om2k_bts_fsm_priv *obfp = fi->priv; - struct gsm_bts *bts = obfp->bts; - - OSMO_ASSERT(event == OM2K_BTS_EVT_CON_DONE); - - osmo_fsm_inst_state_chg(fi, OM2K_BTS_S_WAIT_IS, - BTS_FSM_TIMEOUT, 0); - om2k_mo_fsm_start(fi, OM2K_BTS_EVT_IS_DONE, bts->c0, - &bts->rbs2000.is.om2k_mo); -} - -static void om2k_bts_s_wait_is(struct osmo_fsm_inst *fi, uint32_t event, void *data) -{ - struct om2k_bts_fsm_priv *obfp = fi->priv; - struct gsm_bts_trx *trx; - - OSMO_ASSERT(event == OM2K_BTS_EVT_IS_DONE); - - osmo_fsm_inst_state_chg(fi, OM2K_BTS_S_WAIT_TRX, - BTS_FSM_TIMEOUT, 0); - obfp->next_trx_nr = 0; - trx = gsm_bts_trx_num(obfp->bts, obfp->next_trx_nr++); - om2k_trx_fsm_start(fi, trx, OM2K_BTS_EVT_TRX_DONE); -} - -static void om2k_bts_s_wait_trx(struct osmo_fsm_inst *fi, uint32_t event, void *data) -{ - struct om2k_bts_fsm_priv *obfp = fi->priv; - - OSMO_ASSERT(event == OM2K_BTS_EVT_TRX_DONE); - - if (obfp->next_trx_nr < obfp->bts->num_trx) { - struct gsm_bts_trx *trx; - trx = gsm_bts_trx_num(obfp->bts, obfp->next_trx_nr++); - om2k_trx_fsm_start(fi, trx, OM2K_BTS_EVT_TRX_DONE); - } else { - osmo_fsm_inst_state_chg(fi, OM2K_BTS_S_DONE, 0, 0); - } -} - -static void om2k_bts_s_done_onenter(struct osmo_fsm_inst *fi, uint32_t prev_state) -{ - osmo_fsm_inst_term(fi, OSMO_FSM_TERM_REGULAR, NULL); -} - -static const struct osmo_fsm_state om2k_bts_states[] = { - [OM2K_BTS_S_INIT] = { - .in_event_mask = S(OM2K_BTS_EVT_START), - .out_state_mask = S(OM2K_BTS_S_WAIT_CF), - .name = "INIT", - .action = om2k_bts_s_init, - }, - [OM2K_BTS_S_WAIT_CF] = { - .in_event_mask = S(OM2K_BTS_EVT_CF_DONE), - .out_state_mask = S(OM2K_BTS_S_ERROR) | - S(OM2K_BTS_S_WAIT_TF), - .name = "WAIT-CF", - .action = om2k_bts_s_wait_cf, - }, - [OM2K_BTS_S_WAIT_TF] = { - .in_event_mask = S(OM2K_BTS_EVT_TF_DONE), - .out_state_mask = S(OM2K_BTS_S_ERROR) | - S(OM2K_BTS_S_WAIT_CON), - .name = "WAIT-TF", - .action = om2k_bts_s_wait_tf, - }, - [OM2K_BTS_S_WAIT_CON] = { - .in_event_mask = S(OM2K_BTS_EVT_CON_DONE), - .out_state_mask = S(OM2K_BTS_S_ERROR) | - S(OM2K_BTS_S_WAIT_IS), - .name = "WAIT-CON", - .action = om2k_bts_s_wait_con, - }, - [OM2K_BTS_S_WAIT_IS] = { - .in_event_mask = S(OM2K_BTS_EVT_IS_DONE), - .out_state_mask = S(OM2K_BTS_S_ERROR) | - S(OM2K_BTS_S_WAIT_TRX), - .name = "WAIT-IS", - .action = om2k_bts_s_wait_is, - }, - [OM2K_BTS_S_WAIT_TRX] = { - .in_event_mask = S(OM2K_BTS_EVT_TRX_DONE), - .out_state_mask = S(OM2K_BTS_S_ERROR) | - S(OM2K_BTS_S_DONE), - .name = "WAIT-TRX", - .action = om2k_bts_s_wait_trx, - }, - [OM2K_BTS_S_DONE] = { - .name = "DONE", - .onenter = om2k_bts_s_done_onenter, - }, - [OM2K_BTS_S_ERROR] = { - .name = "ERROR", - }, -}; - -static int om2k_bts_timer_cb(struct osmo_fsm_inst *fi) -{ - osmo_fsm_inst_state_chg(fi, OM2K_BTS_S_ERROR, 0, 0); - return 0; -} - -static struct osmo_fsm om2k_bts_fsm = { - .name = "OM2000-BTS", - .states = om2k_bts_states, - .num_states = ARRAY_SIZE(om2k_bts_states), - .log_subsys = DNM, - .event_names = om2k_bts_events, - .timer_cb = om2k_bts_timer_cb, -}; - -struct osmo_fsm_inst * -om2k_bts_fsm_start(struct gsm_bts *bts) -{ - struct osmo_fsm_inst *fi; - struct om2k_bts_fsm_priv *obfp; - char idbuf[16]; - - snprintf(idbuf, sizeof(idbuf), "%u", bts->nr); - - fi = osmo_fsm_inst_alloc(&om2k_bts_fsm, bts, NULL, - LOGL_DEBUG, idbuf); - if (!fi) - return NULL; - fi->priv = obfp = talloc_zero(fi, struct om2k_bts_fsm_priv); - obfp->bts = bts; - - osmo_fsm_inst_dispatch(fi, OM2K_BTS_EVT_START, NULL); - - return fi; -} - - -/*********************************************************************** - * OM2000 Negotiation - ***********************************************************************/ - -static int abis_om2k_tx_negot_req_ack(struct gsm_bts *bts, const struct abis_om2k_mo *mo, - uint8_t *data, unsigned int len) -{ - struct msgb *msg = om2k_msgb_alloc(); - struct abis_om2k_hdr *o2k; - - o2k = (struct abis_om2k_hdr *) msgb_put(msg, sizeof(*o2k)); - fill_om2k_hdr(o2k, mo, OM2K_MSGT_NEGOT_REQ_ACK); - - msgb_tlv_put(msg, OM2K_DEI_NEGOT_REC2, len, data); - - DEBUGP(DNM, "Tx MO=%s %s\n", om2k_mo_name(mo), - get_value_string(om2k_msgcode_vals, OM2K_MSGT_NEGOT_REQ_ACK)); - - return abis_om2k_sendmsg(bts, msg); -} - -struct iwd_version { - uint8_t gen_char[3+1]; - uint8_t rev_char[3+1]; -}; - -struct iwd_type { - uint8_t num_vers; - struct iwd_version v[8]; -}; - -static int om2k_rx_negot_req(struct msgb *msg) -{ - struct e1inp_sign_link *sign_link = (struct e1inp_sign_link *)msg->dst; - struct abis_om2k_hdr *o2h = msgb_l2(msg); - struct iwd_type iwd_types[16]; - uint8_t num_iwd_types = o2h->data[2]; - uint8_t *cur = o2h->data+3; - unsigned int i, v; - - uint8_t out_buf[1024]; - uint8_t *out_cur = out_buf+1; - uint8_t out_num_types = 0; - - memset(iwd_types, 0, sizeof(iwd_types)); - - /* Parse the RBS-supported IWD versions into iwd_types array */ - for (i = 0; i < num_iwd_types; i++) { - uint8_t num_versions = *cur++; - uint8_t iwd_type = *cur++; - - iwd_types[iwd_type].num_vers = num_versions; - - for (v = 0; v < num_versions; v++) { - struct iwd_version *iwd_v = &iwd_types[iwd_type].v[v]; - - memcpy(iwd_v->gen_char, cur, 3); - cur += 3; - memcpy(iwd_v->rev_char, cur, 3); - cur += 3; - - DEBUGP(DNM, "\tIWD Type %u Gen %s Rev %s\n", iwd_type, - iwd_v->gen_char, iwd_v->rev_char); - } - } - - /* Select the last version for each IWD type */ - for (i = 0; i < ARRAY_SIZE(iwd_types); i++) { - struct iwd_type *type = &iwd_types[i]; - struct iwd_version *last_v; - - if (type->num_vers == 0) - continue; - - out_num_types++; - - last_v = &type->v[type->num_vers-1]; - - *out_cur++ = i; - memcpy(out_cur, last_v->gen_char, 3); - out_cur += 3; - memcpy(out_cur, last_v->rev_char, 3); - out_cur += 3; - } - - out_buf[0] = out_num_types; - - return abis_om2k_tx_negot_req_ack(sign_link->trx->bts, &o2h->mo, out_buf, out_cur - out_buf); -} - - -/*********************************************************************** - * OM2000 Receive Message Handler - ***********************************************************************/ - -static int om2k_rx_nack(struct msgb *msg) -{ - struct abis_om2k_hdr *o2h = msgb_l2(msg); - uint16_t msg_type = ntohs(o2h->msg_type); - struct tlv_parsed tp; - - LOGP(DNM, LOGL_ERROR, "Rx MO=%s %s", om2k_mo_name(&o2h->mo), - get_value_string(om2k_msgcode_vals, msg_type)); - - abis_om2k_msg_tlv_parse(&tp, o2h); - if (TLVP_PRESENT(&tp, OM2K_DEI_REASON_CODE)) - LOGPC(DNM, LOGL_ERROR, ", Reason 0x%02x", - *TLVP_VAL(&tp, OM2K_DEI_REASON_CODE)); - - if (TLVP_PRESENT(&tp, OM2K_DEI_RESULT_CODE)) - LOGPC(DNM, LOGL_ERROR, ", Result %s", - get_value_string(om2k_result_strings, - *TLVP_VAL(&tp, OM2K_DEI_RESULT_CODE))); - LOGPC(DNM, LOGL_ERROR, "\n"); - - return 0; -} - -static int process_mo_state(struct gsm_bts *bts, struct om2k_decoded_msg *odm) -{ - uint8_t mo_state; - - if (!TLVP_PRESENT(&odm->tp, OM2K_DEI_MO_STATE)) - return -EIO; - mo_state = *TLVP_VAL(&odm->tp, OM2K_DEI_MO_STATE); - - LOGP(DNM, LOGL_DEBUG, "Rx MO=%s %s, MO State: %s\n", - om2k_mo_name(&odm->o2h.mo), - get_value_string(om2k_msgcode_vals, odm->msg_type), - get_value_string(om2k_mostate_vals, mo_state)); - - /* Throw error message in case we see an enable rsponse that does - * not yield an enabled mo-state */ - if (odm->msg_type == OM2K_MSGT_ENABLE_RES - && mo_state != OM2K_MO_S_ENABLED) { - LOGP(DNM, LOGL_ERROR, - "Rx MO=%s %s Failed to enable MO State!\n", - om2k_mo_name(&odm->o2h.mo), - get_value_string(om2k_msgcode_vals, odm->msg_type)); - } - - update_mo_state(bts, &odm->o2h.mo, mo_state); - - return 0; -} - -/* Display fault report bits (helper function of display_fault_maps()) */ -static bool display_fault_bits(const uint8_t *vect, uint16_t len, - uint8_t dei, const struct abis_om2k_mo *mo) -{ - uint16_t i; - int k; - bool faults_present = false; - int first = 1; - char string[255]; - - /* Check if errors are present at all */ - for (i = 0; i < len; i++) - if (vect[i]) - faults_present = true; - if (!faults_present) - return false; - - sprintf(string, "Fault Report: %s (", - get_value_string(om2k_attr_vals, dei)); - - for (i = 0; i < len; i++) { - for (k = 0; k < 8; k++) { - if ((vect[i] >> k) & 1) { - if (!first) - sprintf(string + strlen(string), ","); - sprintf(string + strlen(string), "%d", k + i*8); - first = 0; - } - } - } - - sprintf(string + strlen(string), ")\n"); - DEBUGP(DNM, "Rx MO=%s %s", om2k_mo_name(mo), string); - - return true; -} - -/* Display fault report maps */ -static void display_fault_maps(const uint8_t *src, unsigned int src_len, - const struct abis_om2k_mo *mo) -{ - uint8_t tag; - uint16_t tag_len; - const uint8_t *val; - int src_pos = 0; - int rc; - int tlv_count = 0; - uint16_t msg_code; - bool faults_present = false; - - /* Chop off header */ - src+=4; - src_len-=4; - - /* Check message type */ - msg_code = (*src & 0xff) << 8; - src++; - src_len--; - msg_code |= (*src & 0xff); - src++; - src_len--; - if (msg_code != OM2K_MSGT_FAULT_REP) { - LOGP(DNM, LOGL_ERROR, "Rx MO=%s Fault report: invalid message code!\n", - om2k_mo_name(mo)); - return; - } - - /* Chop off mo-interface */ - src += 4; - src_len -= 4; - - /* Iterate over each TLV element */ - while (1) { - - /* Bail if an the maximum number of TLV fields - * have been parsed */ - if (tlv_count >= 11) { - LOGP(DNM, LOGL_ERROR, - "Rx MO=%s Fault Report: too many tlv elements!\n", - om2k_mo_name(mo)); - return; - } - - /* Parse TLV field */ - rc = tlv_parse_one(&tag, &tag_len, &val, &om2k_att_tlvdef, - src + src_pos, src_len - src_pos); - if (rc > 0) - src_pos += rc; - else { - LOGP(DNM, LOGL_ERROR, - "Rx MO=%s Fault Report: invalid tlv element!\n", - om2k_mo_name(mo)); - return; - } - - switch (tag) { - case OM2K_DEI_INT_FAULT_MAP_1A: - case OM2K_DEI_INT_FAULT_MAP_1B: - case OM2K_DEI_INT_FAULT_MAP_2A: - case OM2K_DEI_EXT_COND_MAP_1: - case OM2K_DEI_EXT_COND_MAP_2: - case OM2K_DEI_REPL_UNIT_MAP: - case OM2K_DEI_INT_FAULT_MAP_2A_EXT: - case OM2K_DEI_EXT_COND_MAP_2_EXT: - case OM2K_DEI_REPL_UNIT_MAP_EXT: - faults_present |= display_fault_bits(val, tag_len, - tag, mo); - break; - } - - /* Stop when no further TLV elements can be expected */ - if (src_len - src_pos < 2) - break; - - tlv_count++; - } - - if (!faults_present) { - DEBUGP(DNM, "Rx MO=%s Fault Report: All faults ceased!\n", - om2k_mo_name(mo)); - } -} - -int abis_om2k_rcvmsg(struct msgb *msg) -{ - struct e1inp_sign_link *sign_link = (struct e1inp_sign_link *)msg->dst; - struct gsm_bts *bts = sign_link->trx->bts; - struct abis_om2k_hdr *o2h = msgb_l2(msg); - struct abis_om_hdr *oh = &o2h->om; - uint16_t msg_type = ntohs(o2h->msg_type); - struct om2k_decoded_msg odm; - struct om2k_mo *mo; - int rc = 0; - - /* Various consistency checks */ - if (oh->placement != ABIS_OM_PLACEMENT_ONLY) { - LOGP(DNM, LOGL_ERROR, "ABIS OML placement 0x%x not supported\n", - oh->placement); - if (oh->placement != ABIS_OM_PLACEMENT_FIRST) - return -EINVAL; - } - if (oh->sequence != 0) { - LOGP(DNM, LOGL_ERROR, "ABIS OML sequence 0x%x != 0x00\n", - oh->sequence); - return -EINVAL; - } - - msg->l3h = (unsigned char *)o2h + sizeof(*o2h); - - if (oh->mdisc != ABIS_OM_MDISC_FOM) { - LOGP(DNM, LOGL_ERROR, "unknown ABIS OM2000 message discriminator 0x%x\n", - oh->mdisc); - return -EINVAL; - } - - DEBUGP(DNM, "Rx MO=%s %s (%s)\n", om2k_mo_name(&o2h->mo), - get_value_string(om2k_msgcode_vals, msg_type), - osmo_hexdump(msg->l2h, msgb_l2len(msg))); - - om2k_decode_msg(&odm, msg); - - process_mo_state(bts, &odm); - - switch (msg_type) { - case OM2K_MSGT_CAL_TIME_REQ: - rc = abis_om2k_cal_time_resp(bts); - break; - case OM2K_MSGT_FAULT_REP: - display_fault_maps(msg->l2h, msgb_l2len(msg), &o2h->mo); - rc = abis_om2k_tx_simple(bts, &o2h->mo, OM2K_MSGT_FAULT_REP_ACK); - break; - case OM2K_MSGT_NEGOT_REQ: - rc = om2k_rx_negot_req(msg); - break; - case OM2K_MSGT_START_RES: - /* common processing here */ - rc = abis_om2k_tx_simple(bts, &o2h->mo, OM2K_MSGT_START_RES_ACK); - /* below we dispatch into MO */ - break; - case OM2K_MSGT_IS_CONF_RES: - rc = abis_om2k_tx_simple(bts, &o2h->mo, OM2K_MSGT_IS_CONF_RES_ACK); - break; - case OM2K_MSGT_CON_CONF_RES: - rc = abis_om2k_tx_simple(bts, &o2h->mo, OM2K_MSGT_CON_CONF_RES_ACK); - break; - case OM2K_MSGT_TX_CONF_RES: - rc = abis_om2k_tx_simple(bts, &o2h->mo, OM2K_MSGT_TX_CONF_RES_ACK); - break; - case OM2K_MSGT_RX_CONF_RES: - rc = abis_om2k_tx_simple(bts, &o2h->mo, OM2K_MSGT_RX_CONF_RES_ACK); - break; - case OM2K_MSGT_TS_CONF_RES: - rc = abis_om2k_tx_simple(bts, &o2h->mo, OM2K_MSGT_TS_CONF_RES_ACK); - break; - case OM2K_MSGT_TF_CONF_RES: - rc = abis_om2k_tx_simple(bts, &o2h->mo, OM2K_MSGT_TF_CONF_RES_ACK); - break; - case OM2K_MSGT_ENABLE_RES: - rc = abis_om2k_tx_simple(bts, &o2h->mo, OM2K_MSGT_ENABLE_RES_ACK); - break; - case OM2K_MSGT_DISABLE_RES: - rc = abis_om2k_tx_simple(bts, &o2h->mo, OM2K_MSGT_DISABLE_RES_ACK); - break; - case OM2K_MSGT_TEST_RES: - rc = abis_om2k_tx_simple(bts, &o2h->mo, OM2K_MSGT_TEST_RES_ACK); - break; - case OM2K_MSGT_CAPA_RES: - rc = abis_om2k_tx_simple(bts, &o2h->mo, OM2K_MSGT_CAPA_RES_ACK); - break; - /* ERrors */ - case OM2K_MSGT_START_REQ_REJ: - case OM2K_MSGT_CONNECT_REJ: - case OM2K_MSGT_OP_INFO_REJ: - case OM2K_MSGT_DISCONNECT_REJ: - case OM2K_MSGT_TEST_REQ_REJ: - case OM2K_MSGT_CON_CONF_REQ_REJ: - case OM2K_MSGT_IS_CONF_REQ_REJ: - case OM2K_MSGT_TX_CONF_REQ_REJ: - case OM2K_MSGT_RX_CONF_REQ_REJ: - case OM2K_MSGT_TS_CONF_REQ_REJ: - case OM2K_MSGT_TF_CONF_REQ_REJ: - case OM2K_MSGT_ENABLE_REQ_REJ: - case OM2K_MSGT_ALARM_STATUS_REQ_REJ: - case OM2K_MSGT_DISABLE_REQ_REJ: - rc = om2k_rx_nack(msg); - break; - } - - /* Resolve the MO for this message */ - mo = get_om2k_mo(bts, &o2h->mo); - if (!mo) { - LOGP(DNM, LOGL_ERROR, "Couldn't resolve MO for OM2K msg " - "%s: %s\n", get_value_string(om2k_msgcode_vals, msg_type), - msgb_hexdump(msg)); - return 0; - } - if (!mo->fsm) { - LOGP(DNM, LOGL_ERROR, "MO object should not generate any message. fsm == NULL " - "%s: %s\n", get_value_string(om2k_msgcode_vals, msg_type), - msgb_hexdump(msg)); - return 0; - } - - /* Dispatch message to that MO */ - om2k_mo_fsm_recvmsg(bts, mo, &odm); - - msgb_free(msg); - return rc; -} - -static void om2k_mo_init(struct om2k_mo *mo, uint8_t class, - uint8_t bts_nr, uint8_t assoc_so, uint8_t inst) -{ - mo->addr.class = class; - mo->addr.bts = bts_nr; - mo->addr.assoc_so = assoc_so; - mo->addr.inst = inst; -} - -/* initialize the OM2K_MO members of gsm_bts_trx and its timeslots */ -void abis_om2k_trx_init(struct gsm_bts_trx *trx) -{ - struct gsm_bts *bts = trx->bts; - unsigned int i; - - OSMO_ASSERT(bts->type == GSM_BTS_TYPE_RBS2000); - - om2k_mo_init(&trx->rbs2000.trxc.om2k_mo, OM2K_MO_CLS_TRXC, - bts->nr, 255, trx->nr); - om2k_mo_init(&trx->rbs2000.tx.om2k_mo, OM2K_MO_CLS_TX, - bts->nr, 255, trx->nr); - om2k_mo_init(&trx->rbs2000.rx.om2k_mo, OM2K_MO_CLS_RX, - bts->nr, 255, trx->nr); - - for (i = 0; i < ARRAY_SIZE(trx->ts); i++) { - om2k_mo_init(&trx->ts[i].rbs2000.om2k_mo, OM2K_MO_CLS_TS, - bts->nr, trx->nr, i); - } -} - -/* initialize the OM2K_MO members of gsm_bts */ -void abis_om2k_bts_init(struct gsm_bts *bts) -{ - OSMO_ASSERT(bts->type == GSM_BTS_TYPE_RBS2000); - - om2k_mo_init(&bts->rbs2000.cf.om2k_mo, OM2K_MO_CLS_CF, - bts->nr, 0xFF, 0); - om2k_mo_init(&bts->rbs2000.is.om2k_mo, OM2K_MO_CLS_IS, - bts->nr, 0xFF, 0); - om2k_mo_init(&bts->rbs2000.con.om2k_mo, OM2K_MO_CLS_CON, - bts->nr, 0xFF, 0); - om2k_mo_init(&bts->rbs2000.dp.om2k_mo, OM2K_MO_CLS_DP, - bts->nr, 0xFF, 0); - om2k_mo_init(&bts->rbs2000.tf.om2k_mo, OM2K_MO_CLS_TF, - bts->nr, 0xFF, 0); -} - -static __attribute__((constructor)) void abis_om2k_init(void) -{ - osmo_fsm_register(&om2k_mo_fsm); - osmo_fsm_register(&om2k_bts_fsm); - osmo_fsm_register(&om2k_trx_fsm); -} diff --git a/openbsc/src/libbsc/abis_om2000_vty.c b/openbsc/src/libbsc/abis_om2000_vty.c deleted file mode 100644 index a6bc4c78..00000000 --- a/openbsc/src/libbsc/abis_om2000_vty.c +++ /dev/null @@ -1,609 +0,0 @@ -/* VTY interface for A-bis OM2000 */ - -/* (C) 2010-2011 by Harald Welte - * - * All Rights Reserved - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation; either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public 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, -}; - -static struct cmd_node om2k_con_group_node = { - OM2K_CON_GROUP_NODE, - "%s(om2k-con-group)# ", - 1, -}; - -struct con_group; - -struct oml_node_state { - struct gsm_bts *bts; - struct abis_om2k_mo mo; - struct con_group *cg; -}; - -static int dummy_config_write(struct vty *v) -{ - return CMD_SUCCESS; -} - -/* FIXME: auto-generate those strings from the value_string lists */ -#define OM2K_OBJCLASS_VTY "(trxc|ts|tf|is|con|dp|cf|tx|rx)" -#define OM2K_OBJCLASS_VTY_HELP "TRX Controller\n" \ - "Timeslot\n" \ - "Timing Function\n" \ - "Interface Switch\n" \ - "Abis Concentrator\n" \ - "Digital Path\n" \ - "Central Function\n" \ - "Transmitter\n" \ - "Receiver\n" - -DEFUN(om2k_class_inst, om2k_class_inst_cmd, - "bts <0-255> om2000 class " OM2K_OBJCLASS_VTY - " <0-255> <0-255> <0-255>", - "BTS related commands\n" "BTS Number\n" - "Manipulate the OM2000 managed objects\n" - "Object Class\n" OM2K_OBJCLASS_VTY_HELP - "BTS Number\n" "Associated SO Instance\n" "Instance Number\n") -{ - struct gsm_bts *bts; - struct oml_node_state *oms; - int bts_nr = atoi(argv[0]); - - bts = gsm_bts_num(gsmnet_from_vty(vty), bts_nr); - if (!bts) { - vty_out(vty, "%% No such BTS (%d)%s", bts_nr, VTY_NEWLINE); - return CMD_WARNING; - } - - if (bts->type != GSM_BTS_TYPE_RBS2000) { - vty_out(vty, "%% BTS %d not an Ericsson RBS%s", - bts_nr, VTY_NEWLINE); - return CMD_WARNING; - } - - oms = talloc_zero(tall_bsc_ctx, struct oml_node_state); - if (!oms) - return CMD_WARNING; - - oms->bts = bts; - oms->mo.class = get_string_value(om2k_mo_class_short_vals, argv[1]); - oms->mo.bts = atoi(argv[2]); - oms->mo.assoc_so = atoi(argv[3]); - oms->mo.inst = atoi(argv[4]); - - vty->index = oms; - vty->node = OM2K_NODE; - - return CMD_SUCCESS; - -} - -DEFUN(om2k_classnum_inst, om2k_classnum_inst_cmd, - "bts <0-255> om2000 class <0-255> <0-255> <0-255> <0-255>", - "BTS related commands\n" "BTS Number\n" - "Manipulate the OML managed objects\n" - "Object Class\n" "Object Class\n" - "BTS Number\n" "Associated SO Instance\n" "Instance Number\n") -{ - struct gsm_bts *bts; - struct oml_node_state *oms; - int bts_nr = atoi(argv[0]); - - bts = gsm_bts_num(gsmnet_from_vty(vty), bts_nr); - if (!bts) { - vty_out(vty, "%% No such BTS (%d)%s", bts_nr, VTY_NEWLINE); - return CMD_WARNING; - } - - oms = talloc_zero(tall_bsc_ctx, struct oml_node_state); - if (!oms) - return CMD_WARNING; - - oms->bts = bts; - oms->mo.class = atoi(argv[1]); - oms->mo.bts = atoi(argv[2]); - oms->mo.assoc_so = atoi(argv[3]); - oms->mo.inst = atoi(argv[4]); - - vty->index = oms; - vty->node = OM2K_NODE; - - return CMD_SUCCESS; -} - -DEFUN(om2k_reset, om2k_reset_cmd, - "reset-command", - "Reset the MO\n") -{ - struct oml_node_state *oms = vty->index; - - abis_om2k_tx_reset_cmd(oms->bts, &oms->mo); - return CMD_SUCCESS; -} - -DEFUN(om2k_start, om2k_start_cmd, - "start-request", - "Start the MO\n") -{ - struct oml_node_state *oms = vty->index; - - abis_om2k_tx_start_req(oms->bts, &oms->mo); - return CMD_SUCCESS; -} - -DEFUN(om2k_status, om2k_status_cmd, - "status-request", - "Get the MO Status\n") -{ - struct oml_node_state *oms = vty->index; - - abis_om2k_tx_status_req(oms->bts, &oms->mo); - return CMD_SUCCESS; -} - -DEFUN(om2k_connect, om2k_connect_cmd, - "connect-command", - "Connect the MO\n") -{ - struct oml_node_state *oms = vty->index; - - abis_om2k_tx_connect_cmd(oms->bts, &oms->mo); - return CMD_SUCCESS; -} - -DEFUN(om2k_disconnect, om2k_disconnect_cmd, - "disconnect-command", - "Disconnect the MO\n") -{ - struct oml_node_state *oms = vty->index; - - abis_om2k_tx_disconnect_cmd(oms->bts, &oms->mo); - return CMD_SUCCESS; -} - -DEFUN(om2k_enable, om2k_enable_cmd, - "enable-request", - "Enable the MO\n") -{ - struct oml_node_state *oms = vty->index; - - abis_om2k_tx_enable_req(oms->bts, &oms->mo); - return CMD_SUCCESS; -} - -DEFUN(om2k_disable, om2k_disable_cmd, - "disable-request", - "Disable the MO\n") -{ - struct oml_node_state *oms = vty->index; - - abis_om2k_tx_disable_req(oms->bts, &oms->mo); - return CMD_SUCCESS; -} - -DEFUN(om2k_op_info, om2k_op_info_cmd, - "operational-info <0-1>", - "Set operational information\n" - "Set operational info to 0 or 1\n") -{ - struct oml_node_state *oms = vty->index; - int oper = atoi(argv[0]); - - abis_om2k_tx_op_info(oms->bts, &oms->mo, oper); - return CMD_SUCCESS; -} - -DEFUN(om2k_test, om2k_test_cmd, - "test-request", - "Test the MO\n") -{ - struct oml_node_state *oms = vty->index; - - abis_om2k_tx_test_req(oms->bts, &oms->mo); - return CMD_SUCCESS; -} - -DEFUN(om2k_cap_req, om2k_cap_req_cmd, - "capabilities-request", - "Request MO capabilities\n") -{ - struct oml_node_state *oms = vty->index; - - abis_om2k_tx_cap_req(oms->bts, &oms->mo); - return CMD_SUCCESS; -} - -static struct con_group *con_group_find_or_create(struct gsm_bts *bts, uint8_t cg) -{ - struct con_group *ent; - - llist_for_each_entry(ent, &bts->rbs2000.con.conn_groups, list) { - if (ent->cg == cg) - return ent; - } - - ent = talloc_zero(bts, struct con_group); - ent->bts = bts; - ent->cg = cg; - INIT_LLIST_HEAD(&ent->paths); - llist_add_tail(&ent->list, &bts->rbs2000.con.conn_groups); - - return ent; -} - -static int con_group_del(struct gsm_bts *bts, uint8_t cg_id) -{ - struct con_group *cg, *cg2; - - llist_for_each_entry_safe(cg, cg2, &bts->rbs2000.con.conn_groups, list) { - if (cg->cg == cg_id) { - llist_del(&cg->list); - talloc_free(cg); - return 0; - }; - } - return -ENOENT; -} - -static void con_group_add_path(struct con_group *cg, uint16_t ccp, - uint8_t ci, uint8_t tag, uint8_t tei) -{ - struct con_path *cp = talloc_zero(cg, struct con_path); - - cp->ccp = ccp; - cp->ci = ci; - cp->tag = tag; - cp->tei = tei; - llist_add(&cp->list, &cg->paths); -} - -static int con_group_del_path(struct con_group *cg, uint16_t ccp, - uint8_t ci, uint8_t tag, uint8_t tei) -{ - struct con_path *cp, *cp2; - llist_for_each_entry_safe(cp, cp2, &cg->paths, list) { - if (cp->ccp == ccp && cp->ci == ci && cp->tag == tag && - cp->tei == tei) { - llist_del(&cp->list); - talloc_free(cp); - return 0; - } - } - return -ENOENT; -} - -DEFUN(cfg_om2k_con_group, cfg_om2k_con_group_cmd, - "con-connection-group <1-31>", - "Configure a CON (Concentrator) Connection Group\n" - "CON Connection Group Number\n") -{ - struct gsm_bts *bts = vty->index; - struct con_group *cg; - uint8_t cgid = atoi(argv[0]); - - if (bts->type != GSM_BTS_TYPE_RBS2000) { - vty_out(vty, "%% CON MO only exists in RBS2000%s", - VTY_NEWLINE); - return CMD_WARNING; - } - - cg = con_group_find_or_create(bts, cgid); - if (!cg) { - vty_out(vty, "%% Cannot create CON Group%s", - VTY_NEWLINE); - return CMD_WARNING; - } - - vty->node = OM2K_CON_GROUP_NODE; - vty->index = cg; - - return CMD_SUCCESS; -} - -DEFUN(del_om2k_con_group, del_om2k_con_group_cmd, - "del-connection-group <1-31>", - "Delete a CON (Concentrator) Connection Group\n" - "CON Connection Group Number\n") -{ - struct gsm_bts *bts = vty->index; - int rc; - uint8_t cgid = atoi(argv[0]); - - if (bts->type != GSM_BTS_TYPE_RBS2000) { - vty_out(vty, "%% CON MO only exists in RBS2000%s", - VTY_NEWLINE); - return CMD_WARNING; - } - - rc = con_group_del(bts, cgid); - if (rc != 0) { - vty_out(vty, "%% Cannot delete CON Group%s", - VTY_NEWLINE); - return CMD_WARNING; - } - - return CMD_SUCCESS; -} - -#define CON_PATH_HELP "CON Path (In/Out)\n" \ - "Add CON Path to Concentration Group\n" \ - "Delete CON Path from Concentration Group\n" \ - "CON Conection Point\n" \ - "Contiguity Index\n" \ - -DEFUN(cfg_om2k_con_path_dec, cfg_om2k_con_path_dec_cmd, - "con-path (add|del) <0-2047> <0-255> deconcentrated <0-63>", - CON_PATH_HELP "De-concentrated in/outlet\n" "TEI Value\n") -{ - struct con_group *cg = vty->index; - uint16_t ccp = atoi(argv[1]); - uint8_t ci = atoi(argv[2]); - uint8_t tei = atoi(argv[3]); - - if (!strcmp(argv[0], "add")) - con_group_add_path(cg, ccp, ci, 0, tei); - else { - if (con_group_del_path(cg, ccp, ci, 0, tei) < 0) { - vty_out(vty, "%% No matching CON Path%s", - VTY_NEWLINE); - return CMD_WARNING; - } - } - - return CMD_SUCCESS; -} - -DEFUN(cfg_om2k_con_path_conc, cfg_om2k_con_path_conc_cmd, - "con-path (add|del) <0-2047> <0-255> concentrated <1-16>", - CON_PATH_HELP "Concentrated in/outlet\n" "Tag Number\n") -{ - struct con_group *cg = vty->index; - uint16_t ccp = atoi(argv[1]); - uint8_t ci = atoi(argv[2]); - uint8_t tag = atoi(argv[3]); - - if (!strcmp(argv[0], "add")) - con_group_add_path(cg, ccp, ci, tag, 0xff); - else { - if (con_group_del_path(cg, ccp, ci, tag, 0xff) < 0) { - vty_out(vty, "%% No matching CON list entry%s", - VTY_NEWLINE); - return CMD_WARNING; - } - } - - return CMD_SUCCESS; -} - -DEFUN(cfg_bts_alt_mode, cfg_bts_alt_mode_cmd, - "abis-lower-transport (single-timeslot|super-channel)", - "Configure thee Abis Lower Transport\n" - "Single Timeslot (classic Abis)\n" - "SuperChannel (Packet Abis)\n") -{ - struct gsm_bts *bts = vty->index; - struct con_group *cg; - - if (bts->type != GSM_BTS_TYPE_RBS2000) { - vty_out(vty, "%% Command only works for RBS2000%s", - VTY_NEWLINE); - return CMD_WARNING; - } - - if (!strcmp(argv[0], "super-channel")) - bts->rbs2000.use_superchannel = 1; - else - bts->rbs2000.use_superchannel = 0; - - return CMD_SUCCESS; -} - -DEFUN(cfg_bts_is_conn_list, cfg_bts_is_conn_list_cmd, - "is-connection-list (add|del) <0-2047> <0-2047> <0-255>", - "Interface Switch Connection List\n" - "Add to IS list\n" "Delete from IS list\n" - "ICP1\n" "ICP2\n" "Contiguity Index\n") -{ - struct gsm_bts *bts = vty->index; - uint16_t icp1 = atoi(argv[1]); - uint16_t icp2 = atoi(argv[2]); - uint8_t ci = atoi(argv[3]); - struct is_conn_group *grp, *grp2; - - if (bts->type != GSM_BTS_TYPE_RBS2000) { - vty_out(vty, "%% IS MO only exists in RBS2000%s", - VTY_NEWLINE); - return CMD_WARNING; - } - - if (!strcmp(argv[0], "add")) { - grp = talloc_zero(bts, struct is_conn_group); - grp->icp1 = icp1; - grp->icp2 = icp2; - grp->ci = ci; - llist_add_tail(&grp->list, &bts->rbs2000.is.conn_groups); - } else { - llist_for_each_entry_safe(grp, grp2, &bts->rbs2000.is.conn_groups, list) { - if (grp->icp1 == icp1 && grp->icp2 == icp2 - && grp->ci == ci) { - llist_del(&grp->list); - talloc_free(grp); - return CMD_SUCCESS; - } - } - vty_out(vty, "%% No matching IS Conn Group found!%s", - VTY_NEWLINE); - return CMD_WARNING; - } - - return CMD_SUCCESS; -} - - -DEFUN(om2k_conf_req, om2k_conf_req_cmd, - "configuration-request", - "Send the configuration request for current MO\n") -{ - struct oml_node_state *oms = vty->index; - struct gsm_bts *bts = oms->bts; - struct gsm_bts_trx *trx = NULL; - struct gsm_bts_trx_ts *ts = NULL; - - switch (oms->mo.class) { - case OM2K_MO_CLS_IS: - abis_om2k_tx_is_conf_req(bts); - break; - case OM2K_MO_CLS_TS: - trx = gsm_bts_trx_by_nr(bts, oms->mo.assoc_so); - if (!trx) { - vty_out(vty, "%% BTS %u has no TRX %u%s", bts->nr, - oms->mo.assoc_so, VTY_NEWLINE); - return CMD_WARNING; - } - if (oms->mo.inst >= ARRAY_SIZE(trx->ts)) { - vty_out(vty, "%% Timeslot %u out of range%s", - oms->mo.inst, VTY_NEWLINE); - return CMD_WARNING; - } - ts = &trx->ts[oms->mo.inst]; - abis_om2k_tx_ts_conf_req(ts); - break; - case OM2K_MO_CLS_RX: - case OM2K_MO_CLS_TX: - case OM2K_MO_CLS_TRXC: - trx = gsm_bts_trx_by_nr(bts, oms->mo.inst); - if (!trx) { - vty_out(vty, "%% BTS %u has no TRX %u%s", bts->nr, - oms->mo.inst, VTY_NEWLINE); - return CMD_WARNING; - } - switch (oms->mo.class) { - case OM2K_MO_CLS_RX: - abis_om2k_tx_rx_conf_req(trx); - break; - case OM2K_MO_CLS_TX: - abis_om2k_tx_tx_conf_req(trx); - break; - default: - break; - } - break; - case OM2K_MO_CLS_TF: - abis_om2k_tx_tf_conf_req(bts); - break; - default: - vty_out(vty, "%% Don't know how to configure MO%s", - VTY_NEWLINE); - } - - return CMD_SUCCESS; -} - -static void dump_con_group(struct vty *vty, struct con_group *cg) -{ - struct con_path *cp; - - llist_for_each_entry(cp, &cg->paths, list) { - vty_out(vty, " con-path add %u %u ", cp->ccp, cp->ci); - if (cp->tei == 0xff) { - vty_out(vty, "concentrated %u%s", cp->tag, - VTY_NEWLINE); - } else { - vty_out(vty, "deconcentrated %u%s", cp->tei, - VTY_NEWLINE); - } - } -} - -void abis_om2k_config_write_bts(struct vty *vty, struct gsm_bts *bts) -{ - struct is_conn_group *igrp; - struct con_group *cgrp; - - llist_for_each_entry(igrp, &bts->rbs2000.is.conn_groups, list) - vty_out(vty, " is-connection-list add %u %u %u%s", - igrp->icp1, igrp->icp2, igrp->ci, VTY_NEWLINE); - - llist_for_each_entry(cgrp, &bts->rbs2000.con.conn_groups, list) { - vty_out(vty, " con-connection-group %u%s", cgrp->cg, - VTY_NEWLINE); - dump_con_group(vty, cgrp); - } - if (bts->rbs2000.use_superchannel) - vty_out(vty, " abis-lower-transport super-channel%s", - VTY_NEWLINE); -} - -int abis_om2k_vty_init(void) -{ - install_element(ENABLE_NODE, &om2k_class_inst_cmd); - install_element(ENABLE_NODE, &om2k_classnum_inst_cmd); - install_node(&om2k_node, dummy_config_write); - - vty_install_default(OM2K_NODE); - install_element(OM2K_NODE, &om2k_reset_cmd); - install_element(OM2K_NODE, &om2k_start_cmd); - install_element(OM2K_NODE, &om2k_status_cmd); - install_element(OM2K_NODE, &om2k_connect_cmd); - install_element(OM2K_NODE, &om2k_disconnect_cmd); - install_element(OM2K_NODE, &om2k_enable_cmd); - install_element(OM2K_NODE, &om2k_disable_cmd); - install_element(OM2K_NODE, &om2k_op_info_cmd); - install_element(OM2K_NODE, &om2k_test_cmd); - install_element(OM2K_NODE, &om2k_cap_req_cmd); - install_element(OM2K_NODE, &om2k_conf_req_cmd); - - install_node(&om2k_con_group_node, dummy_config_write); - vty_install_default(OM2K_CON_GROUP_NODE); - install_element(OM2K_CON_GROUP_NODE, &cfg_om2k_con_path_dec_cmd); - install_element(OM2K_CON_GROUP_NODE, &cfg_om2k_con_path_conc_cmd); - - install_element(BTS_NODE, &cfg_bts_is_conn_list_cmd); - install_element(BTS_NODE, &cfg_bts_alt_mode_cmd); - install_element(BTS_NODE, &cfg_om2k_con_group_cmd); - install_element(BTS_NODE, &del_om2k_con_group_cmd); - - return 0; -} diff --git a/openbsc/src/libbsc/abis_rsl.c b/openbsc/src/libbsc/abis_rsl.c deleted file mode 100644 index 6ae790f6..00000000 --- a/openbsc/src/libbsc/abis_rsl.c +++ /dev/null @@ -1,2927 +0,0 @@ -/* GSM Radio Signalling Link messages on the A-bis interface - * 3GPP TS 08.58 version 8.6.0 Release 1999 / ETSI TS 100 596 V8.6.0 */ - -/* (C) 2008-2010 by Harald Welte - * (C) 2012 by Holger Hans Peter Freyther - * - * All Rights Reserved - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation; either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - * - */ - -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#define RSL_ALLOC_SIZE 1024 -#define RSL_ALLOC_HEADROOM 128 - -enum sacch_deact { - SACCH_NONE, - SACCH_DEACTIVATE, -}; - -static int rsl_send_imm_assignment(struct gsm_lchan *lchan); -static void error_timeout_cb(void *data); -static int dyn_ts_switchover_continue(struct gsm_bts_trx_ts *ts); -static int dyn_ts_switchover_failed(struct gsm_bts_trx_ts *ts, int rc); -static void dyn_ts_switchover_complete(struct gsm_lchan *lchan); - -static void send_lchan_signal(int sig_no, struct gsm_lchan *lchan, - struct gsm_meas_rep *resp) -{ - struct lchan_signal_data sig; - sig.lchan = lchan; - sig.mr = resp; - osmo_signal_dispatch(SS_LCHAN, sig_no, &sig); -} - -static void do_lchan_free(struct gsm_lchan *lchan) -{ - /* We start the error timer to make the channel available again */ - if (lchan->state == LCHAN_S_REL_ERR) { - osmo_timer_setup(&lchan->error_timer, error_timeout_cb, lchan); - osmo_timer_schedule(&lchan->error_timer, - lchan->ts->trx->bts->network->T3111 + 2, 0); - } else { - rsl_lchan_set_state(lchan, LCHAN_S_NONE); - } - lchan_free(lchan); -} - -static void count_codecs(struct gsm_bts *bts, struct gsm_lchan *lchan) -{ - OSMO_ASSERT(bts); - - if (lchan->type == GSM_LCHAN_TCH_H) { - switch (lchan->tch_mode) { - case GSM48_CMODE_SPEECH_AMR: - rate_ctr_inc(&bts->network->bsc_ctrs->ctr[BSC_CTR_CODEC_AMR_H]); - break; - case GSM48_CMODE_SPEECH_V1: - rate_ctr_inc(&bts->network->bsc_ctrs->ctr[BSC_CTR_CODEC_V1_HR]); - break; - default: - break; - } - } else if (lchan->type == GSM_LCHAN_TCH_F) { - switch (lchan->tch_mode) { - case GSM48_CMODE_SPEECH_AMR: - rate_ctr_inc(&bts->network->bsc_ctrs->ctr[BSC_CTR_CODEC_AMR_F]); - break; - case GSM48_CMODE_SPEECH_V1: - rate_ctr_inc(&bts->network->bsc_ctrs->ctr[BSC_CTR_CODEC_V1_FR]); - break; - case GSM48_CMODE_SPEECH_EFR: - rate_ctr_inc(&bts->network->bsc_ctrs->ctr[BSC_CTR_CODEC_EFR]); - break; - default: - break; - } - } -} - -static uint8_t mdisc_by_msgtype(uint8_t msg_type) -{ - /* mask off the transparent bit ? */ - msg_type &= 0xfe; - - if ((msg_type & 0xf0) == 0x00) - return ABIS_RSL_MDISC_RLL; - if ((msg_type & 0xf0) == 0x10) { - if (msg_type >= 0x19 && msg_type <= 0x22) - return ABIS_RSL_MDISC_TRX; - else - return ABIS_RSL_MDISC_COM_CHAN; - } - if ((msg_type & 0xe0) == 0x20) - return ABIS_RSL_MDISC_DED_CHAN; - - return ABIS_RSL_MDISC_LOC; -} - -static inline void init_dchan_hdr(struct abis_rsl_dchan_hdr *dh, - uint8_t msg_type) -{ - dh->c.msg_discr = mdisc_by_msgtype(msg_type); - dh->c.msg_type = msg_type; - dh->ie_chan = RSL_IE_CHAN_NR; -} - -/* call rsl_lchan_lookup and set the log context */ -static struct gsm_lchan *lchan_lookup(struct gsm_bts_trx *trx, uint8_t chan_nr, - const char *log_name) -{ - int rc; - struct gsm_lchan *lchan = rsl_lchan_lookup(trx, chan_nr, &rc); - - if (!lchan) { - LOGP(DRSL, LOGL_ERROR, "%sunknown chan_nr=0x%02x\n", - log_name, chan_nr); - return NULL; - } - - if (rc < 0) - LOGP(DRSL, LOGL_ERROR, "%s %smismatching chan_nr=0x%02x\n", - gsm_ts_and_pchan_name(lchan->ts), log_name, chan_nr); - - if (lchan->conn) - log_set_context(LOG_CTX_VLR_SUBSCR, lchan->conn->subscr); - - return lchan; -} - -/* As per TS 03.03 Section 2.2, the IMSI has 'not more than 15 digits' */ -uint64_t str_to_imsi(const char *imsi_str) -{ - uint64_t ret; - - ret = strtoull(imsi_str, NULL, 10); - - return ret; -} - -static struct msgb *rsl_msgb_alloc(void) -{ - return msgb_alloc_headroom(RSL_ALLOC_SIZE, RSL_ALLOC_HEADROOM, - "RSL"); -} - -static void pad_macblock(uint8_t *out, const uint8_t *in, int len) -{ - memcpy(out, in, len); - - if (len < GSM_MACBLOCK_LEN) - memset(out+len, 0x2b, GSM_MACBLOCK_LEN - len); -} - -/* Chapter 9.3.7: Encryption Information */ -static int build_encr_info(uint8_t *out, struct gsm_lchan *lchan) -{ - *out++ = lchan->encr.alg_id & 0xff; - if (lchan->encr.key_len) - memcpy(out, lchan->encr.key, lchan->encr.key_len); - return lchan->encr.key_len + 1; -} - -static void print_rsl_cause(int lvl, const uint8_t *cause_v, uint8_t cause_len) -{ - int i; - - LOGPC(DRSL, lvl, "CAUSE=0x%02x(%s) ", - cause_v[0], rsl_err_name(cause_v[0])); - for (i = 1; i < cause_len-1; i++) - LOGPC(DRSL, lvl, "%02x ", cause_v[i]); -} - -static void lchan_act_tmr_cb(void *data) -{ - struct gsm_lchan *lchan = data; - - rsl_lchan_mark_broken(lchan, "activation timeout"); - lchan_free(lchan); -} - -static void lchan_deact_tmr_cb(void *data) -{ - struct gsm_lchan *lchan = data; - - rsl_lchan_mark_broken(lchan, "de-activation timeout"); - lchan_free(lchan); -} - - -/* Send a BCCH_INFO message as per Chapter 8.5.1 */ -int rsl_bcch_info(const struct gsm_bts_trx *trx, enum osmo_sysinfo_type si_type, const uint8_t *data, int len) -{ - struct abis_rsl_dchan_hdr *dh; - const struct gsm_bts *bts = trx->bts; - struct msgb *msg = rsl_msgb_alloc(); - uint8_t type = osmo_sitype2rsl(si_type); - - if (bts->c0 != trx) - LOGP(DRR, LOGL_ERROR, "Attempting to set BCCH SI%s on wrong BTS%u/TRX%u\n", - get_value_string(osmo_sitype_strs, si_type), bts->nr, trx->nr); - - dh = (struct abis_rsl_dchan_hdr *) msgb_put(msg, sizeof*dh); - init_dchan_hdr(dh, RSL_MT_BCCH_INFO); - dh->chan_nr = RSL_CHAN_BCCH; - - if (trx->bts->type == GSM_BTS_TYPE_RBS2000 - && type == RSL_SYSTEM_INFO_13) { - /* Ericsson proprietary encoding of SI13 */ - msgb_tv_put(msg, RSL_IE_SYSINFO_TYPE, RSL_ERIC_SYSTEM_INFO_13); - msgb_tlv_put(msg, RSL_IE_FULL_BCCH_INFO, len, data); - msgb_tv_put(msg, RSL_IE_ERIC_BCCH_MAPPING, 0x00); - } else { - /* Normal encoding */ - msgb_tv_put(msg, RSL_IE_SYSINFO_TYPE, type); - msgb_tlv_put(msg, RSL_IE_FULL_BCCH_INFO, len, data); - } - - msg->dst = trx->rsl_link; - - return abis_rsl_sendmsg(msg); -} - -int rsl_sacch_filling(struct gsm_bts_trx *trx, uint8_t type, - const uint8_t *data, int len) -{ - struct abis_rsl_common_hdr *ch; - struct msgb *msg = rsl_msgb_alloc(); - - ch = (struct abis_rsl_common_hdr *) msgb_put(msg, sizeof(*ch)); - ch->msg_discr = ABIS_RSL_MDISC_TRX; - ch->msg_type = RSL_MT_SACCH_FILL; - - msgb_tv_put(msg, RSL_IE_SYSINFO_TYPE, type); - msgb_tl16v_put(msg, RSL_IE_L3_INFO, len, data); - - msg->dst = trx->rsl_link; - - return abis_rsl_sendmsg(msg); -} - -int rsl_sacch_info_modify(struct gsm_lchan *lchan, uint8_t type, - const uint8_t *data, int len) -{ - struct abis_rsl_dchan_hdr *dh; - struct msgb *msg = rsl_msgb_alloc(); - uint8_t chan_nr = gsm_lchan2chan_nr(lchan); - - dh = (struct abis_rsl_dchan_hdr *) msgb_put(msg, sizeof(*dh)); - init_dchan_hdr(dh, RSL_MT_SACCH_INFO_MODIFY); - dh->chan_nr = chan_nr; - - msgb_tv_put(msg, RSL_IE_SYSINFO_TYPE, type); - msgb_tl16v_put(msg, RSL_IE_L3_INFO, len, data); - - msg->dst = lchan->ts->trx->rsl_link; - - return abis_rsl_sendmsg(msg); -} - -int rsl_chan_bs_power_ctrl(struct gsm_lchan *lchan, unsigned int fpc, int db) -{ - struct abis_rsl_dchan_hdr *dh; - struct msgb *msg; - uint8_t chan_nr = gsm_lchan2chan_nr(lchan); - - db = abs(db); - if (db > 30) - return -EINVAL; - - msg = rsl_msgb_alloc(); - - lchan->bs_power = db/2; - if (fpc) - lchan->bs_power |= 0x10; - - dh = (struct abis_rsl_dchan_hdr *) msgb_put(msg, sizeof(*dh)); - init_dchan_hdr(dh, RSL_MT_BS_POWER_CONTROL); - dh->chan_nr = chan_nr; - - msgb_tv_put(msg, RSL_IE_BS_POWER, lchan->bs_power); - - msg->dst = lchan->ts->trx->rsl_link; - - return abis_rsl_sendmsg(msg); -} - -int rsl_chan_ms_power_ctrl(struct gsm_lchan *lchan, unsigned int fpc, int dbm) -{ - struct abis_rsl_dchan_hdr *dh; - struct msgb *msg; - uint8_t chan_nr = gsm_lchan2chan_nr(lchan); - int ctl_lvl; - - ctl_lvl = ms_pwr_ctl_lvl(lchan->ts->trx->bts->band, dbm); - if (ctl_lvl < 0) - return ctl_lvl; - - msg = rsl_msgb_alloc(); - - lchan->ms_power = ctl_lvl; - - if (fpc) - lchan->ms_power |= 0x20; - - dh = (struct abis_rsl_dchan_hdr *) msgb_put(msg, sizeof(*dh)); - init_dchan_hdr(dh, RSL_MT_MS_POWER_CONTROL); - dh->chan_nr = chan_nr; - - msgb_tv_put(msg, RSL_IE_MS_POWER, lchan->ms_power); - - msg->dst = lchan->ts->trx->rsl_link; - - return abis_rsl_sendmsg(msg); -} - -static int channel_mode_from_lchan(struct rsl_ie_chan_mode *cm, - struct gsm_lchan *lchan) -{ - memset(cm, 0, sizeof(*cm)); - - /* FIXME: what to do with data calls ? */ - cm->dtx_dtu = 0; - if (lchan->ts->trx->bts->dtxu != GSM48_DTX_SHALL_NOT_BE_USED) - cm->dtx_dtu |= RSL_CMOD_DTXu; - if (lchan->ts->trx->bts->dtxd) - cm->dtx_dtu |= RSL_CMOD_DTXd; - - /* set TCH Speech/Data */ - cm->spd_ind = lchan->rsl_cmode; - - if (lchan->rsl_cmode == RSL_CMOD_SPD_SIGN && - lchan->tch_mode != GSM48_CMODE_SIGN) - LOGP(DRSL, LOGL_ERROR, "unsupported: rsl_mode == signalling, " - "but tch_mode != signalling\n"); - - switch (lchan->type) { - case GSM_LCHAN_SDCCH: - cm->chan_rt = RSL_CMOD_CRT_SDCCH; - break; - case GSM_LCHAN_TCH_F: - cm->chan_rt = RSL_CMOD_CRT_TCH_Bm; - break; - case GSM_LCHAN_TCH_H: - cm->chan_rt = RSL_CMOD_CRT_TCH_Lm; - break; - case GSM_LCHAN_NONE: - case GSM_LCHAN_UNKNOWN: - default: - LOGP(DRSL, LOGL_ERROR, - "unsupported activation lchan->type %u %s\n", - lchan->type, gsm_lchant_name(lchan->type)); - return -EINVAL; - } - - switch (lchan->tch_mode) { - case GSM48_CMODE_SIGN: - cm->chan_rate = 0; - break; - case GSM48_CMODE_SPEECH_V1: - cm->chan_rate = RSL_CMOD_SP_GSM1; - break; - case GSM48_CMODE_SPEECH_EFR: - cm->chan_rate = RSL_CMOD_SP_GSM2; - break; - case GSM48_CMODE_SPEECH_AMR: - cm->chan_rate = RSL_CMOD_SP_GSM3; - break; - case GSM48_CMODE_DATA_14k5: - case GSM48_CMODE_DATA_12k0: - case GSM48_CMODE_DATA_6k0: - switch (lchan->csd_mode) { - case LCHAN_CSD_M_NT: - /* non-transparent CSD with RLP */ - switch (lchan->tch_mode) { - case GSM48_CMODE_DATA_14k5: - cm->chan_rate = RSL_CMOD_SP_NT_14k5; - break; - case GSM48_CMODE_DATA_12k0: - cm->chan_rate = RSL_CMOD_SP_NT_12k0; - break; - case GSM48_CMODE_DATA_6k0: - cm->chan_rate = RSL_CMOD_SP_NT_6k0; - break; - default: - LOGP(DRSL, LOGL_ERROR, - "unsupported lchan->tch_mode %u\n", - lchan->tch_mode); - return -EINVAL; - } - break; - /* transparent data services below */ - case LCHAN_CSD_M_T_1200_75: - cm->chan_rate = RSL_CMOD_CSD_T_1200_75; - break; - case LCHAN_CSD_M_T_600: - cm->chan_rate = RSL_CMOD_CSD_T_600; - break; - case LCHAN_CSD_M_T_1200: - cm->chan_rate = RSL_CMOD_CSD_T_1200; - break; - case LCHAN_CSD_M_T_2400: - cm->chan_rate = RSL_CMOD_CSD_T_2400; - break; - case LCHAN_CSD_M_T_9600: - cm->chan_rate = RSL_CMOD_CSD_T_9600; - break; - case LCHAN_CSD_M_T_14400: - cm->chan_rate = RSL_CMOD_CSD_T_14400; - break; - case LCHAN_CSD_M_T_29000: - cm->chan_rate = RSL_CMOD_CSD_T_29000; - break; - case LCHAN_CSD_M_T_32000: - cm->chan_rate = RSL_CMOD_CSD_T_32000; - break; - default: - LOGP(DRSL, LOGL_ERROR, - "unsupported lchan->csd_mode %u\n", - lchan->csd_mode); - return -EINVAL; - } - break; - default: - LOGP(DRSL, LOGL_ERROR, - "unsupported lchan->tch_mode %u\n", - lchan->tch_mode); - return -EINVAL; - } - - return 0; -} - -static void mr_config_for_bts(struct gsm_lchan *lchan, struct msgb *msg) -{ - if (lchan->tch_mode == GSM48_CMODE_SPEECH_AMR) - msgb_tlv_put(msg, RSL_IE_MR_CONFIG, lchan->mr_bts_lv[0], - lchan->mr_bts_lv + 1); -} - -static enum gsm_phys_chan_config pchan_for_lchant(enum gsm_chan_t type) -{ - switch (type) { - case GSM_LCHAN_TCH_F: - return GSM_PCHAN_TCH_F; - case GSM_LCHAN_TCH_H: - return GSM_PCHAN_TCH_H; - case GSM_LCHAN_NONE: - case GSM_LCHAN_PDTCH: - /* TODO: so far lchan->type is NONE in PDCH mode. PDTCH is only - * used in osmo-bts. Maybe set PDTCH and drop the NONE case - * here. */ - return GSM_PCHAN_PDCH; - default: - return GSM_PCHAN_UNKNOWN; - } -} - -/*! Tx simplified channel activation message for non-standard PDCH type. */ -static int rsl_chan_activate_lchan_as_pdch(struct gsm_lchan *lchan) -{ - struct msgb *msg; - struct abis_rsl_dchan_hdr *dh; - - /* This might be called after release of the second lchan of a TCH/H, - * but PDCH activation must always happen on the first lchan. Make sure - * the calling code passes the correct lchan. */ - OSMO_ASSERT(lchan == lchan->ts->lchan); - - rsl_lchan_set_state(lchan, LCHAN_S_ACT_REQ); - - msg = rsl_msgb_alloc(); - dh = (struct abis_rsl_dchan_hdr *) msgb_put(msg, sizeof(*dh)); - init_dchan_hdr(dh, RSL_MT_CHAN_ACTIV); - dh->chan_nr = gsm_lchan_as_pchan2chan_nr(lchan, GSM_PCHAN_PDCH); - - msgb_tv_put(msg, RSL_IE_ACT_TYPE, RSL_ACT_OSMO_PDCH); - - if (lchan->ts->trx->bts->type == GSM_BTS_TYPE_RBS2000 && - lchan->ts->trx->bts->rbs2000.use_superchannel) { - const uint8_t eric_pgsl_tmr[] = { 30, 1 }; - msgb_tv_fixed_put(msg, RSL_IE_ERIC_PGSL_TIMERS, - sizeof(eric_pgsl_tmr), eric_pgsl_tmr); - } - - msg->dst = lchan->ts->trx->rsl_link; - - return abis_rsl_sendmsg(msg); -} - -/* Chapter 8.4.1 */ -int rsl_chan_activate_lchan(struct gsm_lchan *lchan, uint8_t act_type, - uint8_t ho_ref) -{ - struct abis_rsl_dchan_hdr *dh; - struct msgb *msg; - int rc; - uint8_t *len; - uint8_t ta; - - struct rsl_ie_chan_mode cm; - struct gsm48_chan_desc cd; - - /* If a TCH_F/PDCH TS is in PDCH mode, deactivate PDCH first. */ - if (lchan->ts->pchan == GSM_PCHAN_TCH_F_PDCH - && (lchan->ts->flags & TS_F_PDCH_ACTIVE)) { - /* store activation type and handover reference */ - lchan->dyn.act_type = act_type; - lchan->dyn.ho_ref = ho_ref; - return rsl_ipacc_pdch_activate(lchan->ts, 0); - } - - /* - * If necessary, release PDCH on dynamic TS. Note that sending a - * release here is only necessary when in PDCH mode; for TCH types, an - * RSL RF Chan Release is initiated by the BTS when a voice call ends, - * so when we reach this, it will already be released. If a dyn TS is - * in PDCH mode, it is still active and we need to initiate a release - * from the BSC side here. - * - * If pchan_is != pchan_want, the PDCH has already been taken down and - * the switchover now needs to enable the TCH lchan. - * - * To switch a dyn TS between TCH/H and TCH/F, it is sufficient to send - * a chan activ with the new lchan type, because it will already be - * released. - */ - if (lchan->ts->pchan == GSM_PCHAN_TCH_F_TCH_H_PDCH - && lchan->ts->dyn.pchan_is == GSM_PCHAN_PDCH - && lchan->ts->dyn.pchan_is == lchan->ts->dyn.pchan_want) { - enum gsm_phys_chan_config pchan_want; - pchan_want = pchan_for_lchant(lchan->type); - if (lchan->ts->dyn.pchan_is != pchan_want) { - /* - * Make sure to record on lchan[0] so that we'll find - * it after the PDCH release. - */ - struct gsm_lchan *lchan0 = lchan->ts->lchan; - lchan0->dyn.act_type = act_type, - lchan0->dyn.ho_ref = ho_ref; - lchan0->dyn.rqd_ref = lchan->rqd_ref; - lchan0->dyn.rqd_ta = lchan->rqd_ta; - lchan->rqd_ref = NULL; - lchan->rqd_ta = 0; - DEBUGP(DRSL, "%s saved rqd_ref=%p ta=%u\n", - gsm_lchan_name(lchan0), lchan0->rqd_ref, - lchan0->rqd_ta); - return dyn_ts_switchover_start(lchan->ts, pchan_want); - } - } - - DEBUGP(DRSL, "%s Tx RSL Channel Activate with act_type=%s\n", - gsm_ts_and_pchan_name(lchan->ts), - rsl_act_type_name(act_type)); - - if (act_type == RSL_ACT_OSMO_PDCH) { - if (lchan->ts->pchan != GSM_PCHAN_TCH_F_TCH_H_PDCH) { - LOGP(DRSL, LOGL_ERROR, - "%s PDCH channel activation only allowed on %s\n", - gsm_ts_and_pchan_name(lchan->ts), - gsm_pchan_name(GSM_PCHAN_TCH_F_TCH_H_PDCH)); - return -EINVAL; - } - return rsl_chan_activate_lchan_as_pdch(lchan); - } - - if (lchan->ts->pchan == GSM_PCHAN_TCH_F_TCH_H_PDCH - && lchan->ts->dyn.pchan_want == GSM_PCHAN_PDCH) { - LOGP(DRSL, LOGL_ERROR, - "%s Expected PDCH activation kind\n", - gsm_ts_and_pchan_name(lchan->ts)); - return -EINVAL; - } - - rc = channel_mode_from_lchan(&cm, lchan); - if (rc < 0) { - LOGP(DRSL, LOGL_ERROR, - "%s Cannot find channel mode from lchan type\n", - gsm_ts_and_pchan_name(lchan->ts)); - return rc; - } - - rsl_lchan_set_state(lchan, LCHAN_S_ACT_REQ); - - ta = lchan->rqd_ta; - - /* BS11 requires TA shifted by 2 bits */ - if (lchan->ts->trx->bts->type == GSM_BTS_TYPE_BS11) - ta <<= 2; - - memset(&cd, 0, sizeof(cd)); - gsm48_lchan2chan_desc(&cd, lchan); - - msg = rsl_msgb_alloc(); - dh = (struct abis_rsl_dchan_hdr *) msgb_put(msg, sizeof(*dh)); - init_dchan_hdr(dh, RSL_MT_CHAN_ACTIV); - - if (lchan->ts->pchan == GSM_PCHAN_TCH_F_TCH_H_PDCH) - dh->chan_nr = gsm_lchan_as_pchan2chan_nr( - lchan, lchan->ts->dyn.pchan_want); - else - dh->chan_nr = gsm_lchan2chan_nr(lchan); - - msgb_tv_put(msg, RSL_IE_ACT_TYPE, act_type); - msgb_tlv_put(msg, RSL_IE_CHAN_MODE, sizeof(cm), - (uint8_t *) &cm); - - /* - * The Channel Identification is needed for Phase1 phones - * and it contains the GSM48 Channel Description and the - * Mobile Allocation. The GSM 08.58 asks for the Mobile - * Allocation to have a length of zero. We are using the - * msgb_l3len to calculate the length of both messages. - */ - msgb_v_put(msg, RSL_IE_CHAN_IDENT); - len = msgb_put(msg, 1); - msgb_tv_fixed_put(msg, GSM48_IE_CHANDESC_2, sizeof(cd), (const uint8_t *) &cd); - - if (lchan->ts->hopping.enabled) - msgb_tlv_put(msg, GSM48_IE_MA_AFTER, lchan->ts->hopping.ma_len, - lchan->ts->hopping.ma_data); - else - msgb_tlv_put(msg, GSM48_IE_MA_AFTER, 0, NULL); - - /* update the calculated size */ - msg->l3h = len + 1; - *len = msgb_l3len(msg); - - if (lchan->encr.alg_id > RSL_ENC_ALG_A5(0)) { - uint8_t encr_info[MAX_A5_KEY_LEN+2]; - rc = build_encr_info(encr_info, lchan); - if (rc > 0) - msgb_tlv_put(msg, RSL_IE_ENCR_INFO, rc, encr_info); - } - - switch (act_type) { - case RSL_ACT_INTER_ASYNC: - case RSL_ACT_INTER_SYNC: - msgb_tv_put(msg, RSL_IE_HANDO_REF, ho_ref); - break; - default: - break; - } - - msgb_tv_put(msg, RSL_IE_BS_POWER, lchan->bs_power); - msgb_tv_put(msg, RSL_IE_MS_POWER, lchan->ms_power); - msgb_tv_put(msg, RSL_IE_TIMING_ADVANCE, ta); - mr_config_for_bts(lchan, msg); - - msg->dst = lchan->ts->trx->rsl_link; - - return abis_rsl_sendmsg(msg); -} - -/* Chapter 8.4.9: Modify channel mode on BTS side */ -int rsl_chan_mode_modify_req(struct gsm_lchan *lchan) -{ - struct abis_rsl_dchan_hdr *dh; - struct msgb *msg; - int rc; - - uint8_t chan_nr = gsm_lchan2chan_nr(lchan); - struct rsl_ie_chan_mode cm; - - rc = channel_mode_from_lchan(&cm, lchan); - if (rc < 0) - return rc; - - msg = rsl_msgb_alloc(); - dh = (struct abis_rsl_dchan_hdr *) msgb_put(msg, sizeof(*dh)); - init_dchan_hdr(dh, RSL_MT_MODE_MODIFY_REQ); - dh->chan_nr = chan_nr; - - msgb_tlv_put(msg, RSL_IE_CHAN_MODE, sizeof(cm), - (uint8_t *) &cm); - - if (lchan->encr.alg_id > RSL_ENC_ALG_A5(0)) { - uint8_t encr_info[MAX_A5_KEY_LEN+2]; - rc = build_encr_info(encr_info, lchan); - if (rc > 0) - msgb_tlv_put(msg, RSL_IE_ENCR_INFO, rc, encr_info); - } - - mr_config_for_bts(lchan, msg); - - msg->dst = lchan->ts->trx->rsl_link; - - return abis_rsl_sendmsg(msg); -} - -/* Chapter 8.4.6: Send the encryption command with given L3 info */ -int rsl_encryption_cmd(struct msgb *msg) -{ - struct abis_rsl_dchan_hdr *dh; - struct gsm_lchan *lchan = msg->lchan; - uint8_t chan_nr = gsm_lchan2chan_nr(lchan); - uint8_t encr_info[MAX_A5_KEY_LEN+2]; - uint8_t l3_len = msg->len; - int rc; - - /* First push the L3 IE tag and length */ - msgb_tv16_push(msg, RSL_IE_L3_INFO, l3_len); - - /* then the link identifier (SAPI0, main sign link) */ - msgb_tv_push(msg, RSL_IE_LINK_IDENT, 0); - - /* then encryption information */ - rc = build_encr_info(encr_info, lchan); - if (rc <= 0) - return rc; - msgb_tlv_push(msg, RSL_IE_ENCR_INFO, rc, encr_info); - - /* and finally the DCHAN header */ - dh = (struct abis_rsl_dchan_hdr *) msgb_push(msg, sizeof(*dh)); - init_dchan_hdr(dh, RSL_MT_ENCR_CMD); - dh->chan_nr = chan_nr; - - msg->dst = lchan->ts->trx->rsl_link; - - return abis_rsl_sendmsg(msg); -} - -/* Chapter 8.4.5 / 4.6: Deactivate the SACCH after 04.08 RR CHAN RELEASE */ -int rsl_deact_sacch(struct gsm_lchan *lchan) -{ - struct abis_rsl_dchan_hdr *dh; - struct msgb *msg = rsl_msgb_alloc(); - - dh = (struct abis_rsl_dchan_hdr *) msgb_put(msg, sizeof(*dh)); - init_dchan_hdr(dh, RSL_MT_DEACTIVATE_SACCH); - dh->chan_nr = gsm_lchan2chan_nr(lchan); - - msg->lchan = lchan; - msg->dst = lchan->ts->trx->rsl_link; - - DEBUGP(DRSL, "%s DEACTivate SACCH CMD\n", gsm_lchan_name(lchan)); - - return abis_rsl_sendmsg(msg); -} - -static bool dyn_ts_should_switch_to_pdch(struct gsm_bts_trx_ts *ts) -{ - int ss; - - if (ts->pchan != GSM_PCHAN_TCH_F_TCH_H_PDCH) - return false; - - if (ts->trx->bts->gprs.mode == BTS_GPRS_NONE) - return false; - - /* Already in PDCH mode? */ - if (ts->dyn.pchan_is == GSM_PCHAN_PDCH) - return false; - - /* See if all lchans are released. */ - for (ss = 0; ss < ts_subslots(ts); ss++) { - struct gsm_lchan *lc = &ts->lchan[ss]; - if (lc->state != LCHAN_S_NONE) { - DEBUGP(DRSL, "%s lchan %u still in use" - " (type=%s,state=%s)\n", - gsm_ts_and_pchan_name(ts), lc->nr, - gsm_lchant_name(lc->type), - gsm_lchans_name(lc->state)); - /* An lchan is still used. */ - return false; - } - } - - /* All channels are released, go to PDCH mode. */ - DEBUGP(DRSL, "%s back to PDCH\n", - gsm_ts_and_pchan_name(ts)); - return true; -} - -static void error_timeout_cb(void *data) -{ - struct gsm_lchan *lchan = data; - if (lchan->state != LCHAN_S_REL_ERR) { - LOGP(DRSL, LOGL_ERROR, "%s error timeout but not in error state: %d\n", - gsm_lchan_name(lchan), lchan->state); - return; - } - - /* go back to the none state */ - LOGP(DRSL, LOGL_INFO, "%s is back in operation.\n", gsm_lchan_name(lchan)); - rsl_lchan_set_state(lchan, LCHAN_S_NONE); - - /* Put PDCH channel back into PDCH mode, if GPRS is enabled */ - if (lchan->ts->pchan == GSM_PCHAN_TCH_F_PDCH - && lchan->ts->trx->bts->gprs.mode != BTS_GPRS_NONE) - rsl_ipacc_pdch_activate(lchan->ts, 1); - - if (dyn_ts_should_switch_to_pdch(lchan->ts)) - dyn_ts_switchover_start(lchan->ts, GSM_PCHAN_PDCH); -} - -static int rsl_rx_rf_chan_rel_ack(struct gsm_lchan *lchan); - -/* Chapter 8.4.14 / 4.7: Tell BTS to release the radio channel */ -static int rsl_rf_chan_release(struct gsm_lchan *lchan, int error, - enum sacch_deact deact_sacch) -{ - struct abis_rsl_dchan_hdr *dh; - struct msgb *msg; - int rc; - - /* Stop timers that should lead to a channel release */ - osmo_timer_del(&lchan->T3109); - - if (lchan->state == LCHAN_S_REL_ERR) { - LOGP(DRSL, LOGL_NOTICE, "%s is in error state, not sending release.\n", - gsm_lchan_name(lchan)); - return -1; - } - - msg = rsl_msgb_alloc(); - dh = (struct abis_rsl_dchan_hdr *) msgb_put(msg, sizeof(*dh)); - init_dchan_hdr(dh, RSL_MT_RF_CHAN_REL); - dh->chan_nr = gsm_lchan2chan_nr(lchan); - - msg->lchan = lchan; - msg->dst = lchan->ts->trx->rsl_link; - - if (error) - DEBUGP(DRSL, "%s RF Channel Release due to error: %d\n", - gsm_lchan_name(lchan), error); - else - DEBUGP(DRSL, "%s RF Channel Release\n", gsm_lchan_name(lchan)); - - if (error) { - /* - * FIXME: GSM 04.08 gives us two options for the abnormal - * chanel release. This can be either like in the non-existent - * sub-lcuase 3.5.1 or for the main signalling link deactivate - * the SACCH, start timer T3109 and consider the channel as - * released. - * - * This code is doing the later for all raido links and not - * only the main link. Right now all SAPIs are released on the - * local end, the SACCH will be de-activated and right now the - * T3111 will be started. First T3109 should be started and then - * the T3111. - * - * TODO: Move this out of the function. - */ - - /* - * sacch de-activate and "local end release" - */ - if (deact_sacch == SACCH_DEACTIVATE) - rsl_deact_sacch(lchan); - rsl_release_sapis_from(lchan, 0, RSL_REL_LOCAL_END); - - /* - * TODO: start T3109 now. - */ - rsl_lchan_set_state(lchan, LCHAN_S_REL_ERR); - } - - /* Start another timer or assume the BTS sends a ACK/NACK? */ - osmo_timer_setup(&lchan->act_timer, lchan_deact_tmr_cb, lchan); - osmo_timer_schedule(&lchan->act_timer, 4, 0); - - rc = abis_rsl_sendmsg(msg); - - /* BTS will respond by RF CHAN REL ACK */ - return rc; -} - -/* - * Special handling for channel releases in the error case. - */ -static int rsl_rf_chan_release_err(struct gsm_lchan *lchan) -{ - enum sacch_deact sacch_deact; - if (lchan->state != LCHAN_S_ACTIVE) - return 0; - switch (ts_pchan(lchan->ts)) { - case GSM_PCHAN_TCH_F: - case GSM_PCHAN_TCH_H: - case GSM_PCHAN_CCCH_SDCCH4: - case GSM_PCHAN_CCCH_SDCCH4_CBCH: - case GSM_PCHAN_SDCCH8_SACCH8C: - case GSM_PCHAN_SDCCH8_SACCH8C_CBCH: - sacch_deact = SACCH_DEACTIVATE; - break; - default: - sacch_deact = SACCH_NONE; - break; - } - return rsl_rf_chan_release(lchan, 1, sacch_deact); -} - -static int rsl_rx_rf_chan_rel_ack(struct gsm_lchan *lchan) -{ - struct gsm_bts_trx_ts *ts = lchan->ts; - - DEBUGP(DRSL, "%s RF CHANNEL RELEASE ACK\n", gsm_lchan_name(lchan)); - - /* Stop all pending timers */ - osmo_timer_del(&lchan->act_timer); - osmo_timer_del(&lchan->T3111); - - /* - * The BTS didn't respond within the timeout to our channel - * release request and we have marked the channel as broken. - * Now we do receive an ACK and let's be conservative. If it - * is a sysmoBTS we know that only one RF Channel Release ACK - * will be sent. So let's "repair" the channel. - */ - if (lchan->state == LCHAN_S_BROKEN) { - int do_free = is_sysmobts_v2(ts->trx->bts); - LOGP(DRSL, LOGL_NOTICE, - "%s CHAN REL ACK for broken channel. %s.\n", - gsm_lchan_name(lchan), - do_free ? "Releasing it" : "Keeping it broken"); - if (do_free) - do_lchan_free(lchan); - if (dyn_ts_should_switch_to_pdch(lchan->ts)) - dyn_ts_switchover_start(lchan->ts, GSM_PCHAN_PDCH); - return 0; - } - - if (lchan->state != LCHAN_S_REL_REQ && lchan->state != LCHAN_S_REL_ERR) - LOGP(DRSL, LOGL_NOTICE, "%s CHAN REL ACK but state %s\n", - gsm_lchan_name(lchan), - gsm_lchans_name(lchan->state)); - - do_lchan_free(lchan); - - /* - * Check Osmocom RSL CHAN ACT style dynamic TCH/F_TCH/H_PDCH TS for pending - * transitions in these cases: - * - * a) after PDCH was released due to switchover request, activate TCH. - * BSC initiated this switchover, so dyn.pchan_is != pchan_want and - * lchan->type has been set to the desired GSM_LCHAN_*. - * - * b) Voice call ended and a TCH is released. If the TS is now unused, - * switch to PDCH. Here still dyn.pchan_is == dyn.pchan_want because - * we're only just notified and may decide to switch to PDCH now. - */ - if (ts->pchan == GSM_PCHAN_TCH_F_TCH_H_PDCH) { - DEBUGP(DRSL, "%s Rx RSL Channel Release ack for lchan %u\n", - gsm_ts_and_pchan_name(ts), lchan->nr); - - /* (a) */ - if (ts->dyn.pchan_is != ts->dyn.pchan_want) - return dyn_ts_switchover_continue(ts); - - /* (b) */ - if (dyn_ts_should_switch_to_pdch(ts)) - return dyn_ts_switchover_start(ts, GSM_PCHAN_PDCH); - } - - /* - * Put a dynamic TCH/F_PDCH channel back to PDCH mode iff it was - * released successfully. If in error, the PDCH ACT will follow after - * T3111 in error_timeout_cb(). - * - * Any state other than LCHAN_S_REL_ERR became LCHAN_S_NONE after above - * do_lchan_free(). Assert this, because that's what ensures a PDCH ACT - * on a TCH/F_PDCH TS in all cases. - * - * If GPRS is disabled, always skip the PDCH ACT. - */ - OSMO_ASSERT(lchan->state == LCHAN_S_NONE - || lchan->state == LCHAN_S_REL_ERR); - if (ts->trx->bts->gprs.mode == BTS_GPRS_NONE) - return 0; - if (ts->pchan == GSM_PCHAN_TCH_F_PDCH - && lchan->state == LCHAN_S_NONE) - return rsl_ipacc_pdch_activate(ts, 1); - return 0; -} - -int rsl_paging_cmd(struct gsm_bts *bts, uint8_t paging_group, uint8_t len, - uint8_t *ms_ident, uint8_t chan_needed, bool is_gprs) -{ - struct abis_rsl_dchan_hdr *dh; - struct msgb *msg = rsl_msgb_alloc(); - - dh = (struct abis_rsl_dchan_hdr *) msgb_put(msg, sizeof(*dh)); - init_dchan_hdr(dh, RSL_MT_PAGING_CMD); - dh->chan_nr = RSL_CHAN_PCH_AGCH; - - msgb_tv_put(msg, RSL_IE_PAGING_GROUP, paging_group); - msgb_tlv_put(msg, RSL_IE_MS_IDENTITY, len-2, ms_ident+2); - msgb_tv_put(msg, RSL_IE_CHAN_NEEDED, chan_needed); - - /* Ericsson wants to have this IE in case a paging message - * relates to packet paging */ - if (bts->type == GSM_BTS_TYPE_RBS2000 && is_gprs) - msgb_tv_put(msg, RSL_IE_ERIC_PACKET_PAG_IND, 0); - - msg->dst = bts->c0->rsl_link; - - return abis_rsl_sendmsg(msg); -} - -int imsi_str2bcd(uint8_t *bcd_out, const char *str_in) -{ - int i, len = strlen(str_in); - - for (i = 0; i < len; i++) { - int num = str_in[i] - 0x30; - if (num < 0 || num > 9) - return -1; - if (i % 2 == 0) - bcd_out[i/2] = num; - else - bcd_out[i/2] |= (num << 4); - } - - return 0; -} - -/* Chapter 8.5.6 */ -struct msgb *rsl_imm_assign_cmd_common(struct gsm_bts *bts, uint8_t len, uint8_t *val) -{ - struct msgb *msg = rsl_msgb_alloc(); - struct abis_rsl_dchan_hdr *dh; - uint8_t buf[GSM_MACBLOCK_LEN]; - - dh = (struct abis_rsl_dchan_hdr *) msgb_put(msg, sizeof(*dh)); - init_dchan_hdr(dh, RSL_MT_IMMEDIATE_ASSIGN_CMD); - dh->chan_nr = RSL_CHAN_PCH_AGCH; - - switch (bts->type) { - case GSM_BTS_TYPE_BS11: - msgb_tlv_put(msg, RSL_IE_IMM_ASS_INFO, len, val); - break; - default: - /* If phase 2, construct a FULL_IMM_ASS_INFO */ - pad_macblock(buf, val, len); - msgb_tlv_put(msg, RSL_IE_FULL_IMM_ASS_INFO, GSM_MACBLOCK_LEN, - buf); - break; - } - - msg->dst = bts->c0->rsl_link; - return msg; -} - -/* Chapter 8.5.6 */ -int rsl_imm_assign_cmd(struct gsm_bts *bts, uint8_t len, uint8_t *val) -{ - struct msgb *msg = rsl_imm_assign_cmd_common(bts, len, val); - if (!msg) - return 1; - return abis_rsl_sendmsg(msg); -} - -/* Chapter 8.5.6 */ -int rsl_ericsson_imm_assign_cmd(struct gsm_bts *bts, uint32_t tlli, uint8_t len, uint8_t *val) -{ - struct msgb *msg = rsl_imm_assign_cmd_common(bts, len, val); - if (!msg) - return 1; - - /* ericsson can handle a reference at the end of the message which is used in - * the confirm message. The confirm message is only sent if the trailer is present */ - msgb_put_u8(msg, RSL_IE_ERIC_MOBILE_ID); - msgb_put_u32(msg, tlli); - - return abis_rsl_sendmsg(msg); -} - -/* Send Siemens specific MS RF Power Capability Indication */ -int rsl_siemens_mrpci(struct gsm_lchan *lchan, struct rsl_mrpci *mrpci) -{ - struct msgb *msg = rsl_msgb_alloc(); - struct abis_rsl_dchan_hdr *dh; - - dh = (struct abis_rsl_dchan_hdr *) msgb_put(msg, sizeof(*dh)); - init_dchan_hdr(dh, RSL_MT_SIEMENS_MRPCI); - dh->c.msg_discr = ABIS_RSL_MDISC_DED_CHAN; - dh->chan_nr = gsm_lchan2chan_nr(lchan); - msgb_tv_put(msg, RSL_IE_SIEMENS_MRPCI, *(uint8_t *)mrpci); - - DEBUGP(DRSL, "%s TX Siemens MRPCI 0x%02x\n", - gsm_lchan_name(lchan), *(uint8_t *)mrpci); - - msg->dst = lchan->ts->trx->rsl_link; - - return abis_rsl_sendmsg(msg); -} - - -/* Send "DATA REQUEST" message with given L3 Info payload */ -/* Chapter 8.3.1 */ -int rsl_data_request(struct msgb *msg, uint8_t link_id) -{ - if (msg->lchan == NULL) { - LOGP(DRSL, LOGL_ERROR, "cannot send DATA REQUEST to unknown lchan\n"); - return -EINVAL; - } - - rsl_rll_push_l3(msg, RSL_MT_DATA_REQ, gsm_lchan2chan_nr(msg->lchan), - link_id, 1); - - msg->dst = msg->lchan->ts->trx->rsl_link; - - return abis_rsl_sendmsg(msg); -} - -/* Send "ESTABLISH REQUEST" message with given L3 Info payload */ -/* Chapter 8.3.1 */ -int rsl_establish_request(struct gsm_lchan *lchan, uint8_t link_id) -{ - struct msgb *msg; - - msg = rsl_rll_simple(RSL_MT_EST_REQ, gsm_lchan2chan_nr(lchan), - link_id, 0); - msg->dst = lchan->ts->trx->rsl_link; - - DEBUGP(DRLL, "%s RSL RLL ESTABLISH REQ (link_id=0x%02x)\n", - gsm_lchan_name(lchan), link_id); - - return abis_rsl_sendmsg(msg); -} - -static void rsl_handle_release(struct gsm_lchan *lchan); - -/* Special work handler to handle missing RSL_MT_REL_CONF message from - * Nokia InSite BTS */ -static void lchan_rel_work_cb(void *data) -{ - struct gsm_lchan *lchan = data; - int sapi; - - for (sapi = 0; sapi < ARRAY_SIZE(lchan->sapis); ++sapi) { - if (lchan->sapis[sapi] == LCHAN_SAPI_REL) - lchan->sapis[sapi] = LCHAN_SAPI_UNUSED; - } - rsl_handle_release(lchan); -} - -/* Chapter 8.3.7 Request the release of multiframe mode of RLL connection. - This is what higher layers should call. The BTS then responds with - RELEASE CONFIRM, which we in turn use to trigger RSL CHANNEL RELEASE, - which in turn is acknowledged by RSL CHANNEL RELEASE ACK, which calls - lchan_free() */ -int rsl_release_request(struct gsm_lchan *lchan, uint8_t link_id, - enum rsl_rel_mode release_mode) -{ - - struct msgb *msg; - - msg = rsl_rll_simple(RSL_MT_REL_REQ, gsm_lchan2chan_nr(lchan), - link_id, 0); - /* 0 is normal release, 1 is local end */ - msgb_tv_put(msg, RSL_IE_RELEASE_MODE, release_mode); - - /* FIXME: start some timer in case we don't receive a REL ACK ? */ - - msg->dst = lchan->ts->trx->rsl_link; - - DEBUGP(DRLL, "%s RSL RLL RELEASE REQ (link_id=0x%02x, reason=%u)\n", - gsm_lchan_name(lchan), link_id, release_mode); - - abis_rsl_sendmsg(msg); - - /* Do not wait for Nokia BTS to send the confirm. */ - if (is_nokia_bts(lchan->ts->trx->bts) - && lchan->ts->trx->bts->nokia.no_loc_rel_cnf - && release_mode == RSL_REL_LOCAL_END) { - DEBUGP(DRLL, "Scheduling release, becasuse Nokia InSite BTS does not send a RELease CONFirm.\n"); - lchan->sapis[link_id & 0x7] = LCHAN_SAPI_REL; - osmo_timer_setup(&lchan->rel_work, lchan_rel_work_cb, lchan); - osmo_timer_schedule(&lchan->rel_work, 0, 0); - } - - return 0; -} - -int rsl_lchan_mark_broken(struct gsm_lchan *lchan, const char *reason) -{ - LOGP(DRSL, LOGL_ERROR, "%s %s lchan broken: %s\n", - gsm_lchan_name(lchan), gsm_lchant_name(lchan->type), reason); - rsl_lchan_set_state(lchan, LCHAN_S_BROKEN); - lchan->broken_reason = reason; - return 0; -} - -int rsl_lchan_set_state(struct gsm_lchan *lchan, int state) -{ - DEBUGP(DRSL, "%s state %s -> %s\n", - gsm_lchan_name(lchan), gsm_lchans_name(lchan->state), - gsm_lchans_name(state)); - lchan->state = state; - return 0; -} - -/* Chapter 8.4.2: Channel Activate Acknowledge */ -static int rsl_rx_chan_act_ack(struct msgb *msg) -{ - struct abis_rsl_dchan_hdr *rslh = msgb_l2(msg); - struct gsm_lchan *lchan = msg->lchan; - struct gsm_bts_trx_ts *ts = lchan->ts; - - /* BTS has confirmed channel activation, we now need - * to assign the activated channel to the MS */ - if (rslh->ie_chan != RSL_IE_CHAN_NR) - return -EINVAL; - - osmo_timer_del(&lchan->act_timer); - - if (lchan->state == LCHAN_S_BROKEN) { - int do_release = is_sysmobts_v2(ts->trx->bts); - LOGP(DRSL, LOGL_NOTICE, "%s CHAN ACT ACK for broken channel. %s\n", - gsm_lchan_name(lchan), - do_release ? "Releasing it" : "Keeping it broken"); - if (do_release) { - talloc_free(lchan->rqd_ref); - lchan->rqd_ref = NULL; - lchan->rqd_ta = 0; - rsl_lchan_set_state(msg->lchan, LCHAN_S_ACTIVE); - if (ts->pchan == GSM_PCHAN_TCH_F_TCH_H_PDCH) { - /* - * lchan_act_tmr_cb() already called - * lchan_free() and cleared the lchan->type, so - * calling dyn_ts_switchover_complete() here - * would not have the desired effect of - * mimicking an activated lchan that we can - * release. Instead hack the dyn ts state to - * make sure that rsl_rx_rf_chan_rel_ack() will - * switch back to PDCH, i.e. have pchan_is == - * pchan_want, both != GSM_PCHAN_PDCH: - */ - ts->dyn.pchan_is = GSM_PCHAN_NONE; - ts->dyn.pchan_want = GSM_PCHAN_NONE; - } - rsl_rf_chan_release(msg->lchan, 0, SACCH_NONE); - } - return 0; - } - - if (lchan->state != LCHAN_S_ACT_REQ) - LOGP(DRSL, LOGL_NOTICE, "%s CHAN ACT ACK, but state %s\n", - gsm_lchan_name(lchan), - gsm_lchans_name(lchan->state)); - rsl_lchan_set_state(lchan, LCHAN_S_ACTIVE); - - if (ts->pchan == GSM_PCHAN_TCH_F_TCH_H_PDCH) - dyn_ts_switchover_complete(lchan); - - if (lchan->rqd_ref) { - rsl_send_imm_assignment(lchan); - talloc_free(lchan->rqd_ref); - lchan->rqd_ref = NULL; - lchan->rqd_ta = 0; - } - - send_lchan_signal(S_LCHAN_ACTIVATE_ACK, lchan, NULL); - - /* Update bts attributes inside the PCU */ - if (ts->pchan == GSM_PCHAN_TCH_F_TCH_H_PDCH || - ts->pchan == GSM_PCHAN_TCH_F_PDCH || - ts->pchan == GSM_PCHAN_PDCH) - pcu_info_update(ts->trx->bts); - - return 0; -} - -/* Chapter 8.4.3: Channel Activate NACK */ -static int rsl_rx_chan_act_nack(struct msgb *msg) -{ - struct abis_rsl_dchan_hdr *dh = msgb_l2(msg); - struct tlv_parsed tp; - - osmo_timer_del(&msg->lchan->act_timer); - - if (msg->lchan->state == LCHAN_S_BROKEN) { - LOGP(DRSL, LOGL_ERROR, - "%s CHANNEL ACTIVATE NACK for broken channel.\n", - gsm_lchan_name(msg->lchan)); - return -1; - } - - LOGP(DRSL, LOGL_ERROR, "%s CHANNEL ACTIVATE NACK ", - gsm_lchan_name(msg->lchan)); - - /* BTS has rejected channel activation ?!? */ - if (dh->ie_chan != RSL_IE_CHAN_NR) - return -EINVAL; - - rsl_tlv_parse(&tp, dh->data, msgb_l2len(msg)-sizeof(*dh)); - if (TLVP_PRESENT(&tp, RSL_IE_CAUSE)) { - const uint8_t *cause = TLVP_VAL(&tp, RSL_IE_CAUSE); - print_rsl_cause(LOGL_ERROR, cause, - TLVP_LEN(&tp, RSL_IE_CAUSE)); - msg->lchan->error_cause = *cause; - if (*cause != RSL_ERR_RCH_ALR_ACTV_ALLOC) { - rsl_lchan_mark_broken(msg->lchan, "NACK on activation"); - } else - rsl_rf_chan_release(msg->lchan, 1, SACCH_DEACTIVATE); - - } else { - rsl_lchan_mark_broken(msg->lchan, "NACK on activation no IE"); - } - - LOGPC(DRSL, LOGL_ERROR, "\n"); - - send_lchan_signal(S_LCHAN_ACTIVATE_NACK, msg->lchan, NULL); - return 0; -} - -/* Chapter 8.4.4: Connection Failure Indication */ -static int rsl_rx_conn_fail(struct msgb *msg) -{ - struct abis_rsl_dchan_hdr *dh = msgb_l2(msg); - struct tlv_parsed tp; - - LOGP(DRSL, LOGL_NOTICE, "%s CONNECTION FAIL: RELEASING state %s ", - gsm_lchan_name(msg->lchan), - gsm_lchans_name(msg->lchan->state)); - - rsl_tlv_parse(&tp, dh->data, msgb_l2len(msg)-sizeof(*dh)); - - if (TLVP_PRESENT(&tp, RSL_IE_CAUSE)) - print_rsl_cause(LOGL_NOTICE, TLVP_VAL(&tp, RSL_IE_CAUSE), - TLVP_LEN(&tp, RSL_IE_CAUSE)); - - LOGPC(DRSL, LOGL_NOTICE, "\n"); - rate_ctr_inc(&msg->lchan->ts->trx->bts->network->bsc_ctrs->ctr[BSC_CTR_CHAN_RF_FAIL]); - return rsl_rf_chan_release_err(msg->lchan); -} - -static void print_meas_rep_uni(struct gsm_meas_rep_unidir *mru, - const char *prefix) -{ - DEBUGPC(DMEAS, "RXL-FULL-%s=%3ddBm RXL-SUB-%s=%3ddBm ", - prefix, rxlev2dbm(mru->full.rx_lev), - prefix, rxlev2dbm(mru->sub.rx_lev)); - DEBUGPC(DMEAS, "RXQ-FULL-%s=%d RXQ-SUB-%s=%d ", - prefix, mru->full.rx_qual, prefix, mru->sub.rx_qual); -} - -static void print_meas_rep(struct gsm_lchan *lchan, struct gsm_meas_rep *mr) -{ - int i; - const char *name = ""; - - if (lchan && lchan->conn) { - if (lchan->conn->bsub) - name = bsc_subscr_name(lchan->conn->bsub); - else if (lchan->conn->subscr) - name = lchan->conn->subscr->imsi; - else - name = lchan->name; - } - - DEBUGP(DMEAS, "[%s] MEASUREMENT RESULT NR=%d ", name, mr->nr); - - if (mr->flags & MEAS_REP_F_DL_DTX) - DEBUGPC(DMEAS, "DTXd "); - - print_meas_rep_uni(&mr->ul, "ul"); - DEBUGPC(DMEAS, "BS_POWER=%d ", mr->bs_power); - - if (mr->flags & MEAS_REP_F_MS_TO) - DEBUGPC(DMEAS, "MS_TO=%d ", mr->ms_timing_offset); - - if (mr->flags & MEAS_REP_F_MS_L1) { - DEBUGPC(DMEAS, "L1_MS_PWR=%3ddBm ", mr->ms_l1.pwr); - DEBUGPC(DMEAS, "L1_FPC=%u ", - mr->flags & MEAS_REP_F_FPC ? 1 : 0); - DEBUGPC(DMEAS, "L1_TA=%u ", mr->ms_l1.ta); - } - - if (mr->flags & MEAS_REP_F_UL_DTX) - DEBUGPC(DMEAS, "DTXu "); - if (mr->flags & MEAS_REP_F_BA1) - DEBUGPC(DMEAS, "BA1 "); - if (!(mr->flags & MEAS_REP_F_DL_VALID)) - DEBUGPC(DMEAS, "NOT VALID "); - else - print_meas_rep_uni(&mr->dl, "dl"); - - DEBUGPC(DMEAS, "NUM_NEIGH=%u\n", mr->num_cell); - if (mr->num_cell == 7) - return; - for (i = 0; i < mr->num_cell; i++) { - struct gsm_meas_rep_cell *mrc = &mr->cell[i]; - DEBUGP(DMEAS, "IDX=%u ARFCN=%u BSIC=%u => %d dBm\n", - mrc->neigh_idx, mrc->arfcn, mrc->bsic, rxlev2dbm(mrc->rxlev)); - } -} - -static int rsl_rx_meas_res(struct msgb *msg) -{ - struct abis_rsl_dchan_hdr *dh = msgb_l2(msg); - struct tlv_parsed tp; - struct gsm_meas_rep *mr = lchan_next_meas_rep(msg->lchan); - uint8_t len; - const uint8_t *val; - int rc; - - /* check if this channel is actually active */ - /* FIXME: maybe this check should be way more generic/centralized */ - if (msg->lchan->state != LCHAN_S_ACTIVE) { - LOGP(DRSL, LOGL_DEBUG, "%s: MEAS RES for inactive channel\n", - gsm_lchan_name(msg->lchan)); - return 0; - } - - memset(mr, 0, sizeof(*mr)); - mr->lchan = msg->lchan; - - rsl_tlv_parse(&tp, dh->data, msgb_l2len(msg)-sizeof(*dh)); - - if (!TLVP_PRESENT(&tp, RSL_IE_MEAS_RES_NR) || - !TLVP_PRESENT(&tp, RSL_IE_UPLINK_MEAS) || - !TLVP_PRESENT(&tp, RSL_IE_BS_POWER)) - return -EIO; - - /* Mandatory Parts */ - mr->nr = *TLVP_VAL(&tp, RSL_IE_MEAS_RES_NR); - - len = TLVP_LEN(&tp, RSL_IE_UPLINK_MEAS); - val = TLVP_VAL(&tp, RSL_IE_UPLINK_MEAS); - if (len >= 3) { - if (val[0] & 0x40) - mr->flags |= MEAS_REP_F_DL_DTX; - mr->ul.full.rx_lev = val[0] & 0x3f; - mr->ul.sub.rx_lev = val[1] & 0x3f; - mr->ul.full.rx_qual = val[2]>>3 & 0x7; - mr->ul.sub.rx_qual = val[2] & 0x7; - } - - mr->bs_power = *TLVP_VAL(&tp, RSL_IE_BS_POWER); - - /* Optional Parts */ - if (TLVP_PRESENT(&tp, RSL_IE_MS_TIMING_OFFSET)) { - /* According to 3GPP TS 48.058 § MS Timing Offset = Timing Offset field - 63 */ - mr->ms_timing_offset = *TLVP_VAL(&tp, RSL_IE_MS_TIMING_OFFSET) - 63; - mr->flags |= MEAS_REP_F_MS_TO; - } - - if (TLVP_PRESENT(&tp, RSL_IE_L1_INFO)) { - struct e1inp_sign_link *sign_link = msg->dst; - - val = TLVP_VAL(&tp, RSL_IE_L1_INFO); - mr->flags |= MEAS_REP_F_MS_L1; - mr->ms_l1.pwr = ms_pwr_dbm(sign_link->trx->bts->band, val[0] >> 3); - if (val[0] & 0x04) - mr->flags |= MEAS_REP_F_FPC; - mr->ms_l1.ta = val[1]; - /* BS11 and Nokia reports TA shifted by 2 bits */ - if (msg->lchan->ts->trx->bts->type == GSM_BTS_TYPE_BS11 - || msg->lchan->ts->trx->bts->type == GSM_BTS_TYPE_NOKIA_SITE) - mr->ms_l1.ta >>= 2; - } - if (TLVP_PRESENT(&tp, RSL_IE_L3_INFO)) { - msg->l3h = (uint8_t *) TLVP_VAL(&tp, RSL_IE_L3_INFO); - rc = gsm48_parse_meas_rep(mr, msg); - if (rc < 0) - return rc; - } - - print_meas_rep(msg->lchan, mr); - - send_lchan_signal(S_LCHAN_MEAS_REP, msg->lchan, mr); - - return 0; -} - -/* Chapter 8.4.7 */ -static int rsl_rx_hando_det(struct msgb *msg) -{ - struct abis_rsl_dchan_hdr *dh = msgb_l2(msg); - struct tlv_parsed tp; - - DEBUGP(DRSL, "%s HANDOVER DETECT ", gsm_lchan_name(msg->lchan)); - - rsl_tlv_parse(&tp, dh->data, msgb_l2len(msg)-sizeof(*dh)); - - if (TLVP_PRESENT(&tp, RSL_IE_ACCESS_DELAY)) - DEBUGPC(DRSL, "access delay = %u\n", - *TLVP_VAL(&tp, RSL_IE_ACCESS_DELAY)); - else - DEBUGPC(DRSL, "\n"); - - send_lchan_signal(S_LCHAN_HANDOVER_DETECT, msg->lchan, NULL); - - return 0; -} - -static bool lchan_may_change_pdch(struct gsm_lchan *lchan, bool pdch_act) -{ - struct gsm_bts_trx_ts *ts; - - OSMO_ASSERT(lchan); - - ts = lchan->ts; - OSMO_ASSERT(ts); - OSMO_ASSERT(ts->trx); - OSMO_ASSERT(ts->trx->bts); - - if (lchan->ts->pchan != GSM_PCHAN_TCH_F_PDCH) { - LOGP(DRSL, LOGL_ERROR, "%s pchan=%s Rx PDCH %s ACK" - " for channel that is no TCH/F_PDCH\n", - gsm_lchan_name(lchan), - gsm_pchan_name(ts->pchan), - pdch_act? "ACT" : "DEACT"); - return false; - } - - if (lchan->state != LCHAN_S_NONE) { - LOGP(DRSL, LOGL_ERROR, "%s pchan=%s Rx PDCH %s ACK" - " in unexpected state: %s\n", - gsm_lchan_name(lchan), - gsm_pchan_name(ts->pchan), - pdch_act? "ACT" : "DEACT", - gsm_lchans_name(lchan->state)); - return false; - } - return true; -} - -static int rsl_rx_pdch_act_ack(struct msgb *msg) -{ - if (!lchan_may_change_pdch(msg->lchan, true)) - return -EINVAL; - - msg->lchan->ts->flags |= TS_F_PDCH_ACTIVE; - msg->lchan->ts->flags &= ~TS_F_PDCH_ACT_PENDING; - - return 0; -} - -static int rsl_rx_pdch_deact_ack(struct msgb *msg) -{ - if (!lchan_may_change_pdch(msg->lchan, false)) - return -EINVAL; - - msg->lchan->ts->flags &= ~TS_F_PDCH_ACTIVE; - msg->lchan->ts->flags &= ~TS_F_PDCH_DEACT_PENDING; - - rsl_chan_activate_lchan(msg->lchan, msg->lchan->dyn.act_type, - msg->lchan->dyn.ho_ref); - - return 0; -} - -static int abis_rsl_rx_dchan(struct msgb *msg) -{ - struct abis_rsl_dchan_hdr *rslh = msgb_l2(msg); - int rc = 0; - char *ts_name; - struct e1inp_sign_link *sign_link = msg->dst; - - msg->lchan = lchan_lookup(sign_link->trx, rslh->chan_nr, - "Abis RSL rx DCHAN: "); - if (!msg->lchan) - return -1; - ts_name = gsm_lchan_name(msg->lchan); - - switch (rslh->c.msg_type) { - case RSL_MT_CHAN_ACTIV_ACK: - DEBUGP(DRSL, "%s CHANNEL ACTIVATE ACK\n", ts_name); - rc = rsl_rx_chan_act_ack(msg); - count_codecs(sign_link->trx->bts, msg->lchan); - break; - case RSL_MT_CHAN_ACTIV_NACK: - rc = rsl_rx_chan_act_nack(msg); - break; - case RSL_MT_CONN_FAIL: - rc = rsl_rx_conn_fail(msg); - break; - case RSL_MT_MEAS_RES: - rc = rsl_rx_meas_res(msg); - break; - case RSL_MT_HANDO_DET: - rc = rsl_rx_hando_det(msg); - break; - case RSL_MT_RF_CHAN_REL_ACK: - rc = rsl_rx_rf_chan_rel_ack(msg->lchan); - break; - case RSL_MT_MODE_MODIFY_ACK: - count_codecs(sign_link->trx->bts, msg->lchan); - DEBUGP(DRSL, "%s CHANNEL MODE MODIFY ACK\n", ts_name); - break; - case RSL_MT_MODE_MODIFY_NACK: - LOGP(DRSL, LOGL_ERROR, "%s CHANNEL MODE MODIFY NACK\n", ts_name); - break; - case RSL_MT_IPAC_PDCH_ACT_ACK: - DEBUGP(DRSL, "%s IPAC PDCH ACT ACK\n", ts_name); - rc = rsl_rx_pdch_act_ack(msg); - break; - case RSL_MT_IPAC_PDCH_ACT_NACK: - LOGP(DRSL, LOGL_ERROR, "%s IPAC PDCH ACT NACK\n", ts_name); - break; - case RSL_MT_IPAC_PDCH_DEACT_ACK: - DEBUGP(DRSL, "%s IPAC PDCH DEACT ACK\n", ts_name); - rc = rsl_rx_pdch_deact_ack(msg); - break; - case RSL_MT_IPAC_PDCH_DEACT_NACK: - LOGP(DRSL, LOGL_ERROR, "%s IPAC PDCH DEACT NACK\n", ts_name); - break; - case RSL_MT_PHY_CONTEXT_CONF: - case RSL_MT_PREPROC_MEAS_RES: - case RSL_MT_TALKER_DET: - case RSL_MT_LISTENER_DET: - case RSL_MT_REMOTE_CODEC_CONF_REP: - case RSL_MT_MR_CODEC_MOD_ACK: - case RSL_MT_MR_CODEC_MOD_NACK: - case RSL_MT_MR_CODEC_MOD_PER: - LOGP(DRSL, LOGL_NOTICE, "%s Unimplemented Abis RSL DChan " - "msg 0x%02x\n", ts_name, rslh->c.msg_type); - break; - default: - LOGP(DRSL, LOGL_NOTICE, "%s unknown Abis RSL DChan msg 0x%02x\n", - ts_name, rslh->c.msg_type); - return -EINVAL; - } - - return rc; -} - -static int rsl_rx_error_rep(struct msgb *msg) -{ - struct abis_rsl_common_hdr *rslh = msgb_l2(msg); - struct tlv_parsed tp; - struct e1inp_sign_link *sign_link = msg->dst; - - LOGP(DRSL, LOGL_ERROR, "%s ERROR REPORT ", gsm_trx_name(sign_link->trx)); - - rsl_tlv_parse(&tp, rslh->data, msgb_l2len(msg)-sizeof(*rslh)); - - if (TLVP_PRESENT(&tp, RSL_IE_CAUSE)) - print_rsl_cause(LOGL_ERROR, TLVP_VAL(&tp, RSL_IE_CAUSE), - TLVP_LEN(&tp, RSL_IE_CAUSE)); - - LOGPC(DRSL, LOGL_ERROR, "\n"); - - return 0; -} - -static int abis_rsl_rx_trx(struct msgb *msg) -{ - struct abis_rsl_common_hdr *rslh = msgb_l2(msg); - struct e1inp_sign_link *sign_link = msg->dst; - int rc = 0; - - switch (rslh->msg_type) { - case RSL_MT_ERROR_REPORT: - rc = rsl_rx_error_rep(msg); - break; - case RSL_MT_RF_RES_IND: - /* interference on idle channels of TRX */ - //DEBUGP(DRSL, "%s RF Resource Indication\n", gsm_trx_name(sign_link->trx)); - break; - case RSL_MT_OVERLOAD: - /* indicate CCCH / ACCH / processor overload */ - LOGP(DRSL, LOGL_ERROR, "%s CCCH/ACCH/CPU Overload\n", - gsm_trx_name(sign_link->trx)); - break; - case 0x42: /* Nokia specific: SI End ACK */ - LOGP(DRSL, LOGL_INFO, "Nokia SI End ACK\n"); - break; - case 0x43: /* Nokia specific: SI End NACK */ - LOGP(DRSL, LOGL_INFO, "Nokia SI End NACK\n"); - break; - default: - LOGP(DRSL, LOGL_NOTICE, "%s Unknown Abis RSL TRX message " - "type 0x%02x\n", gsm_trx_name(sign_link->trx), rslh->msg_type); - return -EINVAL; - } - return rc; -} - -/* If T3101 expires, we never received a response to IMMEDIATE ASSIGN */ -static void t3101_expired(void *data) -{ - struct gsm_lchan *lchan = data; - LOGP(DRSL, LOGL_NOTICE, - "%s T3101 expired: no response to IMMEDIATE ASSIGN\n", - gsm_lchan_name(lchan)); - rsl_rf_chan_release(lchan, 1, SACCH_DEACTIVATE); -} - -/* If T3111 expires, we will send the RF Channel Request */ -static void t3111_expired(void *data) -{ - struct gsm_lchan *lchan = data; - LOGP(DRSL, LOGL_NOTICE, - "%s T3111 expired: releasing RF Channel\n", - gsm_lchan_name(lchan)); - rsl_rf_chan_release(lchan, 0, SACCH_NONE); -} - -/* If T3109 expires the MS has not send a UA/UM do the error release */ -static void t3109_expired(void *data) -{ - struct gsm_lchan *lchan = data; - - LOGP(DRSL, LOGL_ERROR, - "%s SACCH deactivation timeout.\n", gsm_lchan_name(lchan)); - rsl_rf_chan_release(lchan, 1, SACCH_NONE); -} - -/* Format an IMM ASS REJ according to 04.08 Chapter 9.1.20 */ -static int rsl_send_imm_ass_rej(struct gsm_bts *bts, - unsigned int num_req_refs, - struct gsm48_req_ref *rqd_refs, - uint8_t wait_ind) -{ - uint8_t buf[GSM_MACBLOCK_LEN]; - struct gsm48_imm_ass_rej *iar = (struct gsm48_imm_ass_rej *)buf; - - /* create IMMEDIATE ASSIGN REJECT 04.08 message */ - memset(iar, 0, sizeof(*iar)); - iar->proto_discr = GSM48_PDISC_RR; - iar->msg_type = GSM48_MT_RR_IMM_ASS_REJ; - iar->page_mode = GSM48_PM_SAME; - - memcpy(&iar->req_ref1, &rqd_refs[0], sizeof(iar->req_ref1)); - iar->wait_ind1 = wait_ind; - - if (num_req_refs >= 2) - memcpy(&iar->req_ref2, &rqd_refs[1], sizeof(iar->req_ref2)); - else - memcpy(&iar->req_ref2, &rqd_refs[0], sizeof(iar->req_ref2)); - iar->wait_ind2 = wait_ind; - - if (num_req_refs >= 3) - memcpy(&iar->req_ref3, &rqd_refs[2], sizeof(iar->req_ref3)); - else - memcpy(&iar->req_ref3, &rqd_refs[0], sizeof(iar->req_ref3)); - iar->wait_ind3 = wait_ind; - - if (num_req_refs >= 4) - memcpy(&iar->req_ref4, &rqd_refs[3], sizeof(iar->req_ref4)); - else - memcpy(&iar->req_ref4, &rqd_refs[0], sizeof(iar->req_ref4)); - iar->wait_ind4 = wait_ind; - - /* we need to subtract 1 byte from sizeof(*iar) since ia includes the l2_plen field */ - iar->l2_plen = GSM48_LEN2PLEN((sizeof(*iar)-1)); - - return rsl_imm_assign_cmd(bts, sizeof(*iar), (uint8_t *) iar); -} - -/* Handle packet channel rach requests */ -static int rsl_rx_pchan_rqd(struct msgb *msg, struct gsm_bts *bts) -{ - struct gsm48_req_ref *rqd_ref; - struct abis_rsl_dchan_hdr *rqd_hdr = msgb_l2(msg); - rqd_ref = (struct gsm48_req_ref *) &rqd_hdr->data[1]; - uint8_t ra = rqd_ref->ra; - uint8_t t1, t2, t3; - uint32_t fn; - uint8_t rqd_ta; - uint8_t is_11bit; - - /* Process rach request and forward contained information to PCU */ - if (ra == 0x7F) { - is_11bit = 1; - - /* FIXME: Also handle 11 bit rach requests */ - LOGP(DRSL, LOGL_ERROR, "BTS %d eleven bit access burst not supported yet!\n",bts->nr); - return -EINVAL; - } else { - is_11bit = 0; - t1 = rqd_ref->t1; - t2 = rqd_ref->t2; - t3 = rqd_ref->t3_low | (rqd_ref->t3_high << 3); - fn = (51 * ((t3-t2) % 26) + t3 + 51 * 26 * t1); - - rqd_ta = rqd_hdr->data[sizeof(struct gsm48_req_ref)+2]; - } - - return pcu_tx_rach_ind(bts, rqd_ta, ra, fn, is_11bit, - GSM_L1_BURST_TYPE_ACCESS_0); -} - -/* MS has requested a channel on the RACH */ -static int rsl_rx_chan_rqd(struct msgb *msg) -{ - struct e1inp_sign_link *sign_link = msg->dst; - struct gsm_bts *bts = sign_link->trx->bts; - struct abis_rsl_dchan_hdr *rqd_hdr = msgb_l2(msg); - struct gsm48_req_ref *rqd_ref; - enum gsm_chan_t lctype; - enum gsm_chreq_reason_t chreq_reason; - struct gsm_lchan *lchan; - uint8_t rqd_ta; - int is_lu; - - uint16_t arfcn; - uint8_t subch; - - /* parse request reference to be used in immediate assign */ - if (rqd_hdr->data[0] != RSL_IE_REQ_REFERENCE) - return -EINVAL; - - rqd_ref = (struct gsm48_req_ref *) &rqd_hdr->data[1]; - - /* parse access delay and use as TA */ - if (rqd_hdr->data[sizeof(struct gsm48_req_ref)+1] != RSL_IE_ACCESS_DELAY) - return -EINVAL; - rqd_ta = rqd_hdr->data[sizeof(struct gsm48_req_ref)+2]; - - /* Determine channel request cause code */ - chreq_reason = get_reason_by_chreq(rqd_ref->ra, bts->network->neci); - LOGP(DRSL, LOGL_NOTICE, "BTS %d CHAN RQD: reason: %s (ra=0x%02x, neci=0x%02x, chreq_reason=0x%02x)\n", - msg->lchan->ts->trx->bts->nr, - get_value_string(gsm_chreq_descs, chreq_reason), - rqd_ref->ra, bts->network->neci, chreq_reason); - - /* Handle PDCH related rach requests (in case of BSC-co-located-PCU */ - if (chreq_reason == GSM_CHREQ_REASON_PDCH) - return rsl_rx_pchan_rqd(msg, bts); - - /* determine channel type (SDCCH/TCH_F/TCH_H) based on - * request reference RA */ - lctype = get_ctype_by_chreq(bts->network, rqd_ref->ra); - - rate_ctr_inc(&bts->network->bsc_ctrs->ctr[BSC_CTR_CHREQ_TOTAL]); - - /* - * We want LOCATION UPDATES to succeed and will assign a TCH - * if we have no SDCCH available. - */ - is_lu = !!(chreq_reason == GSM_CHREQ_REASON_LOCATION_UPD); - - /* check availability / allocate channel */ - lchan = lchan_alloc(bts, lctype, is_lu); - if (!lchan) { - LOGP(DRSL, LOGL_NOTICE, "BTS %d CHAN RQD: no resources for %s 0x%x\n", - msg->lchan->ts->trx->bts->nr, gsm_lchant_name(lctype), rqd_ref->ra); - rate_ctr_inc(&bts->network->bsc_ctrs->ctr[BSC_CTR_CHREQ_NO_CHANNEL]); - /* FIXME gather multiple CHAN RQD and reject up to 4 at the same time */ - if (bts->network->T3122) - rsl_send_imm_ass_rej(bts, 1, rqd_ref, bts->network->T3122 & 0xff); - return 0; - } - - /* - * Expecting lchan state to be NONE, except for dyn TS in PDCH mode. - * Those are expected to be ACTIVE: the PDCH release will be sent from - * rsl_chan_activate_lchan() below. - */ - if (lchan->state != LCHAN_S_NONE - && !(lchan->ts->pchan == GSM_PCHAN_TCH_F_TCH_H_PDCH - && lchan->ts->dyn.pchan_is == GSM_PCHAN_PDCH - && lchan->state == LCHAN_S_ACTIVE)) - LOGP(DRSL, LOGL_NOTICE, "%s lchan_alloc() returned channel " - "in state %s\n", gsm_lchan_name(lchan), - gsm_lchans_name(lchan->state)); - - /* save the RACH data as we need it after the CHAN ACT ACK */ - lchan->rqd_ref = talloc_zero(bts, struct gsm48_req_ref); - if (!lchan->rqd_ref) { - LOGP(DRSL, LOGL_ERROR, "Failed to allocate gsm48_req_ref.\n"); - lchan_free(lchan); - return -ENOMEM; - } - - memcpy(lchan->rqd_ref, rqd_ref, sizeof(*rqd_ref)); - lchan->rqd_ta = rqd_ta; - - arfcn = lchan->ts->trx->arfcn; - subch = lchan->nr; - - lchan->encr.alg_id = RSL_ENC_ALG_A5(0); /* no encryption */ - lchan->ms_power = ms_pwr_ctl_lvl(bts->band, bts->ms_max_power); - lchan->bs_power = 0; /* 0dB reduction, output power = Pn */ - lchan->rsl_cmode = RSL_CMOD_SPD_SIGN; - lchan->tch_mode = GSM48_CMODE_SIGN; - - /* Start another timer or assume the BTS sends a ACK/NACK? */ - osmo_timer_setup(&lchan->act_timer, lchan_act_tmr_cb, lchan); - osmo_timer_schedule(&lchan->act_timer, 4, 0); - - DEBUGP(DRSL, "%s Activating ARFCN(%u) SS(%u) lctype %s " - "r=%s ra=0x%02x ta=%d\n", gsm_lchan_name(lchan), arfcn, subch, - gsm_lchant_name(lchan->type), gsm_chreq_name(chreq_reason), - rqd_ref->ra, rqd_ta); - - rsl_chan_activate_lchan(lchan, RSL_ACT_INTRA_IMM_ASS, 0); - - return 0; -} - -static int rsl_send_imm_assignment(struct gsm_lchan *lchan) -{ - struct gsm_bts *bts = lchan->ts->trx->bts; - uint8_t buf[GSM_MACBLOCK_LEN]; - struct gsm48_imm_ass *ia = (struct gsm48_imm_ass *) buf; - - /* create IMMEDIATE ASSIGN 04.08 messge */ - memset(ia, 0, sizeof(*ia)); - /* we set ia->l2_plen once we know the length of the MA below */ - ia->proto_discr = GSM48_PDISC_RR; - ia->msg_type = GSM48_MT_RR_IMM_ASS; - ia->page_mode = GSM48_PM_SAME; - gsm48_lchan2chan_desc(&ia->chan_desc, lchan); - - /* use request reference extracted from CHAN_RQD */ - memcpy(&ia->req_ref, lchan->rqd_ref, sizeof(ia->req_ref)); - ia->timing_advance = lchan->rqd_ta; - if (!lchan->ts->hopping.enabled) { - ia->mob_alloc_len = 0; - } else { - ia->mob_alloc_len = lchan->ts->hopping.ma_len; - memcpy(ia->mob_alloc, lchan->ts->hopping.ma_data, ia->mob_alloc_len); - } - /* we need to subtract 1 byte from sizeof(*ia) since ia includes the l2_plen field */ - ia->l2_plen = GSM48_LEN2PLEN((sizeof(*ia)-1) + ia->mob_alloc_len); - - /* Start timer T3101 to wait for GSM48_MT_RR_PAG_RESP */ - osmo_timer_setup(&lchan->T3101, t3101_expired, lchan); - osmo_timer_schedule(&lchan->T3101, bts->network->T3101, 0); - - /* send IMMEDIATE ASSIGN CMD on RSL to BTS (to send on CCCH to MS) */ - return rsl_imm_assign_cmd(bts, sizeof(*ia)+ia->mob_alloc_len, (uint8_t *) ia); -} - -/* current load on the CCCH */ -static int rsl_rx_ccch_load(struct msgb *msg) -{ - struct e1inp_sign_link *sign_link = msg->dst; - struct abis_rsl_dchan_hdr *rslh = msgb_l2(msg); - struct ccch_signal_data sd; - - sd.bts = sign_link->trx->bts; - sd.rach_slot_count = -1; - sd.rach_busy_count = -1; - sd.rach_access_count = -1; - - switch (rslh->data[0]) { - case RSL_IE_PAGING_LOAD: - sd.pg_buf_space = rslh->data[1] << 8 | rslh->data[2]; - if (is_ipaccess_bts(sign_link->trx->bts) && sd.pg_buf_space == 0xffff) { - /* paging load below configured threshold, use 50 as default */ - sd.pg_buf_space = 50; - } - paging_update_buffer_space(sign_link->trx->bts, sd.pg_buf_space); - osmo_signal_dispatch(SS_CCCH, S_CCCH_PAGING_LOAD, &sd); - break; - case RSL_IE_RACH_LOAD: - if (msg->data_len >= 7) { - sd.rach_slot_count = rslh->data[2] << 8 | rslh->data[3]; - sd.rach_busy_count = rslh->data[4] << 8 | rslh->data[5]; - sd.rach_access_count = rslh->data[6] << 8 | rslh->data[7]; - osmo_signal_dispatch(SS_CCCH, S_CCCH_RACH_LOAD, &sd); - } - break; - default: - break; - } - - return 0; -} - -static int abis_rsl_rx_cchan(struct msgb *msg) -{ - struct e1inp_sign_link *sign_link = msg->dst; - struct abis_rsl_dchan_hdr *rslh = msgb_l2(msg); - int rc = 0; - uint32_t tlli; - - msg->lchan = lchan_lookup(sign_link->trx, rslh->chan_nr, - "Abis RSL rx CCHAN: "); - - switch (rslh->c.msg_type) { - case RSL_MT_CHAN_RQD: - /* MS has requested a channel on the RACH */ - rc = rsl_rx_chan_rqd(msg); - break; - case RSL_MT_CCCH_LOAD_IND: - /* current load on the CCCH */ - rc = rsl_rx_ccch_load(msg); - break; - case RSL_MT_DELETE_IND: - /* CCCH overloaded, IMM_ASSIGN was dropped */ - case RSL_MT_CBCH_LOAD_IND: - /* current load on the CBCH */ - LOGP(DRSL, LOGL_NOTICE, "Unimplemented Abis RSL TRX message " - "type 0x%02x\n", rslh->c.msg_type); - break; - case 0x10: /* Ericsson specific: Immediate Assign Sent */ - /* FIXME: Replace the messy message parsing below - * with proper TV parser */ - LOGP(DRSL, LOGL_INFO, "IMM.ass sent\n"); - if(msg->len < 9) - LOGP(DRSL, LOGL_ERROR, "short IMM.ass sent message!\n"); - else if(msg->data[4] != 0xf1) - LOGP(DRSL, LOGL_ERROR, "unsupported IMM.ass message format! (please fix)\n"); - else { - msgb_pull(msg, 5); /* drop previous data to use msg_pull_u32 */ - tlli = msgb_pull_u32(msg); - pcu_tx_imm_ass_sent(sign_link->trx->bts, tlli); - } - break; - default: - LOGP(DRSL, LOGL_NOTICE, "Unknown Abis RSL TRX message type " - "0x%02x\n", rslh->c.msg_type); - return -EINVAL; - } - - return rc; -} - -static int rsl_rx_rll_err_ind(struct msgb *msg) -{ - struct tlv_parsed tp; - struct abis_rsl_rll_hdr *rllh = msgb_l2(msg); - uint8_t rlm_cause; - - rsl_tlv_parse(&tp, rllh->data, msgb_l2len(msg) - sizeof(*rllh)); - if (!TLVP_PRESENT(&tp, RSL_IE_RLM_CAUSE)) { - LOGP(DRLL, LOGL_ERROR, - "%s ERROR INDICATION without mandantory cause.\n", - gsm_lchan_name(msg->lchan)); - return -1; - } - - rlm_cause = *TLVP_VAL(&tp, RSL_IE_RLM_CAUSE); - LOGP(DRLL, LOGL_ERROR, "%s ERROR INDICATION cause=%s in state=%s\n", - gsm_lchan_name(msg->lchan), - rsl_rlm_cause_name(rlm_cause), - gsm_lchans_name(msg->lchan->state)); - - rll_indication(msg->lchan, rllh->link_id, BSC_RLLR_IND_ERR_IND); - - if (rlm_cause == RLL_CAUSE_T200_EXPIRED) { - rate_ctr_inc(&msg->lchan->ts->trx->bts->network->bsc_ctrs->ctr[BSC_CTR_CHAN_RLL_ERR]); - return rsl_rf_chan_release_err(msg->lchan); - } - - return 0; -} - -static void rsl_handle_release(struct gsm_lchan *lchan) -{ - int sapi; - struct gsm_bts *bts; - - /* - * Maybe only one link/SAPI was releasd or the error handling - * was activated. Just return now and let the other code handle - * it. - */ - if (lchan->state != LCHAN_S_REL_REQ) - return; - - for (sapi = 0; sapi < ARRAY_SIZE(lchan->sapis); ++sapi) { - if (lchan->sapis[sapi] == LCHAN_SAPI_UNUSED) - continue; - LOGP(DRSL, LOGL_DEBUG, "%s waiting for SAPI=%d to be released.\n", - gsm_lchan_name(lchan), sapi); - return; - } - - - /* Stop T3109 and wait for T3111 before re-using the channel */ - osmo_timer_del(&lchan->T3109); - osmo_timer_setup(&lchan->T3111, t3111_expired, lchan); - bts = lchan->ts->trx->bts; - osmo_timer_schedule(&lchan->T3111, bts->network->T3111, 0); -} - -/* ESTABLISH INDICATION, LOCATION AREA UPDATE REQUEST - 0x02, 0x06, - 0x01, 0x20, - 0x02, 0x00, - 0x0b, 0x00, 0x0f, 0x05, 0x08, ... */ - -static int abis_rsl_rx_rll(struct msgb *msg) -{ - struct e1inp_sign_link *sign_link = msg->dst; - struct abis_rsl_rll_hdr *rllh = msgb_l2(msg); - int rc = 0; - char *ts_name; - uint8_t sapi = rllh->link_id & 7; - - msg->lchan = lchan_lookup(sign_link->trx, rllh->chan_nr, - "Abis RSL rx RLL: "); - ts_name = gsm_lchan_name(msg->lchan); - DEBUGP(DRLL, "%s SAPI=%u ", ts_name, sapi); - - switch (rllh->c.msg_type) { - case RSL_MT_DATA_IND: - DEBUGPC(DRLL, "DATA INDICATION\n"); - if (msgb_l2len(msg) > - sizeof(struct abis_rsl_common_hdr) + sizeof(*rllh) && - rllh->data[0] == RSL_IE_L3_INFO) { - msg->l3h = &rllh->data[3]; - return gsm0408_rcvmsg(msg, rllh->link_id); - } - break; - case RSL_MT_EST_IND: - DEBUGPC(DRLL, "ESTABLISH INDICATION\n"); - /* lchan is established, stop T3101 */ - msg->lchan->sapis[rllh->link_id & 0x7] = LCHAN_SAPI_MS; - osmo_timer_del(&msg->lchan->T3101); - if (msgb_l2len(msg) > - sizeof(struct abis_rsl_common_hdr) + sizeof(*rllh) && - rllh->data[0] == RSL_IE_L3_INFO) { - msg->l3h = &rllh->data[3]; - return gsm0408_rcvmsg(msg, rllh->link_id); - } - break; - case RSL_MT_EST_CONF: - DEBUGPC(DRLL, "ESTABLISH CONFIRM\n"); - msg->lchan->sapis[rllh->link_id & 0x7] = LCHAN_SAPI_NET; - rll_indication(msg->lchan, rllh->link_id, - BSC_RLLR_IND_EST_CONF); - break; - case RSL_MT_REL_IND: - /* BTS informs us of having received DISC from MS */ - DEBUGPC(DRLL, "RELEASE INDICATION\n"); - msg->lchan->sapis[rllh->link_id & 0x7] = LCHAN_SAPI_UNUSED; - rll_indication(msg->lchan, rllh->link_id, - BSC_RLLR_IND_REL_IND); - rsl_handle_release(msg->lchan); - break; - case RSL_MT_REL_CONF: - /* BTS informs us of having received UA from MS, - * in response to DISC that we've sent earlier */ - DEBUGPC(DRLL, "RELEASE CONFIRMATION\n"); - msg->lchan->sapis[rllh->link_id & 0x7] = LCHAN_SAPI_UNUSED; - rsl_handle_release(msg->lchan); - break; - case RSL_MT_ERROR_IND: - DEBUGPC(DRLL, "ERROR INDICATION\n"); - rc = rsl_rx_rll_err_ind(msg); - break; - case RSL_MT_UNIT_DATA_IND: - DEBUGPC(DRLL, "UNIT DATA INDICATION\n"); - LOGP(DRLL, LOGL_NOTICE, "unimplemented Abis RLL message " - "type 0x%02x\n", rllh->c.msg_type); - break; - default: - DEBUGPC(DRLL, "UNKNOWN\n"); - LOGP(DRLL, LOGL_NOTICE, "unknown Abis RLL message " - "type 0x%02x\n", rllh->c.msg_type); - } - return rc; -} - -static uint8_t ipa_smod_s_for_lchan(struct gsm_lchan *lchan) -{ - switch (lchan->tch_mode) { - case GSM48_CMODE_SPEECH_V1: - switch (lchan->type) { - case GSM_LCHAN_TCH_F: - return 0x00; - case GSM_LCHAN_TCH_H: - return 0x03; - default: - break; - } - break; - case GSM48_CMODE_SPEECH_EFR: - switch (lchan->type) { - case GSM_LCHAN_TCH_F: - return 0x01; - /* there's no half-rate EFR */ - default: - break; - } - break; - case GSM48_CMODE_SPEECH_AMR: - switch (lchan->type) { - case GSM_LCHAN_TCH_F: - return 0x02; - case GSM_LCHAN_TCH_H: - return 0x05; - default: - break; - } - break; - default: - break; - } - LOGP(DRSL, LOGL_ERROR, "Cannot determine ip.access speech mode for " - "tch_mode == 0x%02x\n", lchan->tch_mode); - return 0; -} - -static uint8_t ipa_rtp_pt_for_lchan(struct gsm_lchan *lchan) -{ - switch (lchan->tch_mode) { - case GSM48_CMODE_SPEECH_V1: - switch (lchan->type) { - case GSM_LCHAN_TCH_F: - return RTP_PT_GSM_FULL; - case GSM_LCHAN_TCH_H: - return RTP_PT_GSM_HALF; - default: - break; - } - break; - case GSM48_CMODE_SPEECH_EFR: - switch (lchan->type) { - case GSM_LCHAN_TCH_F: - return RTP_PT_GSM_EFR; - /* there's no half-rate EFR */ - default: - break; - } - break; - case GSM48_CMODE_SPEECH_AMR: - switch (lchan->type) { - case GSM_LCHAN_TCH_F: - case GSM_LCHAN_TCH_H: - return RTP_PT_AMR; - default: - break; - } - break; - default: - break; - } - LOGP(DRSL, LOGL_ERROR, "Cannot determine ip.access rtp payload type for " - "tch_mode == 0x%02x\n & lchan_type == %d", - lchan->tch_mode, lchan->type); - return 0; -} - -/* ip.access specific RSL extensions */ -static void ipac_parse_rtp(struct gsm_lchan *lchan, struct tlv_parsed *tv) -{ - struct in_addr ip; - uint16_t port, conn_id; - - if (TLVP_PRESENT(tv, RSL_IE_IPAC_LOCAL_IP)) { - ip.s_addr = tlvp_val32_unal(tv, RSL_IE_IPAC_LOCAL_IP); - DEBUGPC(DRSL, "LOCAL_IP=%s ", inet_ntoa(ip)); - lchan->abis_ip.bound_ip = ntohl(ip.s_addr); - } - - if (TLVP_PRESENT(tv, RSL_IE_IPAC_LOCAL_PORT)) { - port = tlvp_val16_unal(tv, RSL_IE_IPAC_LOCAL_PORT); - port = ntohs(port); - DEBUGPC(DRSL, "LOCAL_PORT=%u ", port); - lchan->abis_ip.bound_port = port; - } - - if (TLVP_PRESENT(tv, RSL_IE_IPAC_CONN_ID)) { - conn_id = tlvp_val16_unal(tv, RSL_IE_IPAC_CONN_ID); - conn_id = ntohs(conn_id); - DEBUGPC(DRSL, "CON_ID=%u ", conn_id); - lchan->abis_ip.conn_id = conn_id; - } - - if (TLVP_PRESENT(tv, RSL_IE_IPAC_RTP_PAYLOAD2)) { - lchan->abis_ip.rtp_payload2 = - *TLVP_VAL(tv, RSL_IE_IPAC_RTP_PAYLOAD2); - DEBUGPC(DRSL, "RTP_PAYLOAD2=0x%02x ", - lchan->abis_ip.rtp_payload2); - } - - if (TLVP_PRESENT(tv, RSL_IE_IPAC_SPEECH_MODE)) { - lchan->abis_ip.speech_mode = - *TLVP_VAL(tv, RSL_IE_IPAC_SPEECH_MODE); - DEBUGPC(DRSL, "speech_mode=0x%02x ", - lchan->abis_ip.speech_mode); - } - - if (TLVP_PRESENT(tv, RSL_IE_IPAC_REMOTE_IP)) { - ip.s_addr = tlvp_val32_unal(tv, RSL_IE_IPAC_REMOTE_IP); - DEBUGPC(DRSL, "REMOTE_IP=%s ", inet_ntoa(ip)); - lchan->abis_ip.connect_ip = ntohl(ip.s_addr); - } - - if (TLVP_PRESENT(tv, RSL_IE_IPAC_REMOTE_PORT)) { - port = tlvp_val16_unal(tv, RSL_IE_IPAC_REMOTE_PORT); - port = ntohs(port); - DEBUGPC(DRSL, "REMOTE_PORT=%u ", port); - lchan->abis_ip.connect_port = port; - } -} - -/*! \brief Issue IPA RSL CRCX to configure RTP on BTS side - * \param[in] lchan Logical Channel for which we issue CRCX - */ -int rsl_ipacc_crcx(struct gsm_lchan *lchan) -{ - struct msgb *msg = rsl_msgb_alloc(); - struct abis_rsl_dchan_hdr *dh; - - dh = (struct abis_rsl_dchan_hdr *) msgb_put(msg, sizeof(*dh)); - init_dchan_hdr(dh, RSL_MT_IPAC_CRCX); - dh->c.msg_discr = ABIS_RSL_MDISC_IPACCESS; - dh->chan_nr = gsm_lchan2chan_nr(lchan); - - /* 0x1- == receive-only, 0x-1 == EFR codec */ - lchan->abis_ip.speech_mode = 0x10 | ipa_smod_s_for_lchan(lchan); - lchan->abis_ip.rtp_payload = ipa_rtp_pt_for_lchan(lchan); - msgb_tv_put(msg, RSL_IE_IPAC_SPEECH_MODE, lchan->abis_ip.speech_mode); - msgb_tv_put(msg, RSL_IE_IPAC_RTP_PAYLOAD, lchan->abis_ip.rtp_payload); - - DEBUGP(DRSL, "%s IPAC_BIND speech_mode=0x%02x RTP_PAYLOAD=%d\n", - gsm_lchan_name(lchan), lchan->abis_ip.speech_mode, - lchan->abis_ip.rtp_payload); - - msg->dst = lchan->ts->trx->rsl_link; - - return abis_rsl_sendmsg(msg); -} - -/*! \brief Issue IPA RSL MDCX to configure MGW-side of RTP - * \param[in] lchan Logical Channel for which we issue MDCX - * \param[in] ip Remote (MGW) IP address for RTP - * \param[in] port Remote (MGW) UDP port number for RTP - * \param[in] rtp_payload2 Contents of RTP PAYLOAD 2 IE - */ -int rsl_ipacc_mdcx(struct gsm_lchan *lchan, uint32_t ip, uint16_t port, - uint8_t rtp_payload2) -{ - struct msgb *msg = rsl_msgb_alloc(); - struct abis_rsl_dchan_hdr *dh; - uint32_t *att_ip; - struct in_addr ia; - - dh = (struct abis_rsl_dchan_hdr *) msgb_put(msg, sizeof(*dh)); - init_dchan_hdr(dh, RSL_MT_IPAC_MDCX); - dh->c.msg_discr = ABIS_RSL_MDISC_IPACCESS; - dh->chan_nr = gsm_lchan2chan_nr(lchan); - - /* we need to store these now as MDCX_ACK does not return them :( */ - lchan->abis_ip.rtp_payload2 = rtp_payload2; - lchan->abis_ip.connect_port = port; - lchan->abis_ip.connect_ip = ip; - - /* 0x0- == both directions, 0x-1 == EFR codec */ - lchan->abis_ip.speech_mode = 0x00 | ipa_smod_s_for_lchan(lchan); - lchan->abis_ip.rtp_payload = ipa_rtp_pt_for_lchan(lchan); - - ia.s_addr = htonl(ip); - DEBUGP(DRSL, "%s IPAC_MDCX IP=%s PORT=%d RTP_PAYLOAD=%d RTP_PAYLOAD2=%d " - "CONN_ID=%d speech_mode=0x%02x\n", gsm_lchan_name(lchan), - inet_ntoa(ia), port, lchan->abis_ip.rtp_payload, rtp_payload2, - lchan->abis_ip.conn_id, lchan->abis_ip.speech_mode); - - msgb_tv16_put(msg, RSL_IE_IPAC_CONN_ID, lchan->abis_ip.conn_id); - msgb_v_put(msg, RSL_IE_IPAC_REMOTE_IP); - att_ip = (uint32_t *) msgb_put(msg, sizeof(ip)); - *att_ip = ia.s_addr; - msgb_tv16_put(msg, RSL_IE_IPAC_REMOTE_PORT, port); - msgb_tv_put(msg, RSL_IE_IPAC_SPEECH_MODE, lchan->abis_ip.speech_mode); - msgb_tv_put(msg, RSL_IE_IPAC_RTP_PAYLOAD, lchan->abis_ip.rtp_payload); - if (rtp_payload2) - msgb_tv_put(msg, RSL_IE_IPAC_RTP_PAYLOAD2, rtp_payload2); - - msg->dst = lchan->ts->trx->rsl_link; - - return abis_rsl_sendmsg(msg); -} - -/* tell BTS to connect RTP stream to our local RTP socket */ -int rsl_ipacc_mdcx_to_rtpsock(struct gsm_lchan *lchan) -{ - struct rtp_socket *rs = lchan->abis_ip.rtp_socket; - int rc; - - rc = rsl_ipacc_mdcx(lchan, ntohl(rs->rtp.sin_local.sin_addr.s_addr), - ntohs(rs->rtp.sin_local.sin_port), - /* FIXME: use RTP payload of bound socket, not BTS*/ - lchan->abis_ip.rtp_payload2); - - return rc; -} - -int rsl_ipacc_pdch_activate(struct gsm_bts_trx_ts *ts, int act) -{ - struct msgb *msg = rsl_msgb_alloc(); - struct abis_rsl_dchan_hdr *dh; - uint8_t msg_type; - - if (ts->flags & TS_F_PDCH_PENDING_MASK) { - LOGP(DRSL, LOGL_ERROR, - "%s PDCH %s requested, but a PDCH%s%s is still pending\n", - gsm_ts_name(ts), - act ? "ACT" : "DEACT", - ts->flags & TS_F_PDCH_ACT_PENDING? " ACT" : "", - ts->flags & TS_F_PDCH_DEACT_PENDING? " DEACT" : ""); - return -EINVAL; - } - - if (act){ - /* Callers should heed the GPRS mode. */ - OSMO_ASSERT(ts->trx->bts->gprs.mode != BTS_GPRS_NONE); - msg_type = RSL_MT_IPAC_PDCH_ACT; - ts->flags |= TS_F_PDCH_ACT_PENDING; - } else { - msg_type = RSL_MT_IPAC_PDCH_DEACT; - ts->flags |= TS_F_PDCH_DEACT_PENDING; - } - /* TODO add timeout to cancel PDCH DE/ACT */ - - dh = (struct abis_rsl_dchan_hdr *) msgb_put(msg, sizeof(*dh)); - init_dchan_hdr(dh, msg_type); - dh->c.msg_discr = ABIS_RSL_MDISC_DED_CHAN; - dh->chan_nr = gsm_pchan2chan_nr(GSM_PCHAN_PDCH, ts->nr, 0); - - DEBUGP(DRSL, "%s IPAC PDCH %sACT\n", gsm_ts_name(ts), - act ? "" : "DE"); - - msg->dst = ts->trx->rsl_link; - - return abis_rsl_sendmsg(msg); -} - -static int abis_rsl_rx_ipacc_crcx_ack(struct msgb *msg) -{ - struct abis_rsl_dchan_hdr *dh = msgb_l2(msg); - struct tlv_parsed tv; - struct gsm_lchan *lchan = msg->lchan; - - /* the BTS has acknowledged a local bind, it now tells us the IP - * address and port number to which it has bound the given logical - * channel */ - - rsl_tlv_parse(&tv, dh->data, msgb_l2len(msg)-sizeof(*dh)); - if (!TLVP_PRESENT(&tv, RSL_IE_IPAC_LOCAL_PORT) || - !TLVP_PRESENT(&tv, RSL_IE_IPAC_LOCAL_IP) || - !TLVP_PRESENT(&tv, RSL_IE_IPAC_CONN_ID)) { - LOGP(DRSL, LOGL_NOTICE, "mandatory IE missing"); - return -EINVAL; - } - - ipac_parse_rtp(lchan, &tv); - - osmo_signal_dispatch(SS_ABISIP, S_ABISIP_CRCX_ACK, msg->lchan); - - return 0; -} - -static int abis_rsl_rx_ipacc_mdcx_ack(struct msgb *msg) -{ - struct abis_rsl_dchan_hdr *dh = msgb_l2(msg); - struct tlv_parsed tv; - struct gsm_lchan *lchan = msg->lchan; - - /* the BTS has acknowledged a remote connect request and - * it now tells us the IP address and port number to which it has - * connected the given logical channel */ - - rsl_tlv_parse(&tv, dh->data, msgb_l2len(msg)-sizeof(*dh)); - ipac_parse_rtp(lchan, &tv); - osmo_signal_dispatch(SS_ABISIP, S_ABISIP_MDCX_ACK, msg->lchan); - - return 0; -} - -static int abis_rsl_rx_ipacc_dlcx_ind(struct msgb *msg) -{ - struct abis_rsl_dchan_hdr *dh = msgb_l2(msg); - struct tlv_parsed tv; - - rsl_tlv_parse(&tv, dh->data, msgb_l2len(msg)-sizeof(*dh)); - - if (TLVP_PRESENT(&tv, RSL_IE_CAUSE)) - print_rsl_cause(LOGL_DEBUG, TLVP_VAL(&tv, RSL_IE_CAUSE), - TLVP_LEN(&tv, RSL_IE_CAUSE)); - - osmo_signal_dispatch(SS_ABISIP, S_ABISIP_DLCX_IND, msg->lchan); - - return 0; -} - -static int abis_rsl_rx_ipacc(struct msgb *msg) -{ - struct e1inp_sign_link *sign_link = msg->dst; - struct abis_rsl_rll_hdr *rllh = msgb_l2(msg); - char *ts_name; - int rc = 0; - - msg->lchan = lchan_lookup(sign_link->trx, rllh->chan_nr, - "Abis RSL rx IPACC: "); - ts_name = gsm_lchan_name(msg->lchan); - - switch (rllh->c.msg_type) { - case RSL_MT_IPAC_CRCX_ACK: - DEBUGP(DRSL, "%s IPAC_CRCX_ACK ", ts_name); - rc = abis_rsl_rx_ipacc_crcx_ack(msg); - break; - case RSL_MT_IPAC_CRCX_NACK: - /* somehow the BTS was unable to bind the lchan to its local - * port?!? */ - LOGP(DRSL, LOGL_ERROR, "%s IPAC_CRCX_NACK\n", ts_name); - break; - case RSL_MT_IPAC_MDCX_ACK: - /* the BTS tells us that a connect operation was successful */ - DEBUGP(DRSL, "%s IPAC_MDCX_ACK ", ts_name); - rc = abis_rsl_rx_ipacc_mdcx_ack(msg); - break; - case RSL_MT_IPAC_MDCX_NACK: - /* somehow the BTS was unable to connect the lchan to a remote - * port */ - LOGP(DRSL, LOGL_ERROR, "%s IPAC_MDCX_NACK\n", ts_name); - break; - case RSL_MT_IPAC_DLCX_IND: - DEBUGP(DRSL, "%s IPAC_DLCX_IND ", ts_name); - rc = abis_rsl_rx_ipacc_dlcx_ind(msg); - break; - default: - LOGP(DRSL, LOGL_NOTICE, "Unknown ip.access msg_type 0x%02x\n", - rllh->c.msg_type); - break; - } - DEBUGPC(DRSL, "\n"); - - return rc; -} - -int dyn_ts_switchover_start(struct gsm_bts_trx_ts *ts, - enum gsm_phys_chan_config to_pchan) -{ - int ss; - int rc = -EIO; - - OSMO_ASSERT(ts->pchan == GSM_PCHAN_TCH_F_TCH_H_PDCH); - DEBUGP(DRSL, "%s starting switchover to %s\n", - gsm_ts_and_pchan_name(ts), gsm_pchan_name(to_pchan)); - - if (ts->dyn.pchan_is != ts->dyn.pchan_want) { - LOGP(DRSL, LOGL_ERROR, - "%s: Attempt to switch dynamic channel to %s," - " but is already in switchover.\n", - gsm_ts_and_pchan_name(ts), - gsm_pchan_name(to_pchan)); - return ts->dyn.pchan_want == to_pchan? 0 : -EAGAIN; - } - - if (ts->dyn.pchan_is == to_pchan) { - LOGP(DRSL, LOGL_INFO, - "%s %s Already is in %s mode, cannot switchover.\n", - gsm_ts_name(ts), gsm_pchan_name(ts->pchan), - gsm_pchan_name(to_pchan)); - return -EINVAL; - } - - /* Paranoia: let's make sure all is indeed released. */ - for (ss = 0; ss < ts_subslots(ts); ss++) { - struct gsm_lchan *lc = &ts->lchan[ss]; - if (lc->state != LCHAN_S_NONE) { - LOGP(DRSL, LOGL_ERROR, - "%s Attempt to switch dynamic channel to %s," - " but is not fully released.\n", - gsm_ts_and_pchan_name(ts), - gsm_pchan_name(to_pchan)); - return -EAGAIN; - } - } - - /* Record that we're busy switching. */ - ts->dyn.pchan_want = to_pchan; - - /* - * To switch from PDCH, we need to initiate the release from the BSC - * side. dyn_ts_switchover_continue() will be called from - * rsl_rx_rf_chan_rel_ack(). PDCH is always on lchan[0]. - */ - if (ts->dyn.pchan_is == GSM_PCHAN_PDCH) { - rsl_lchan_set_state(ts->lchan, LCHAN_S_REL_REQ); - rc = rsl_rf_chan_release(ts->lchan, 0, SACCH_NONE); - if (rc) { - LOGP(DRSL, LOGL_ERROR, - "%s RSL RF Chan Release failed\n", - gsm_ts_and_pchan_name(ts)); - return dyn_ts_switchover_failed(ts, rc); - } - return 0; - } - - /* - * To switch from TCH/F and TCH/H pchans, this has been called from - * rsl_rx_rf_chan_rel_ack(), i.e. release is complete. Go ahead and - * activate as new type. This will always be PDCH. - */ - return dyn_ts_switchover_continue(ts); -} - -static int dyn_ts_switchover_continue(struct gsm_bts_trx_ts *ts) -{ - int rc; - uint8_t act_type; - uint8_t ho_ref; - int ss; - struct gsm_lchan *lchan; - - OSMO_ASSERT(ts->pchan == GSM_PCHAN_TCH_F_TCH_H_PDCH); - DEBUGP(DRSL, "%s switchover: release complete," - " activating new pchan type\n", - gsm_ts_and_pchan_name(ts)); - - if (ts->dyn.pchan_is == ts->dyn.pchan_want) { - LOGP(DRSL, LOGL_ERROR, - "%s Requested to switchover dynamic channel to the" - " same type it is already in.\n", - gsm_ts_and_pchan_name(ts)); - return 0; - } - - for (ss = 0; ss < ts_subslots(ts); ss++) { - lchan = &ts->lchan[ss]; - if (lchan->rqd_ref) { - LOGP(DRSL, LOGL_ERROR, - "%s During dyn TS switchover, expecting no" - " Request Reference to be pending. Discarding!\n", - gsm_lchan_name(lchan)); - talloc_free(lchan->rqd_ref); - lchan->rqd_ref = NULL; - } - } - - /* - * When switching pchan modes, all lchans are unused. So always - * activate whatever wants to be activated on the first lchan. (We - * wouldn't remember to use lchan[1] across e.g. a PDCH deact anyway) - */ - lchan = ts->lchan; - - /* - * For TCH/x, the lchan->type has been set in lchan_alloc(), but it may - * have been lost during channel release due to dynamic switchover. - * - * For PDCH, the lchan->type will actually remain NONE. - * TODO: set GSM_LCHAN_PDTCH? - */ - switch (ts->dyn.pchan_want) { - case GSM_PCHAN_TCH_F: - lchan->type = GSM_LCHAN_TCH_F; - break; - case GSM_PCHAN_TCH_H: - lchan->type = GSM_LCHAN_TCH_H; - break; - case GSM_PCHAN_PDCH: - lchan->type = GSM_LCHAN_NONE; - break; - default: - LOGP(DRSL, LOGL_ERROR, - "%s Invalid target pchan for dynamic TS\n", - gsm_ts_and_pchan_name(ts)); - } - - act_type = (ts->dyn.pchan_want == GSM_PCHAN_PDCH) - ? RSL_ACT_OSMO_PDCH - : lchan->dyn.act_type; - ho_ref = (ts->dyn.pchan_want == GSM_PCHAN_PDCH) - ? 0 - : lchan->dyn.ho_ref; - - /* Fetch the rqd_ref back from before switchover started. */ - lchan->rqd_ref = lchan->dyn.rqd_ref; - lchan->rqd_ta = lchan->dyn.rqd_ta; - lchan->dyn.rqd_ref = NULL; - lchan->dyn.rqd_ta = 0; - - /* During switchover, we have received a release ack, which means that - * the act_timer has been stopped. Start the timer again so we mark - * this channel broken if the activation ack comes too late. */ - osmo_timer_setup(&lchan->act_timer, lchan_act_tmr_cb, lchan); - osmo_timer_schedule(&lchan->act_timer, 4, 0); - - rc = rsl_chan_activate_lchan(lchan, act_type, ho_ref); - if (rc) { - LOGP(DRSL, LOGL_ERROR, - "%s RSL Chan Activate failed\n", - gsm_ts_and_pchan_name(ts)); - return dyn_ts_switchover_failed(ts, rc); - } - return 0; -} - -static int dyn_ts_switchover_failed(struct gsm_bts_trx_ts *ts, int rc) -{ - ts->dyn.pchan_want = ts->dyn.pchan_is; - LOGP(DRSL, LOGL_ERROR, "%s Error %d during dynamic channel switchover." - " Going back to previous pchan.\n", gsm_ts_and_pchan_name(ts), - rc); - return rc; -} - -static void dyn_ts_switchover_complete(struct gsm_lchan *lchan) -{ - enum gsm_phys_chan_config pchan_act; - enum gsm_phys_chan_config pchan_was; - struct gsm_bts_trx_ts *ts = lchan->ts; - - OSMO_ASSERT(ts->pchan == GSM_PCHAN_TCH_F_TCH_H_PDCH); - - pchan_act = pchan_for_lchant(lchan->type); - /* - * Paranoia: do the types match? - * In case of errors: we've received an act ack already, so what to do - * about it? Logging the error should suffice for now. - */ - if (pchan_act != ts->dyn.pchan_want) - LOGP(DRSL, LOGL_ERROR, - "%s Requested transition does not match lchan type %s\n", - gsm_ts_and_pchan_name(ts), - gsm_lchant_name(lchan->type)); - - pchan_was = ts->dyn.pchan_is; - ts->dyn.pchan_is = ts->dyn.pchan_want = pchan_act; - - if (pchan_was != ts->dyn.pchan_is) - LOGP(DRSL, LOGL_INFO, "%s switchover from %s complete.\n", - gsm_ts_and_pchan_name(ts), gsm_pchan_name(pchan_was)); -} - -/* Entry-point where L2 RSL from BTS enters */ -int abis_rsl_rcvmsg(struct msgb *msg) -{ - struct abis_rsl_common_hdr *rslh; - int rc = 0; - - if (!msg) { - DEBUGP(DRSL, "Empty RSL msg?..\n"); - return -1; - } - - if (msgb_l2len(msg) < sizeof(*rslh)) { - DEBUGP(DRSL, "Truncated RSL message with l2len: %u\n", msgb_l2len(msg)); - msgb_free(msg); - return -1; - } - - rslh = msgb_l2(msg); - - switch (rslh->msg_discr & 0xfe) { - case ABIS_RSL_MDISC_RLL: - rc = abis_rsl_rx_rll(msg); - break; - case ABIS_RSL_MDISC_DED_CHAN: - rc = abis_rsl_rx_dchan(msg); - break; - case ABIS_RSL_MDISC_COM_CHAN: - rc = abis_rsl_rx_cchan(msg); - break; - case ABIS_RSL_MDISC_TRX: - rc = abis_rsl_rx_trx(msg); - break; - case ABIS_RSL_MDISC_LOC: - LOGP(DRSL, LOGL_NOTICE, "unimplemented RSL msg disc 0x%02x\n", - rslh->msg_discr); - break; - case ABIS_RSL_MDISC_IPACCESS: - rc = abis_rsl_rx_ipacc(msg); - break; - default: - LOGP(DRSL, LOGL_NOTICE, "unknown RSL message discriminator " - "0x%02x\n", rslh->msg_discr); - rc = -EINVAL; - } - msgb_free(msg); - return rc; -} - -int rsl_sms_cb_command(struct gsm_bts *bts, uint8_t chan_number, - struct rsl_ie_cb_cmd_type cb_command, - const uint8_t *data, int len) -{ - struct abis_rsl_dchan_hdr *dh; - struct msgb *cb_cmd; - - cb_cmd = rsl_msgb_alloc(); - if (!cb_cmd) - return -1; - - dh = (struct abis_rsl_dchan_hdr *) msgb_put(cb_cmd, sizeof(*dh)); - init_dchan_hdr(dh, RSL_MT_SMS_BC_CMD); - dh->c.msg_discr = ABIS_RSL_MDISC_COM_CHAN; - dh->chan_nr = chan_number; /* TODO: check the chan config */ - - msgb_tv_put(cb_cmd, RSL_IE_CB_CMD_TYPE, *(uint8_t*)&cb_command); - msgb_tlv_put(cb_cmd, RSL_IE_SMSCB_MSG, len, data); - - cb_cmd->dst = bts->c0->rsl_link; - - return abis_rsl_sendmsg(cb_cmd); -} - -int rsl_nokia_si_begin(struct gsm_bts_trx *trx) -{ - struct abis_rsl_common_hdr *ch; - struct msgb *msg = rsl_msgb_alloc(); - - ch = (struct abis_rsl_common_hdr *) msgb_put(msg, sizeof(*ch)); - ch->msg_discr = ABIS_RSL_MDISC_TRX; - ch->msg_type = 0x40; /* Nokia SI Begin */ - - msg->dst = trx->rsl_link; - - return abis_rsl_sendmsg(msg); -} - -int rsl_nokia_si_end(struct gsm_bts_trx *trx) -{ - struct abis_rsl_common_hdr *ch; - struct msgb *msg = rsl_msgb_alloc(); - - ch = (struct abis_rsl_common_hdr *) msgb_put(msg, sizeof(*ch)); - ch->msg_discr = ABIS_RSL_MDISC_TRX; - ch->msg_type = 0x41; /* Nokia SI End */ - - msgb_tv_put(msg, 0xFD, 0x00); /* Nokia Pagemode Info, No paging reorganisation required */ - - msg->dst = trx->rsl_link; - - return abis_rsl_sendmsg(msg); -} - -int rsl_bs_power_control(struct gsm_bts_trx *trx, uint8_t channel, uint8_t reduction) -{ - struct abis_rsl_common_hdr *ch; - struct msgb *msg = rsl_msgb_alloc(); - - ch = (struct abis_rsl_common_hdr *) msgb_put(msg, sizeof(*ch)); - ch->msg_discr = ABIS_RSL_MDISC_DED_CHAN; - ch->msg_type = RSL_MT_BS_POWER_CONTROL; - - msgb_tv_put(msg, RSL_IE_CHAN_NR, channel); - msgb_tv_put(msg, RSL_IE_BS_POWER, reduction); /* reduction in 2dB steps */ - - msg->dst = trx->rsl_link; - - return abis_rsl_sendmsg(msg); -} - -/** - * Release all allocated SAPIs starting from @param start and - * release them with the given release mode. Once the release - * confirmation arrives it will be attempted to release the - * the RF channel. - */ -int rsl_release_sapis_from(struct gsm_lchan *lchan, int start, - enum rsl_rel_mode release_mode) -{ - int no_sapi = 1; - int sapi; - - for (sapi = start; sapi < ARRAY_SIZE(lchan->sapis); ++sapi) { - uint8_t link_id; - if (lchan->sapis[sapi] == LCHAN_SAPI_UNUSED) - continue; - - link_id = sapi; - if (lchan->type == GSM_LCHAN_TCH_F || lchan->type == GSM_LCHAN_TCH_H) - link_id |= 0x40; - rsl_release_request(lchan, link_id, release_mode); - no_sapi = 0; - } - - return no_sapi; -} - -int rsl_start_t3109(struct gsm_lchan *lchan) -{ - struct gsm_bts *bts = lchan->ts->trx->bts; - - /* Disabled, mostly legacy code */ - if (bts->network->T3109 == 0) - return -1; - - osmo_timer_setup(&lchan->T3109, t3109_expired, lchan); - osmo_timer_schedule(&lchan->T3109, bts->network->T3109, 0); - return 0; -} - -/** - * \brief directly RF Channel Release the lchan - * - * When no SAPI was allocated, directly release the logical channel. This - * should only be called from chan_alloc.c on channel release handling. In - * case no SAPI was established the RF Channel can be directly released, - */ -int rsl_direct_rf_release(struct gsm_lchan *lchan) -{ - int i; - for (i = 0; i < ARRAY_SIZE(lchan->sapis); ++i) { - if (lchan->sapis[i] != LCHAN_SAPI_UNUSED) { - LOGP(DRSL, LOGL_ERROR, "%s SAPI(%d) still allocated.\n", - gsm_lchan_name(lchan), i); - return -1; - } - } - - /* Now release it */ - return rsl_rf_chan_release(lchan, 0, SACCH_NONE); -} diff --git a/openbsc/src/libbsc/arfcn_range_encode.c b/openbsc/src/libbsc/arfcn_range_encode.c deleted file mode 100644 index 9ca48407..00000000 --- a/openbsc/src/libbsc/arfcn_range_encode.c +++ /dev/null @@ -1,330 +0,0 @@ -/* gsm 04.08 system information (si) encoding and decoding - * 3gpp ts 04.08 version 7.21.0 release 1998 / etsi ts 100 940 v7.21.0 */ - -/* - * (C) 2012 Holger Hans Peter Freyther - * (C) 2012 by On-Waves - * All Rights Reserved - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation; either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - */ - -#include -#include - -#include - -#include - -#include - -static inline int greatest_power_of_2_lesser_or_equal_to(int index) -{ - int power_of_2 = 1; - - do { - power_of_2 *= 2; - } while (power_of_2 <= index); - - /* now go back one step */ - return power_of_2 / 2; -} - -static inline int mod(int data, int range) -{ - int res = data % range; - while (res < 0) - res += range; - return res; -} - -/** - * Determine at which index to split the ARFCNs to create an - * equally size partition for the given range. Return -1 if - * no such partition exists. - */ -int range_enc_find_index(enum gsm48_range range, const int *freqs, const int size) -{ - int i, j, n; - - const int RANGE_DELTA = (range - 1) / 2; - - for (i = 0; i < size; ++i) { - n = 0; - for (j = 0; j < size; ++j) { - if (mod(freqs[j] - freqs[i], range) <= RANGE_DELTA) - n += 1; - } - - if (n - 1 == (size - 1) / 2) - return i; - } - - return -1; -} - -/** - * Range encode the ARFCN list. - * \param range The range to use. - * \param arfcns The list of ARFCNs - * \param size The size of the list of ARFCNs - * \param out Place to store the W(i) output. - */ -int range_enc_arfcns(enum gsm48_range range, - const int *arfcns, int size, int *out, - const int index) -{ - int split_at; - int i; - - /* - * The below is a GNU extension and we can remove it when - * we move to a quicksort like in-situ swap with the pivot. - */ - int arfcns_left[size / 2]; - int arfcns_right[size / 2]; - int l_size; - int r_size; - int l_origin; - int r_origin; - - - /* Test the two recursion anchors and stop processing */ - if (size == 0) - return 0; - - if (size == 1) { - out[index] = 1 + arfcns[0]; - return 0; - } - - /* Now do the processing */ - split_at = range_enc_find_index(range, arfcns, size); - if (split_at < 0) - return -EINVAL; - - /* we now know where to split */ - out[index] = 1 + arfcns[split_at]; - - /* calculate the work that needs to be done for the leafs */ - l_origin = mod(arfcns[split_at] + ((range - 1) / 2) + 1, range); - r_origin = mod(arfcns[split_at] + 1, range); - for (i = 0, l_size = 0, r_size = 0; i < size; ++i) { - if (mod(arfcns[i] - l_origin, range) < range / 2) - arfcns_left[l_size++] = mod(arfcns[i] - l_origin, range); - if (mod(arfcns[i] - r_origin, range) < range / 2) - arfcns_right[r_size++] = mod(arfcns[i] - r_origin, range); - } - - /* - * Now recurse and we need to make this iterative... but as the - * tree is balanced the stack will not be too deep. - */ - if (l_size) - range_enc_arfcns(range / 2, arfcns_left, l_size, - out, index + greatest_power_of_2_lesser_or_equal_to(index + 1)); - if (r_size) - range_enc_arfcns((range - 1) / 2, arfcns_right, r_size, - out, index + (2 * greatest_power_of_2_lesser_or_equal_to(index + 1))); - return 0; -} - -/* - * The easiest is to use f0 == arfcns[0]. This means that under certain - * circumstances we can encode less ARFCNs than possible with an optimal f0. - * - * TODO: Solve the optimisation problem and pick f0 so that the max distance - * is the smallest. Taking into account the modulo operation. I think picking - * size/2 will be the optimal arfcn. - */ -/** - * This implements the range determination as described in GSM 04.08 J4. The - * result will be a base frequency f0 and the range to use. Note that for range - * 1024 encoding f0 always refers to ARFCN 0 even if it is not an element of - * the arfcns list. - * - * \param[in] arfcns The input frequencies, they must be sorted, lowest number first - * \param[in] size The length of the array - * \param[out] f0 The selected F0 base frequency. It might not be inside the list - */ -int range_enc_determine_range(const int *arfcns, const int size, int *f0) -{ - int max = 0; - - /* - * Go for the easiest. And pick arfcns[0] == f0. - */ - max = arfcns[size - 1] - arfcns[0]; - *f0 = arfcns[0]; - - if (max < 128 && size <= 29) - return ARFCN_RANGE_128; - if (max < 256 && size <= 22) - return ARFCN_RANGE_256; - if (max < 512 && size <= 18) - return ARFCN_RANGE_512; - if (max < 1024 && size <= 17) { - *f0 = 0; - return ARFCN_RANGE_1024; - } - - return ARFCN_RANGE_INVALID; -} - -static void write_orig_arfcn(uint8_t *chan_list, int f0) -{ - chan_list[0] |= (f0 >> 9) & 1; - chan_list[1] = (f0 >> 1); - chan_list[2] = (f0 & 1) << 7; -} - -static void write_all_wn(uint8_t *chan_list, int bit_offs, - int *w, int w_size, int w1_len) -{ - int octet_offs = 0; /* offset into chan_list */ - int wk_len = w1_len; /* encoding size in bits of w[k] */ - int k; /* 1 based */ - int level = 0; /* tree level, top level = 0 */ - int lvl_left = 1; /* nodes per tree level */ - - /* W(2^i) to W(2^(i+1)-1) are on w1_len-i bits when present */ - - for (k = 1; k <= w_size; k++) { - int wk_left = wk_len; - DEBUGP(DRR, - "k=%d, wk_len=%d, offs=%d:%d, level=%d, " - "lvl_left=%d\n", - k, wk_len, octet_offs, bit_offs, level, lvl_left); - - while (wk_left > 0) { - int cur_bits = 8 - bit_offs; - int cur_mask; - int wk_slice; - - if (cur_bits > wk_left) - cur_bits = wk_left; - - cur_mask = ((1 << cur_bits) - 1); - - DEBUGP(DRR, - " wk_left=%d, cur_bits=%d, offs=%d:%d\n", - wk_left, cur_bits, octet_offs, bit_offs); - - /* advance */ - wk_left -= cur_bits; - bit_offs += cur_bits; - - /* right aligned wk data for current out octet */ - wk_slice = (w[k-1] >> wk_left) & cur_mask; - - /* cur_bits now contains the number of bits - * that are to be copied from wk to the chan_list. - * wk_left is set to the number of bits that must - * not yet be copied. - * bit_offs points after the bit area that is going to - * be overwritten: - * - * wk_left - * | - * v - * wk: WWWWWWWWWWW - * |||||<-- wk_slice, cur_bits=5 - * --WWWWW- - * ^ - * | - * bit_offs - */ - - DEBUGP(DRR, - " wk=%02x, slice=%02x/%02x, cl=%02x\n", - w[k-1], wk_slice, cur_mask, wk_slice << (8 - bit_offs)); - - chan_list[octet_offs] &= ~(cur_mask << (8 - bit_offs)); - chan_list[octet_offs] |= wk_slice << (8 - bit_offs); - - /* adjust output */ - if (bit_offs == 8) { - bit_offs = 0; - octet_offs += 1; - } - } - - /* adjust bit sizes */ - lvl_left -= 1; - if (!lvl_left) { - /* completed tree level, advance to next */ - level += 1; - lvl_left = 1 << level; - wk_len -= 1; - } - } -} - -int range_enc_range128(uint8_t *chan_list, int f0, int *w) -{ - chan_list[0] = 0x8C; - write_orig_arfcn(chan_list, f0); - - write_all_wn(&chan_list[2], 1, w, 28, 7); - return 0; -} - -int range_enc_range256(uint8_t *chan_list, int f0, int *w) -{ - chan_list[0] = 0x8A; - write_orig_arfcn(chan_list, f0); - - write_all_wn(&chan_list[2], 1, w, 21, 8); - return 0; -} - -int range_enc_range512(uint8_t *chan_list, int f0, int *w) -{ - chan_list[0] = 0x88; - write_orig_arfcn(chan_list, f0); - - write_all_wn(&chan_list[2], 1, w, 17, 9); - return 0; -} - -int range_enc_range1024(uint8_t *chan_list, int f0, int f0_included, int *w) -{ - chan_list[0] = 0x80 | (f0_included << 2); - - write_all_wn(&chan_list[0], 6, w, 16, 10); - return 0; -} - -int range_enc_filter_arfcns(int *arfcns, - const int size, const int f0, int *f0_included) -{ - int i, j = 0; - *f0_included = 0; - - for (i = 0; i < size; ++i) { - /* - * Appendix J.4 says the following: - * All frequencies except F(0), minus F(0) + 1. - * I assume we need to exclude it here. - */ - if (arfcns[i] == f0) { - *f0_included = 1; - continue; - } - - arfcns[j++] = mod(arfcns[i] - (f0 + 1), 1024); - } - - return j; -} diff --git a/openbsc/src/libbsc/bsc_api.c b/openbsc/src/libbsc/bsc_api.c deleted file mode 100644 index 7613cac9..00000000 --- a/openbsc/src/libbsc/bsc_api.c +++ /dev/null @@ -1,894 +0,0 @@ -/* GSM 08.08 like API for OpenBSC. The bridge from MSC to BSC */ - -/* (C) 2010-2011 by Holger Hans Peter Freyther - * (C) 2010-2011 by On-Waves - * (C) 2009 by Harald Welte - * - * All Rights Reserved - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation; either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public 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 - -#define GSM0808_T10_VALUE 6, 0 - - -static void rll_ind_cb(struct gsm_lchan *, uint8_t, void *, enum bsc_rllr_ind); -static void send_sapi_reject(struct gsm_subscriber_connection *conn, int link_id); -static void handle_release(struct gsm_subscriber_connection *conn, struct bsc_api *bsc, struct gsm_lchan *lchan); -static void handle_chan_ack(struct gsm_subscriber_connection *conn, struct bsc_api *bsc, struct gsm_lchan *lchan); -static void handle_chan_nack(struct gsm_subscriber_connection *conn, struct bsc_api *bsc, struct gsm_lchan *lchan); - -/* GSM 08.08 3.2.2.33 */ -static uint8_t lchan_to_chosen_channel(struct gsm_lchan *lchan) -{ - uint8_t channel_mode = 0, channel = 0; - - switch (lchan->tch_mode) { - case GSM48_CMODE_SPEECH_V1: - case GSM48_CMODE_SPEECH_EFR: - case GSM48_CMODE_SPEECH_AMR: - channel_mode = 0x9; - break; - case GSM48_CMODE_SIGN: - channel_mode = 0x8; - break; - case GSM48_CMODE_DATA_14k5: - channel_mode = 0xe; - break; - case GSM48_CMODE_DATA_12k0: - channel_mode = 0xb; - break; - case GSM48_CMODE_DATA_6k0: - channel_mode = 0xc; - break; - case GSM48_CMODE_DATA_3k6: - channel_mode = 0xd; - break; - } - - switch (lchan->type) { - case GSM_LCHAN_NONE: - channel = 0x0; - break; - case GSM_LCHAN_SDCCH: - channel = 0x1; - break; - case GSM_LCHAN_TCH_F: - channel = 0x8; - break; - case GSM_LCHAN_TCH_H: - channel = 0x9; - break; - case GSM_LCHAN_UNKNOWN: - default: - LOGP(DMSC, LOGL_ERROR, "Unknown lchan type: %p\n", lchan); - break; - } - - return channel_mode << 4 | channel; -} - -static uint8_t chan_mode_to_speech(struct gsm_lchan *lchan) -{ - int mode = 0; - - switch (lchan->tch_mode) { - case GSM48_CMODE_SPEECH_V1: - mode = 1; - break; - case GSM48_CMODE_SPEECH_EFR: - mode = 0x11; - break; - case GSM48_CMODE_SPEECH_AMR: - mode = 0x21; - break; - case GSM48_CMODE_SIGN: - case GSM48_CMODE_DATA_14k5: - case GSM48_CMODE_DATA_12k0: - case GSM48_CMODE_DATA_6k0: - case GSM48_CMODE_DATA_3k6: - default: - LOGP(DMSC, LOGL_ERROR, "Using non speech mode: %d\n", mode); - return 0; - break; - } - - /* assume to always do AMR HR on any TCH type */ - if (lchan->type == GSM_LCHAN_TCH_H || - lchan->tch_mode == GSM48_CMODE_SPEECH_AMR) - mode |= 0x4; - - return mode; -} - -static void assignment_t10_timeout(void *_conn) -{ - struct bsc_api *api; - struct gsm_subscriber_connection *conn = - (struct gsm_subscriber_connection *) _conn; - - LOGP(DMSC, LOGL_ERROR, "Assignment T10 timeout on %p\n", conn); - - /* - * normal release on the secondary channel but only if the - * secondary_channel has not been released by the handle_chan_nack. - */ - if (conn->secondary_lchan) - lchan_release(conn->secondary_lchan, 0, RSL_REL_LOCAL_END); - conn->secondary_lchan = NULL; - - /* inform them about the failure */ - api = conn->network->bsc_api; - api->assign_fail(conn, GSM0808_CAUSE_NO_RADIO_RESOURCE_AVAILABLE, NULL); -} - -/*! \brief Determine and apply AMR multi-rate configuration to lchan - * Determine which AMR multi-rate configuration to use and apply it to - * the lchan (so it can be communicated to BTS and MS during channel - * activation. - * \param[in] conn subscriber connection (used to resolve bsc_api) - * \param[out] lchan logical channel to which to apply mr config - * \param[in] full_rate whether to use full-rate (1) or half-rate (0) config - */ -static void handle_mr_config(struct gsm_subscriber_connection *conn, - struct gsm_lchan *lchan, int full_rate) -{ - struct bsc_api *api; - api = conn->network->bsc_api; - struct amr_multirate_conf *mr; - struct gsm48_multi_rate_conf *mr_conf; - - /* BSC api override for this method, used in OsmoBSC mode with - * bsc_mr_config() to use MSC-specific/specified configuration */ - if (api->mr_config) - return api->mr_config(conn, lchan, full_rate); - - /* NITB case: use the BTS-specic multi-rate configuration from - * the vty/configuration file */ - if (full_rate) - mr = &lchan->ts->trx->bts->mr_full; - else - mr = &lchan->ts->trx->bts->mr_half; - - mr_conf = (struct gsm48_multi_rate_conf *) mr->gsm48_ie; - mr_conf->ver = 1; - - /* default, if no AMR codec defined */ - if (!mr->gsm48_ie[1]) { - mr_conf->icmi = 1; - mr_conf->m5_90 = 1; - } - /* store encoded MR config IE lchan for both MS (uplink) and BTS - * (downlink) directions */ - gsm48_multirate_config(lchan->mr_ms_lv, mr, mr->ms_mode); - gsm48_multirate_config(lchan->mr_bts_lv, mr, mr->bts_mode); -} - -/* - * Start a new assignment and make sure that it is completed within T10 either - * positively, negatively or by the timeout. - * - * 1.) allocate a new lchan - * 2.) copy the encryption key and other data from the - * old to the new channel. - * 3.) RSL Channel Activate this channel and wait - * - * -> Signal handler for the LCHAN - * 4.) Send GSM 04.08 assignment command to the MS - * - * -> Assignment Complete/Assignment Failure - * 5.) Release the SDCCH, continue signalling on the new link - */ -static int handle_new_assignment(struct gsm_subscriber_connection *conn, int chan_mode, int full_rate) -{ - struct gsm_lchan *new_lchan; - int chan_type; - - chan_type = full_rate ? GSM_LCHAN_TCH_F : GSM_LCHAN_TCH_H; - - new_lchan = lchan_alloc(conn->bts, chan_type, 0); - - if (!new_lchan) { - LOGP(DMSC, LOGL_NOTICE, "No free channel.\n"); - return -1; - } - - /* copy old data to the new channel */ - memcpy(&new_lchan->encr, &conn->lchan->encr, sizeof(new_lchan->encr)); - new_lchan->ms_power = conn->lchan->ms_power; - new_lchan->bs_power = conn->lchan->bs_power; - new_lchan->rqd_ta = conn->lchan->rqd_ta; - - /* copy new data to it */ - new_lchan->tch_mode = chan_mode; - new_lchan->rsl_cmode = RSL_CMOD_SPD_SPEECH; - - /* handle AMR correctly */ - if (chan_mode == GSM48_CMODE_SPEECH_AMR) - handle_mr_config(conn, new_lchan, full_rate); - - if (rsl_chan_activate_lchan(new_lchan, 0x1, 0) < 0) { - LOGP(DHO, LOGL_ERROR, "could not activate channel\n"); - lchan_free(new_lchan); - return -1; - } - - /* remember that we have the channel */ - conn->secondary_lchan = new_lchan; - new_lchan->conn = conn; - - rsl_lchan_set_state(new_lchan, LCHAN_S_ACT_REQ); - return 0; -} - -struct gsm_subscriber_connection *bsc_subscr_con_allocate(struct gsm_lchan *lchan) -{ - struct gsm_subscriber_connection *conn; - struct gsm_network *net = lchan->ts->trx->bts->network; - - conn = talloc_zero(net, struct gsm_subscriber_connection); - if (!conn) - return NULL; - - conn->network = net; - conn->lchan = lchan; - conn->bts = lchan->ts->trx->bts; - conn->via_ran = RAN_GERAN_A; - lchan->conn = conn; - llist_add_tail(&conn->entry, &net->subscr_conns); - return conn; -} - -void bsc_subscr_con_free(struct gsm_subscriber_connection *conn) -{ - if (!conn) - return; - - - if (conn->subscr) { - subscr_put(conn->subscr); - conn->subscr = NULL; - } - - - if (conn->ho_lchan) { - LOGP(DNM, LOGL_ERROR, "The ho_lchan should have been cleared.\n"); - conn->ho_lchan->conn = NULL; - } - - if (conn->lchan) { - LOGP(DNM, LOGL_ERROR, "The lchan should have been cleared.\n"); - conn->lchan->conn = NULL; - } - - if (conn->secondary_lchan) { - LOGP(DNM, LOGL_ERROR, "The secondary_lchan should have been cleared.\n"); - conn->secondary_lchan->conn = NULL; - } - - llist_del(&conn->entry); - talloc_free(conn); -} - -int bsc_api_init(struct gsm_network *network, struct bsc_api *api) -{ - network->bsc_api = api; - return 0; -} - -/*! \brief process incoming 08.08 DTAP from MSC (send via BTS to MS) */ -int gsm0808_submit_dtap(struct gsm_subscriber_connection *conn, - struct msgb *msg, int link_id, int allow_sacch) -{ - uint8_t sapi; - - - if (!conn->lchan) { - LOGP(DMSC, LOGL_ERROR, - "Called submit dtap without an lchan.\n"); - msgb_free(msg); - return -1; - } - - sapi = link_id & 0x7; - msg->lchan = conn->lchan; - msg->dst = msg->lchan->ts->trx->rsl_link; - - /* If we are on a TCH and need to submit a SMS (on SAPI=3) we need to use the SACH */ - if (allow_sacch && sapi != 0) { - if (conn->lchan->type == GSM_LCHAN_TCH_F || conn->lchan->type == GSM_LCHAN_TCH_H) - link_id |= 0x40; - } - - msg->l3h = msg->data; - /* is requested SAPI already up? */ - if (conn->lchan->sapis[sapi] == LCHAN_SAPI_UNUSED) { - /* Establish L2 for additional SAPI */ - OBSC_LINKID_CB(msg) = link_id; - if (rll_establish(msg->lchan, sapi, rll_ind_cb, msg) != 0) { - msgb_free(msg); - send_sapi_reject(conn, link_id); - return -1; - } - return 0; - } else { - /* Directly forward via RLL/RSL to BTS */ - return rsl_data_request(msg, link_id); - } -} - -/* - * \brief Check if the given channel is compatible with the mode/fullrate - */ -static int chan_compat_with_mode(struct gsm_lchan *lchan, int chan_mode, int full_rate) -{ - switch (chan_mode) { - case GSM48_CMODE_SIGN: - /* signalling is always possible */ - return 1; - case GSM48_CMODE_SPEECH_V1: - case GSM48_CMODE_SPEECH_AMR: - case GSM48_CMODE_DATA_3k6: - case GSM48_CMODE_DATA_6k0: - /* these services can all run on TCH/H, but we may have - * an explicit override by the 'full_rate' argument */ - switch (lchan->type) { - case GSM_LCHAN_TCH_F: - return 1; - case GSM_LCHAN_TCH_H: - if (full_rate) - return 0; - else - return 1; - break; - default: - return 0; - } - break; - case GSM48_CMODE_DATA_12k0: - case GSM48_CMODE_DATA_14k5: - case GSM48_CMODE_SPEECH_EFR: - /* these services all explicitly require a TCH/F */ - if (lchan->type == GSM_LCHAN_TCH_F) - return 1; - else - return 0; - break; - } - - return 0; -} - -/** - * Send a GSM08.08 Assignment Request. Right now this does not contain the - * audio codec type or the allowed rates for the config. It is assumed that - * this is for audio handling only. In case the current channel does not allow - * the selected mode a new one will be allocated. - * - * TODO: Add multirate configuration, make it work for more than audio. - */ -int gsm0808_assign_req(struct gsm_subscriber_connection *conn, int chan_mode, int full_rate) -{ - struct bsc_api *api; - api = conn->network->bsc_api; - - if (!chan_compat_with_mode(conn->lchan, chan_mode, full_rate)) { - if (handle_new_assignment(conn, chan_mode, full_rate) != 0) - goto error; - } else { - if (chan_mode == GSM48_CMODE_SPEECH_AMR) - handle_mr_config(conn, conn->lchan, full_rate); - - LOGP(DMSC, LOGL_NOTICE, - "Sending %s ChanModify for speech: %s on channel %s\n", - gsm_lchan_name(conn->lchan), - get_value_string(gsm48_chan_mode_names, chan_mode), - get_value_string(gsm_chan_t_names, conn->lchan->type)); - gsm48_lchan_modify(conn->lchan, chan_mode); - } - - /* we will now start the timer to complete the assignment */ - osmo_timer_setup(&conn->T10, assignment_t10_timeout, conn); - osmo_timer_schedule(&conn->T10, GSM0808_T10_VALUE); - return 0; - -error: - api->assign_fail(conn, 0, NULL); - return -1; -} - -int gsm0808_page(struct gsm_bts *bts, unsigned int page_group, unsigned int mi_len, - uint8_t *mi, int chan_type) -{ - return rsl_paging_cmd(bts, page_group, mi_len, mi, chan_type, false); -} - -static void handle_ass_compl(struct gsm_subscriber_connection *conn, - struct msgb *msg) -{ - struct gsm48_hdr *gh; - struct bsc_api *api = conn->network->bsc_api; - - if (conn->secondary_lchan != msg->lchan) { - LOGP(DMSC, LOGL_ERROR, "Assignment Compl should occur on second lchan.\n"); - return; - } - - gh = msgb_l3(msg); - if (msgb_l3len(msg) - sizeof(*gh) != 1) { - LOGP(DMSC, LOGL_ERROR, "Assignment Compl invalid: %zu\n", - msgb_l3len(msg) - sizeof(*gh)); - return; - } - - /* switch TRAU muxer for E1 based BTS from one channel to another */ - if (is_e1_bts(conn->bts)) - switch_trau_mux(conn->lchan, conn->secondary_lchan); - - /* swap channels */ - osmo_timer_del(&conn->T10); - - lchan_release(conn->lchan, 0, RSL_REL_LOCAL_END); - conn->lchan = conn->secondary_lchan; - conn->secondary_lchan = NULL; - - if (is_ipaccess_bts(conn->bts) && conn->lchan->tch_mode != GSM48_CMODE_SIGN) - rsl_ipacc_crcx(conn->lchan); - - api->assign_compl(conn, gh->data[0], - lchan_to_chosen_channel(conn->lchan), - conn->lchan->encr.alg_id, - chan_mode_to_speech(conn->lchan)); -} - -static void handle_ass_fail(struct gsm_subscriber_connection *conn, - struct msgb *msg) -{ - struct bsc_api *api = conn->network->bsc_api; - uint8_t *rr_failure; - struct gsm48_hdr *gh; - - - if (conn->lchan != msg->lchan) { - LOGP(DMSC, LOGL_ERROR, "Assignment failure should occur on primary lchan.\n"); - return; - } - - /* stop the timer and release it */ - osmo_timer_del(&conn->T10); - lchan_release(conn->secondary_lchan, 0, RSL_REL_LOCAL_END); - conn->secondary_lchan = NULL; - - gh = msgb_l3(msg); - if (msgb_l3len(msg) - sizeof(*gh) != 1) { - LOGP(DMSC, LOGL_ERROR, "assignment failure unhandled: %zu\n", - msgb_l3len(msg) - sizeof(*gh)); - rr_failure = NULL; - } else { - rr_failure = &gh->data[0]; - } - - api->assign_fail(conn, - GSM0808_CAUSE_RADIO_INTERFACE_MESSAGE_FAILURE, - rr_failure); -} - -static void handle_classmark_chg(struct gsm_subscriber_connection *conn, - struct msgb *msg) -{ - struct bsc_api *api = msg->lchan->ts->trx->bts->network->bsc_api; - struct gsm48_hdr *gh = msgb_l3(msg); - unsigned int payload_len = msgb_l3len(msg) - sizeof(*gh); - uint8_t cm2_len, cm3_len = 0; - uint8_t *cm2, *cm3 = NULL; - - DEBUGP(DRR, "CLASSMARK CHANGE "); - - /* classmark 2 */ - cm2_len = gh->data[0]; - cm2 = &gh->data[1]; - DEBUGPC(DRR, "CM2(len=%u) ", cm2_len); - - if (payload_len > cm2_len + 1) { - /* we must have a classmark3 */ - if (gh->data[cm2_len+1] != 0x20) { - DEBUGPC(DRR, "ERR CM3 TAG\n"); - return; - } - if (cm2_len > 3) { - DEBUGPC(DRR, "CM2 too long!\n"); - return; - } - - cm3_len = gh->data[cm2_len+2]; - cm3 = &gh->data[cm2_len+3]; - if (cm3_len > 14) { - DEBUGPC(DRR, "CM3 len %u too long!\n", cm3_len); - return; - } - DEBUGPC(DRR, "CM3(len=%u)\n", cm3_len); - } - api->classmark_chg(conn, cm2, cm2_len, cm3, cm3_len); -} - -/* Chapter 9.1.16 Handover complete */ -static void handle_rr_ho_compl(struct msgb *msg) -{ - struct lchan_signal_data sig; - struct gsm48_hdr *gh = msgb_l3(msg); - - DEBUGP(DRR, "HANDOVER COMPLETE cause = %s\n", - rr_cause_name(gh->data[0])); - - sig.lchan = msg->lchan; - sig.mr = NULL; - osmo_signal_dispatch(SS_LCHAN, S_LCHAN_HANDOVER_COMPL, &sig); - /* FIXME: release old channel */ -} - -/* Chapter 9.1.17 Handover Failure */ -static void handle_rr_ho_fail(struct msgb *msg) -{ - struct lchan_signal_data sig; - struct gsm48_hdr *gh = msgb_l3(msg); - - DEBUGP(DRR, "HANDOVER FAILED cause = %s\n", - rr_cause_name(gh->data[0])); - - sig.lchan = msg->lchan; - sig.mr = NULL; - osmo_signal_dispatch(SS_LCHAN, S_LCHAN_HANDOVER_FAIL, &sig); - /* FIXME: release allocated new channel */ -} - - -static void dispatch_dtap(struct gsm_subscriber_connection *conn, - uint8_t link_id, struct msgb *msg) -{ - struct bsc_api *api = msg->lchan->ts->trx->bts->network->bsc_api; - struct gsm48_hdr *gh; - uint8_t pdisc; - uint8_t msg_type; - int rc; - - if (msgb_l3len(msg) < sizeof(*gh)) { - LOGP(DMSC, LOGL_ERROR, "Message too short for a GSM48 header.\n"); - return; - } - - gh = msgb_l3(msg); - pdisc = gsm48_hdr_pdisc(gh); - msg_type = gsm48_hdr_msg_type(gh); - - /* the idea is to handle all RR messages here, and only hand - * MM/CC/SMS-CP/LCS up to the MSC. Some messages like PAGING - * RESPONSE or CM SERVICE REQUEST will not be covered here, as - * they are only possible in the first L3 message of each L2 - * channel, i.e. 'conn' will not exist and gsm0408_rcvmsg() - * will call api->compl_l3() for it */ - switch (pdisc) { - case GSM48_PDISC_RR: - switch (msg_type) { - case GSM48_MT_RR_GPRS_SUSP_REQ: - DEBUGP(DRR, "%s\n", - gsm48_rr_msg_name(GSM48_MT_RR_GPRS_SUSP_REQ)); - break; - case GSM48_MT_RR_STATUS: - LOGP(DRR, LOGL_NOTICE, "%s (cause: %s)\n", - gsm48_rr_msg_name(GSM48_MT_RR_GPRS_SUSP_REQ), - rr_cause_name(gh->data[0])); - break; - case GSM48_MT_RR_MEAS_REP: - /* This shouldn't actually end up here, as RSL treats - * L3 Info of 08.58 MEASUREMENT REPORT different by calling - * directly into gsm48_parse_meas_rep */ - LOGP(DMEAS, LOGL_ERROR, "DIRECT GSM48 MEASUREMENT REPORT ?!? "); - break; - case GSM48_MT_RR_HANDO_COMPL: - handle_rr_ho_compl(msg); - break; - case GSM48_MT_RR_HANDO_FAIL: - handle_rr_ho_fail(msg); - break; - case GSM48_MT_RR_CIPH_M_COMPL: - if (api->cipher_mode_compl) - api->cipher_mode_compl(conn, msg, - conn->lchan->encr.alg_id); - break; - case GSM48_MT_RR_ASS_COMPL: - handle_ass_compl(conn, msg); - break; - case GSM48_MT_RR_ASS_FAIL: - handle_ass_fail(conn, msg); - break; - case GSM48_MT_RR_CHAN_MODE_MODIF_ACK: - osmo_timer_del(&conn->T10); - rc = gsm48_rx_rr_modif_ack(msg); - if (rc < 0) { - api->assign_fail(conn, - GSM0808_CAUSE_NO_RADIO_RESOURCE_AVAILABLE, - NULL); - } else if (rc >= 0) { - api->assign_compl(conn, 0, - lchan_to_chosen_channel(conn->lchan), - conn->lchan->encr.alg_id, - chan_mode_to_speech(conn->lchan)); - } - break; - case GSM48_MT_RR_CLSM_CHG: - handle_classmark_chg(conn, msg); - break; - case GSM48_MT_RR_APP_INFO: - /* Passing RR APP INFO to MSC, not quite - * according to spec */ - if (api->dtap) - api->dtap(conn, link_id, msg); - break; - default: - /* Normally, a MSC should never receive RR - * messages, but we'd rather forward what we - * don't know than drop it... */ - LOGP(DRR, LOGL_NOTICE, - "BSC: Passing %s 04.08 RR message to MSC\n", - gsm48_rr_msg_name(msg_type)); - if (api->dtap) - api->dtap(conn, link_id, msg); - } - break; - default: - if (api->dtap) - api->dtap(conn, link_id, msg); - break; - } -} - -/*! \brief RSL has received a DATA INDICATION with L3 from MS */ -int gsm0408_rcvmsg(struct msgb *msg, uint8_t link_id) -{ - int rc; - struct bsc_api *api = msg->lchan->ts->trx->bts->network->bsc_api; - struct gsm_lchan *lchan; - - lchan = msg->lchan; - if (lchan->state != LCHAN_S_ACTIVE) { - LOGP(DRSL, LOGL_INFO, "Got data in non active state(%s), " - "discarding.\n", gsm_lchans_name(lchan->state)); - return -1; - } - - - if (lchan->conn) { - /* if we already have a connection, forward via DTAP to - * MSC */ - dispatch_dtap(lchan->conn, link_id, msg); - } else { - /* allocate a new connection */ - rc = BSC_API_CONN_POL_REJECT; - lchan->conn = bsc_subscr_con_allocate(msg->lchan); - if (!lchan->conn) { - lchan_release(lchan, 1, RSL_REL_NORMAL); - return -1; - } - - /* fwd via bsc_api to send COMPLETE L3 INFO to MSC */ - rc = api->compl_l3(lchan->conn, msg, 0); - - if (rc != BSC_API_CONN_POL_ACCEPT) { - lchan->conn->lchan = NULL; - bsc_subscr_con_free(lchan->conn); - lchan_release(lchan, 1, RSL_REL_NORMAL); - } - } - - return 0; -} - -/*! \brief We received a GSM 08.08 CIPHER MODE from the MSC */ -int gsm0808_cipher_mode(struct gsm_subscriber_connection *conn, int cipher, - const uint8_t *key, int len, int include_imeisv) -{ - if (cipher > 0 && key == NULL) { - LOGP(DRSL, LOGL_ERROR, "Need to have an encrytpion key.\n"); - return -1; - } - - if (len > MAX_A5_KEY_LEN) { - LOGP(DRSL, LOGL_ERROR, "The key is too long: %d\n", len); - return -1; - } - - conn->lchan->encr.alg_id = RSL_ENC_ALG_A5(cipher); - if (key) { - conn->lchan->encr.key_len = len; - memcpy(conn->lchan->encr.key, key, len); - } - - return gsm48_send_rr_ciph_mode(conn->lchan, include_imeisv); -} - -/* - * Release all occupied RF Channels but stay around for more. - */ -int gsm0808_clear(struct gsm_subscriber_connection *conn) -{ - if (conn->ho_lchan) - bsc_clear_handover(conn, 1); - - if (conn->secondary_lchan) - lchan_release(conn->secondary_lchan, 0, RSL_REL_LOCAL_END); - - if (conn->lchan) - lchan_release(conn->lchan, 1, RSL_REL_NORMAL); - - conn->lchan = NULL; - conn->secondary_lchan = NULL; - conn->ho_lchan = NULL; - conn->bts = NULL; - - osmo_timer_del(&conn->T10); - - return 0; -} - -static void send_sapi_reject(struct gsm_subscriber_connection *conn, int link_id) -{ - struct bsc_api *api; - - if (!conn) - return; - - api = conn->network->bsc_api; - if (!api || !api->sapi_n_reject) - return; - - api->sapi_n_reject(conn, link_id); -} - -static void rll_ind_cb(struct gsm_lchan *lchan, uint8_t link_id, void *_data, enum bsc_rllr_ind rllr_ind) -{ - struct msgb *msg = _data; - - /* - * There seems to be a small window that the RLL timer can - * fire after a lchan_release call and before the S_CHALLOC_FREED - * is called. Check if a conn is set before proceeding. - */ - if (!lchan->conn) - return; - - switch (rllr_ind) { - case BSC_RLLR_IND_EST_CONF: - rsl_data_request(msg, OBSC_LINKID_CB(msg)); - break; - case BSC_RLLR_IND_REL_IND: - case BSC_RLLR_IND_ERR_IND: - case BSC_RLLR_IND_TIMEOUT: - send_sapi_reject(lchan->conn, OBSC_LINKID_CB(msg)); - msgb_free(msg); - break; - } -} - -static int bsc_handle_lchan_signal(unsigned int subsys, unsigned int signal, - void *handler_data, void *signal_data) -{ - struct bsc_api *bsc; - struct gsm_lchan *lchan; - struct lchan_signal_data *lchan_data; - - if (subsys != SS_LCHAN) - return 0; - - - lchan_data = signal_data; - if (!lchan_data->lchan || !lchan_data->lchan->conn) - return 0; - - lchan = lchan_data->lchan; - bsc = lchan->ts->trx->bts->network->bsc_api; - if (!bsc) - return 0; - - switch (signal) { - case S_LCHAN_UNEXPECTED_RELEASE: - handle_release(lchan->conn, bsc, lchan); - break; - case S_LCHAN_ACTIVATE_ACK: - handle_chan_ack(lchan->conn, bsc, lchan); - break; - case S_LCHAN_ACTIVATE_NACK: - handle_chan_nack(lchan->conn, bsc, lchan); - break; - } - - return 0; -} - -static void handle_release(struct gsm_subscriber_connection *conn, - struct bsc_api *bsc, struct gsm_lchan *lchan) -{ - int destruct = 1; - - if (conn->secondary_lchan == lchan) { - osmo_timer_del(&conn->T10); - conn->secondary_lchan = NULL; - - bsc->assign_fail(conn, - GSM0808_CAUSE_RADIO_INTERFACE_FAILURE, - NULL); - } - - /* clear the connection now */ - if (bsc->clear_request) - destruct = bsc->clear_request(conn, 0); - - /* now give up all channels */ - if (conn->lchan == lchan) - conn->lchan = NULL; - if (conn->ho_lchan == lchan) { - bsc_clear_handover(conn, 0); - conn->ho_lchan = NULL; - } - lchan->conn = NULL; - - gsm0808_clear(conn); - - if (destruct) - bsc_subscr_con_free(conn); -} - -static void handle_chan_ack(struct gsm_subscriber_connection *conn, - struct bsc_api *api, struct gsm_lchan *lchan) -{ - if (conn->secondary_lchan != lchan) - return; - - LOGP(DMSC, LOGL_NOTICE, "Sending assignment on chan: %p\n", lchan); - gsm48_send_rr_ass_cmd(conn->lchan, lchan, lchan->ms_power); -} - -static void handle_chan_nack(struct gsm_subscriber_connection *conn, - struct bsc_api *api, struct gsm_lchan *lchan) -{ - if (conn->secondary_lchan != lchan) - return; - - LOGP(DMSC, LOGL_ERROR, "Channel activation failed. Waiting for timeout now\n"); - conn->secondary_lchan->conn = NULL; - conn->secondary_lchan = NULL; -} - -static __attribute__((constructor)) void on_dso_load_bsc(void) -{ - osmo_signal_register_handler(SS_LCHAN, bsc_handle_lchan_signal, NULL); -} - diff --git a/openbsc/src/libbsc/bsc_ctrl_commands.c b/openbsc/src/libbsc/bsc_ctrl_commands.c deleted file mode 100644 index 641fe2bf..00000000 --- a/openbsc/src/libbsc/bsc_ctrl_commands.c +++ /dev/null @@ -1,459 +0,0 @@ -/* - * (C) 2013-2015 by Holger Hans Peter Freyther - * (C) 2013-2015 by sysmocom s.f.m.c. GmbH - * - * All Rights Reserved - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation; either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - * - */ -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include - -#define CTRL_CMD_VTY_STRING(cmdname, cmdstr, dtype, element) \ - CTRL_HELPER_GET_STRING(cmdname, dtype, element) \ - CTRL_HELPER_SET_STRING(cmdname, dtype, element) \ -static struct ctrl_cmd_element cmd_##cmdname = { \ - .name = cmdstr, \ - .get = get_##cmdname, \ - .set = set_##cmdname, \ - .verify = verify_vty_description_string, \ -} - -/** - * Check that there are no newlines or comments or other things - * that could make the VTY configuration unparsable. - */ -static int verify_vty_description_string(struct ctrl_cmd *cmd, - const char *value, void *data) -{ - int i; - const size_t len = strlen(value); - - for (i = 0; i < len; ++i) { - switch(value[i]) { - case '#': - case '\n': - case '\r': - cmd->reply = "String includes illegal character"; - return -1; - default: - break; - } - } - - return 0; -} - -CTRL_CMD_DEFINE_RANGE(net_mnc, "mnc", struct gsm_network, network_code, 0, 999); -CTRL_CMD_DEFINE_RANGE(net_mcc, "mcc", struct gsm_network, country_code, 1, 999); -CTRL_CMD_VTY_STRING(net_short_name, "short-name", struct gsm_network, name_short); -CTRL_CMD_VTY_STRING(net_long_name, "long-name", struct gsm_network, name_long); - -static int set_net_apply_config(struct ctrl_cmd *cmd, void *data) -{ - struct gsm_network *net = cmd->node; - struct gsm_bts *bts; - - llist_for_each_entry(bts, &net->bts_list, list) { - if (!is_ipaccess_bts(bts)) - continue; - - /* - * The ip.access nanoBTS seems to be unrelaible on BSSGP - * so let's us just reboot it. For the sysmoBTS we can just - * restart the process as all state is gone. - */ - if (!is_sysmobts_v2(bts) && strcmp(cmd->value, "restart") == 0) { - struct gsm_bts_trx *trx; - llist_for_each_entry_reverse(trx, &bts->trx_list, list) - abis_nm_ipaccess_restart(trx); - } else - ipaccess_drop_oml(bts); - } - - cmd->reply = "Tried to drop the BTS"; - return CTRL_CMD_REPLY; -} - -CTRL_CMD_DEFINE_WO_NOVRF(net_apply_config, "apply-configuration"); - -static int verify_net_mcc_mnc_apply(struct ctrl_cmd *cmd, const char *value, void *d) -{ - char *tmp, *saveptr, *mcc, *mnc; - - tmp = talloc_strdup(cmd, value); - if (!tmp) - return 1; - - mcc = strtok_r(tmp, ",", &saveptr); - mnc = strtok_r(NULL, ",", &saveptr); - talloc_free(tmp); - - if (!mcc || !mnc) - return 1; - return 0; -} - -static int set_net_mcc_mnc_apply(struct ctrl_cmd *cmd, void *data) -{ - struct gsm_network *net = cmd->node; - char *tmp, *saveptr, *mcc_str, *mnc_str; - int mcc, mnc; - - tmp = talloc_strdup(cmd, cmd->value); - if (!tmp) - goto oom; - - - mcc_str = strtok_r(tmp, ",", &saveptr); - mnc_str = strtok_r(NULL, ",", &saveptr); - - mcc = atoi(mcc_str); - mnc = atoi(mnc_str); - - talloc_free(tmp); - - if (net->network_code == mnc && net->country_code == mcc) { - cmd->reply = "Nothing changed"; - return CTRL_CMD_REPLY; - } - - net->network_code = mnc; - net->country_code = mcc; - - return set_net_apply_config(cmd, data); - -oom: - cmd->reply = "OOM"; - return CTRL_CMD_ERROR; -} -CTRL_CMD_DEFINE_WO(net_mcc_mnc_apply, "mcc-mnc-apply"); - -/* BTS related commands below */ -CTRL_CMD_DEFINE_RANGE(bts_lac, "location-area-code", struct gsm_bts, location_area_code, 0, 65535); -CTRL_CMD_DEFINE_RANGE(bts_ci, "cell-identity", struct gsm_bts, cell_identity, 0, 65535); - -static int set_bts_apply_config(struct ctrl_cmd *cmd, void *data) -{ - struct gsm_bts *bts = cmd->node; - - if (!is_ipaccess_bts(bts)) { - cmd->reply = "BTS is not IP based"; - return CTRL_CMD_ERROR; - } - - ipaccess_drop_oml(bts); - cmd->reply = "Tried to drop the BTS"; - return CTRL_CMD_REPLY; -} - -CTRL_CMD_DEFINE_WO_NOVRF(bts_apply_config, "apply-configuration"); - -static int set_bts_si(struct ctrl_cmd *cmd, void *data) -{ - struct gsm_bts *bts = cmd->node; - int rc; - - rc = gsm_bts_set_system_infos(bts); - if (rc != 0) { - cmd->reply = "Failed to generate SI"; - return CTRL_CMD_ERROR; - } - - cmd->reply = "Generated new System Information"; - return CTRL_CMD_REPLY; -} -CTRL_CMD_DEFINE_WO_NOVRF(bts_si, "send-new-system-informations"); - -static int get_bts_chan_load(struct ctrl_cmd *cmd, void *data) -{ - int i; - struct pchan_load pl; - struct gsm_bts *bts; - const char *space = ""; - - bts = cmd->node; - memset(&pl, 0, sizeof(pl)); - bts_chan_load(&pl, bts); - - cmd->reply = talloc_strdup(cmd, ""); - - for (i = 0; i < ARRAY_SIZE(pl.pchan); ++i) { - const struct load_counter *lc = &pl.pchan[i]; - - /* These can never have user load */ - if (i == GSM_PCHAN_NONE) - continue; - if (i == GSM_PCHAN_CCCH) - continue; - if (i == GSM_PCHAN_PDCH) - continue; - if (i == GSM_PCHAN_UNKNOWN) - continue; - - cmd->reply = talloc_asprintf_append(cmd->reply, - "%s%s,%u,%u", - space, gsm_pchan_name(i), lc->used, lc->total); - if (!cmd->reply) - goto error; - space = " "; - } - - return CTRL_CMD_REPLY; - -error: - cmd->reply = "Memory allocation failure"; - return CTRL_CMD_ERROR; -} - -CTRL_CMD_DEFINE_RO(bts_chan_load, "channel-load"); - -static int get_bts_oml_conn(struct ctrl_cmd *cmd, void *data) -{ - struct gsm_bts *bts = cmd->node; - - cmd->reply = bts->oml_link ? "connected" : "disconnected"; - return CTRL_CMD_REPLY; -} - -CTRL_CMD_DEFINE_RO(bts_oml_conn, "oml-connection-state"); - -static int verify_bts_gprs_mode(struct ctrl_cmd *cmd, const char *value, void *_data) -{ - int valid; - enum bts_gprs_mode mode; - struct gsm_bts *bts = cmd->node; - - mode = bts_gprs_mode_parse(value, &valid); - if (!valid) { - cmd->reply = "Mode is not known"; - return 1; - } - - if (!bts_gprs_mode_is_compat(bts, mode)) { - cmd->reply = "bts does not support this mode"; - return 1; - } - - return 0; -} - -static int get_bts_gprs_mode(struct ctrl_cmd *cmd, void *data) -{ - struct gsm_bts *bts = cmd->node; - - cmd->reply = talloc_strdup(cmd, bts_gprs_mode_name(bts->gprs.mode)); - return CTRL_CMD_REPLY; -} - -static int set_bts_gprs_mode(struct ctrl_cmd *cmd, void *data) -{ - struct gsm_bts *bts = cmd->node; - - bts->gprs.mode = bts_gprs_mode_parse(cmd->value, NULL); - return get_bts_gprs_mode(cmd, data); -} - -CTRL_CMD_DEFINE(bts_gprs_mode, "gprs-mode"); - -static int get_bts_rf_state(struct ctrl_cmd *cmd, void *data) -{ - const char *oper, *admin, *policy; - struct gsm_bts *bts = cmd->node; - - if (!bts) { - cmd->reply = "bts not found."; - return CTRL_CMD_ERROR; - } - - oper = osmo_bsc_rf_get_opstate_name(osmo_bsc_rf_get_opstate_by_bts(bts)); - admin = osmo_bsc_rf_get_adminstate_name(osmo_bsc_rf_get_adminstate_by_bts(bts)); - policy = osmo_bsc_rf_get_policy_name(osmo_bsc_rf_get_policy_by_bts(bts)); - - cmd->reply = talloc_asprintf(cmd, "%s,%s,%s", oper, admin, policy); - if (!cmd->reply) { - cmd->reply = "OOM."; - return CTRL_CMD_ERROR; - } - - return CTRL_CMD_REPLY; -} -CTRL_CMD_DEFINE_RO(bts_rf_state, "rf_state"); - -static int get_net_rf_lock(struct ctrl_cmd *cmd, void *data) -{ - struct gsm_network *net = cmd->node; - struct gsm_bts *bts; - const char *policy_name; - - policy_name = osmo_bsc_rf_get_policy_name(net->bsc_data->rf_ctrl->policy); - - llist_for_each_entry(bts, &net->bts_list, list) { - struct gsm_bts_trx *trx; - - /* Exclude the BTS from the global lock */ - if (bts->excl_from_rf_lock) - continue; - - llist_for_each_entry(trx, &bts->trx_list, list) { - if (trx->mo.nm_state.availability == NM_AVSTATE_OK && - trx->mo.nm_state.operational != NM_OPSTATE_DISABLED) { - cmd->reply = talloc_asprintf(cmd, - "state=on,policy=%s,bts=%u,trx=%u", - policy_name, bts->nr, trx->nr); - return CTRL_CMD_REPLY; - } - } - } - - cmd->reply = talloc_asprintf(cmd, "state=off,policy=%s", - policy_name); - return CTRL_CMD_REPLY; -} - -#define TIME_FORMAT_RFC2822 "%a, %d %b %Y %T %z" - -static int set_net_rf_lock(struct ctrl_cmd *cmd, void *data) -{ - int locked = atoi(cmd->value); - struct gsm_network *net = cmd->node; - time_t now = time(NULL); - char now_buf[64]; - struct osmo_bsc_rf *rf; - - if (!net) { - cmd->reply = "net not found."; - return CTRL_CMD_ERROR; - } - - rf = net->bsc_data->rf_ctrl; - - if (!rf) { - cmd->reply = "RF Ctrl is not enabled in the BSC Configuration"; - return CTRL_CMD_ERROR; - } - - talloc_free(rf->last_rf_lock_ctrl_command); - strftime(now_buf, sizeof(now_buf), TIME_FORMAT_RFC2822, gmtime(&now)); - rf->last_rf_lock_ctrl_command = - talloc_asprintf(rf, "rf_locked %u (%s)", locked, now_buf); - - osmo_bsc_rf_schedule_lock(rf, locked == 1 ? '0' : '1'); - - cmd->reply = talloc_asprintf(cmd, "%u", locked); - if (!cmd->reply) { - cmd->reply = "OOM."; - return CTRL_CMD_ERROR; - } - - return CTRL_CMD_REPLY; -} - -static int verify_net_rf_lock(struct ctrl_cmd *cmd, const char *value, void *data) -{ - int locked = atoi(cmd->value); - - if ((locked != 0) && (locked != 1)) - return 1; - - return 0; -} -CTRL_CMD_DEFINE(net_rf_lock, "rf_locked"); - -static int get_net_bts_num(struct ctrl_cmd *cmd, void *data) -{ - struct gsm_network *net = cmd->node; - - cmd->reply = talloc_asprintf(cmd, "%u", net->num_bts); - return CTRL_CMD_REPLY; -} -CTRL_CMD_DEFINE_RO(net_bts_num, "number-of-bts"); - -/* TRX related commands below here */ -CTRL_HELPER_GET_INT(trx_max_power, struct gsm_bts_trx, max_power_red); -static int verify_trx_max_power(struct ctrl_cmd *cmd, const char *value, void *_data) -{ - int tmp = atoi(value); - - if (tmp < 0 || tmp > 22) { - cmd->reply = "Value must be between 0 and 22"; - return -1; - } - - if (tmp & 1) { - cmd->reply = "Value must be even"; - return -1; - } - - return 0; -} -CTRL_CMD_DEFINE_RANGE(trx_arfcn, "arfcn", struct gsm_bts_trx, arfcn, 0, 1023); - -static int set_trx_max_power(struct ctrl_cmd *cmd, void *_data) -{ - struct gsm_bts_trx *trx = cmd->node; - int old_power; - - /* remember the old value, set the new one */ - old_power = trx->max_power_red; - trx->max_power_red = atoi(cmd->value); - - /* Maybe update the value */ - if (old_power != trx->max_power_red) { - LOGP(DCTRL, LOGL_NOTICE, - "%s updating max_pwr_red(%d)\n", - gsm_trx_name(trx), trx->max_power_red); - abis_nm_update_max_power_red(trx); - } - - return get_trx_max_power(cmd, _data); -} -CTRL_CMD_DEFINE(trx_max_power, "max-power-reduction"); - -int bsc_base_ctrl_cmds_install(void) -{ - int rc = 0; - rc |= ctrl_cmd_install(CTRL_NODE_ROOT, &cmd_net_mnc); - rc |= ctrl_cmd_install(CTRL_NODE_ROOT, &cmd_net_mcc); - rc |= ctrl_cmd_install(CTRL_NODE_ROOT, &cmd_net_short_name); - rc |= ctrl_cmd_install(CTRL_NODE_ROOT, &cmd_net_long_name); - rc |= ctrl_cmd_install(CTRL_NODE_ROOT, &cmd_net_apply_config); - rc |= ctrl_cmd_install(CTRL_NODE_ROOT, &cmd_net_mcc_mnc_apply); - rc |= ctrl_cmd_install(CTRL_NODE_ROOT, &cmd_net_rf_lock); - rc |= ctrl_cmd_install(CTRL_NODE_ROOT, &cmd_net_bts_num); - - rc |= ctrl_cmd_install(CTRL_NODE_BTS, &cmd_bts_lac); - rc |= ctrl_cmd_install(CTRL_NODE_BTS, &cmd_bts_ci); - rc |= ctrl_cmd_install(CTRL_NODE_BTS, &cmd_bts_apply_config); - rc |= ctrl_cmd_install(CTRL_NODE_BTS, &cmd_bts_si); - rc |= ctrl_cmd_install(CTRL_NODE_BTS, &cmd_bts_chan_load); - rc |= ctrl_cmd_install(CTRL_NODE_BTS, &cmd_bts_oml_conn); - rc |= ctrl_cmd_install(CTRL_NODE_BTS, &cmd_bts_gprs_mode); - rc |= ctrl_cmd_install(CTRL_NODE_BTS, &cmd_bts_rf_state); - - rc |= ctrl_cmd_install(CTRL_NODE_TRX, &cmd_trx_max_power); - rc |= ctrl_cmd_install(CTRL_NODE_TRX, &cmd_trx_arfcn); - - return rc; -} diff --git a/openbsc/src/libbsc/bsc_ctrl_lookup.c b/openbsc/src/libbsc/bsc_ctrl_lookup.c deleted file mode 100644 index a8a8cf52..00000000 --- a/openbsc/src/libbsc/bsc_ctrl_lookup.c +++ /dev/null @@ -1,107 +0,0 @@ -/* SNMP-like status interface. Look-up of BTS/TRX - * - * (C) 2010-2011 by Daniel Willmann - * (C) 2010-2011 by On-Waves - * - * All Rights Reserved - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - * - */ - -#include - -#include -#include -#include -#include - -extern vector ctrl_node_vec; - -/*! \brief control interface lookup function for bsc/bts gsm_data - * \param[in] data Private data passed to controlif_setup() - * \param[in] vline Vector of the line holding the command string - * \param[out] node_type type (CTRL_NODE_) that was determined - * \param[out] node_data private dta of node that was determined - * \param i Current index into vline, up to which it is parsed - */ -static int bsc_ctrl_node_lookup(void *data, vector vline, int *node_type, - void **node_data, int *i) -{ - struct gsm_network *net = data; - struct gsm_bts *bts = NULL; - struct gsm_bts_trx *trx = NULL; - struct gsm_bts_trx_ts *ts = NULL; - char *token = vector_slot(vline, *i); - long num; - - /* TODO: We need to make sure that the following chars are digits - * and/or use strtol to check if number conversion was successful - * Right now something like net.bts_stats will not work */ - if (!strcmp(token, "bts")) { - if (*node_type != CTRL_NODE_ROOT || !net) - goto err_missing; - (*i)++; - if (!ctrl_parse_get_num(vline, *i, &num)) - goto err_index; - - bts = gsm_bts_num(net, num); - if (!bts) - goto err_missing; - *node_data = bts; - *node_type = CTRL_NODE_BTS; - } else if (!strcmp(token, "trx")) { - if (*node_type != CTRL_NODE_BTS || !*node_data) - goto err_missing; - bts = *node_data; - (*i)++; - if (!ctrl_parse_get_num(vline, *i, &num)) - goto err_index; - - trx = gsm_bts_trx_num(bts, num); - if (!trx) - goto err_missing; - *node_data = trx; - *node_type = CTRL_NODE_TRX; - } else if (!strcmp(token, "ts")) { - if (*node_type != CTRL_NODE_TRX || !*node_data) - goto err_missing; - trx = *node_data; - (*i)++; - if (!ctrl_parse_get_num(vline, *i, &num)) - goto err_index; - - if ((num >= 0) && (num < TRX_NR_TS)) - ts = &trx->ts[num]; - if (!ts) - goto err_missing; - *node_data = ts; - *node_type = CTRL_NODE_TS; - } else - return 0; - - return 1; -err_missing: - return -ENODEV; -err_index: - return -ERANGE; -} - -struct ctrl_handle *bsc_controlif_setup(struct gsm_network *net, - const char *bind_addr, uint16_t port) -{ - return ctrl_interface_setup_dynip(net, bind_addr, port, - bsc_ctrl_node_lookup); -} diff --git a/openbsc/src/libbsc/bsc_dyn_ts.c b/openbsc/src/libbsc/bsc_dyn_ts.c deleted file mode 100644 index e5422fc5..00000000 --- a/openbsc/src/libbsc/bsc_dyn_ts.c +++ /dev/null @@ -1,77 +0,0 @@ -/* Dynamic PDCH initialisation implementation shared across NM and RSL */ - -/* (C) 2016 by sysmocom s.f.m.c. GmbH - * All Rights Reserved - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation; either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public 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 - -void tchf_pdch_ts_init(struct gsm_bts_trx_ts *ts) -{ - int rc; - - if (ts->trx->bts->gprs.mode == BTS_GPRS_NONE) { - LOGP(DRSL, LOGL_NOTICE, "%s: GPRS mode is 'none':" - " not activating PDCH.\n", - gsm_ts_and_pchan_name(ts)); - return; - } - - LOGP(DRSL, LOGL_DEBUG, "%s: trying to PDCH ACT\n", - gsm_ts_and_pchan_name(ts)); - - rc = rsl_ipacc_pdch_activate(ts, 1); - if (rc != 0) - LOGP(DRSL, LOGL_ERROR, "%s %s: PDCH ACT failed\n", - gsm_ts_name(ts), gsm_pchan_name(ts->pchan)); -} - -void tchf_tchh_pdch_ts_init(struct gsm_bts_trx_ts *ts) -{ - if (ts->trx->bts->gprs.mode == BTS_GPRS_NONE) { - LOGP(DRSL, LOGL_NOTICE, "%s: GPRS mode is 'none':" - " not activating PDCH.\n", - gsm_ts_and_pchan_name(ts)); - return; - } - - dyn_ts_switchover_start(ts, GSM_PCHAN_PDCH); -} - -void dyn_ts_init(struct gsm_bts_trx_ts *ts) -{ - /* Clear all TCH/F_PDCH flags */ - ts->flags &= ~(TS_F_PDCH_PENDING_MASK | TS_F_PDCH_ACTIVE); - - /* Clear TCH/F_TCH/H_PDCH state */ - ts->dyn.pchan_is = ts->dyn.pchan_want = GSM_PCHAN_NONE; - ts->dyn.pending_chan_activ = NULL; - - switch (ts->pchan) { - case GSM_PCHAN_TCH_F_PDCH: - tchf_pdch_ts_init(ts); - break; - case GSM_PCHAN_TCH_F_TCH_H_PDCH: - tchf_tchh_pdch_ts_init(ts); - break; - default: - break; - } -} diff --git a/openbsc/src/libbsc/bsc_init.c b/openbsc/src/libbsc/bsc_init.c deleted file mode 100644 index ec87a7bc..00000000 --- a/openbsc/src/libbsc/bsc_init.c +++ /dev/null @@ -1,552 +0,0 @@ -/* A hackish minimal BSC (+MSC +HLR) implementation */ - -/* (C) 2008-2010 by Harald Welte - * (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 - -/* global pointer to the gsm network data structure */ -extern struct gsm_network *bsc_gsmnet; - -/* Callback function for NACK on the OML NM */ -static int oml_msg_nack(struct nm_nack_signal_data *nack) -{ - if (nack->mt == NM_MT_GET_ATTR_NACK) { - LOGP(DNM, LOGL_ERROR, "BTS%u does not support Get Attributes " - "OML message.\n", nack->bts->nr); - return 0; - } - - if (nack->mt == NM_MT_SET_BTS_ATTR_NACK) - LOGP(DNM, LOGL_ERROR, "Failed to set BTS attributes. That is fatal. " - "Was the bts type and frequency properly specified?\n"); - else - LOGP(DNM, LOGL_ERROR, "Got %s NACK going to drop the OML links.\n", - abis_nm_nack_name(nack->mt)); - - if (!nack->bts) { - LOGP(DNM, LOGL_ERROR, "Unknown bts. Can not drop it.\n"); - return 0; - } - - if (is_ipaccess_bts(nack->bts)) - ipaccess_drop_oml(nack->bts); - - return 0; -} - -/* Callback function to be called every time we receive a signal from NM */ -static int nm_sig_cb(unsigned int subsys, unsigned int signal, - void *handler_data, void *signal_data) -{ - struct nm_nack_signal_data *nack; - - switch (signal) { - case S_NM_NACK: - nack = signal_data; - return oml_msg_nack(nack); - default: - break; - } - return 0; -} - -int bsc_shutdown_net(struct gsm_network *net) -{ - struct gsm_bts *bts; - - llist_for_each_entry(bts, &net->bts_list, list) { - LOGP(DNM, LOGL_NOTICE, "shutting down OML for BTS %u\n", bts->nr); - osmo_signal_dispatch(SS_L_GLOBAL, S_GLOBAL_BTS_CLOSE_OM, bts); - } - - return 0; -} - -static int rsl_si(struct gsm_bts_trx *trx, enum osmo_sysinfo_type i, int si_len) -{ - struct gsm_bts *bts = trx->bts; - int rc, j; - - DEBUGP(DRR, "SI%s: %s\n", get_value_string(osmo_sitype_strs, i), - osmo_hexdump(GSM_BTS_SI(bts, i), GSM_MACBLOCK_LEN)); - - switch (i) { - case SYSINFO_TYPE_5: - case SYSINFO_TYPE_5bis: - case SYSINFO_TYPE_5ter: - case SYSINFO_TYPE_6: - rc = rsl_sacch_filling(trx, osmo_sitype2rsl(i), - GSM_BTS_SI(bts, i), si_len); - break; - case SYSINFO_TYPE_2quater: - for (j = 0; j <= bts->si2q_count; j++) - rc = rsl_bcch_info(trx, i, (const uint8_t *)GSM_BTS_SI2Q(bts, j), GSM_MACBLOCK_LEN); - break; - default: - rc = rsl_bcch_info(trx, i, GSM_BTS_SI(bts, i), si_len); - break; - } - - return rc; -} - -/* set all system information types for a TRX */ -int gsm_bts_trx_set_system_infos(struct gsm_bts_trx *trx) -{ - int i, rc; - struct gsm_bts *bts = trx->bts; - uint8_t gen_si[_MAX_SYSINFO_TYPE], n_si = 0, n; - int si_len[_MAX_SYSINFO_TYPE]; - - bts->si_common.cell_sel_par.ms_txpwr_max_ccch = - ms_pwr_ctl_lvl(bts->band, bts->ms_max_power); - bts->si_common.cell_sel_par.neci = bts->network->neci; - - /* Zero, forget the state of the SIs */ - bts->si_valid = 0; - - /* First, we determine which of the SI messages we actually need */ - - if (trx == bts->c0) { - /* 1...4 are always present on a C0 TRX */ - gen_si[n_si++] = SYSINFO_TYPE_1; - gen_si[n_si++] = SYSINFO_TYPE_2; - gen_si[n_si++] = SYSINFO_TYPE_2bis; - gen_si[n_si++] = SYSINFO_TYPE_2ter; - gen_si[n_si++] = SYSINFO_TYPE_2quater; - gen_si[n_si++] = SYSINFO_TYPE_3; - gen_si[n_si++] = SYSINFO_TYPE_4; - - /* 13 is always present on a C0 TRX of a GPRS BTS */ - if (bts->gprs.mode != BTS_GPRS_NONE) - gen_si[n_si++] = SYSINFO_TYPE_13; - } - - /* 5 and 6 are always present on every TRX */ - gen_si[n_si++] = SYSINFO_TYPE_5; - gen_si[n_si++] = SYSINFO_TYPE_5bis; - gen_si[n_si++] = SYSINFO_TYPE_5ter; - gen_si[n_si++] = SYSINFO_TYPE_6; - - /* Second, we generate the selected SI via RSL */ - - for (n = 0; n < n_si; n++) { - i = gen_si[n]; - /* Only generate SI if this SI is not in "static" (user-defined) mode */ - if (!(bts->si_mode_static & (1 << i))) { - /* Set SI as being valid. gsm_generate_si() might unset - * it, if SI is not required. */ - bts->si_valid |= (1 << i); - rc = gsm_generate_si(bts, i); - if (rc < 0) - goto err_out; - si_len[i] = rc; - } else { - if (i == SYSINFO_TYPE_5 || i == SYSINFO_TYPE_5bis - || i == SYSINFO_TYPE_5ter) - si_len[i] = 18; - else if (i == SYSINFO_TYPE_6) - si_len[i] = 11; - else - si_len[i] = 23; - } - } - - /* Third, we send the selected SI via RSL */ - - for (n = 0; n < n_si; n++) { - i = gen_si[n]; - if (!GSM_BTS_HAS_SI(bts, i)) - continue; - rc = rsl_si(trx, i, si_len[i]); - if (rc < 0) - return rc; - } - - /* Make sure the PCU is aware (in case anything GPRS related has - * changed in SI */ - pcu_info_update(bts); - - return 0; -err_out: - LOGP(DRR, LOGL_ERROR, "Cannot generate SI%s for BTS %u: error <%s>, " - "most likely a problem with neighbor cell list generation\n", - get_value_string(osmo_sitype_strs, i), bts->nr, strerror(-rc)); - return rc; -} - -/* set all system information types for a BTS */ -int gsm_bts_set_system_infos(struct gsm_bts *bts) -{ - struct gsm_bts_trx *trx; - - /* Generate a new ID */ - bts->bcch_change_mark += 1; - bts->bcch_change_mark %= 0x7; - - llist_for_each_entry(trx, &bts->trx_list, list) { - int rc; - - rc = gsm_bts_trx_set_system_infos(trx); - if (rc != 0) - return rc; - } - - return 0; -} - -/* Produce a MA as specified in 10.5.2.21 */ -static int generate_ma_for_ts(struct gsm_bts_trx_ts *ts) -{ - /* we have three bitvecs: the per-timeslot ARFCNs, the cell chan ARFCNs - * and the MA */ - struct bitvec *cell_chan = &ts->trx->bts->si_common.cell_alloc; - struct bitvec *ts_arfcn = &ts->hopping.arfcns; - struct bitvec *ma = &ts->hopping.ma; - unsigned int num_cell_arfcns, bitnum, n_chan; - int i; - - /* re-set the MA to all-zero */ - ma->cur_bit = 0; - ts->hopping.ma_len = 0; - memset(ma->data, 0, ma->data_len); - - if (!ts->hopping.enabled) - return 0; - - /* count the number of ARFCNs in the cell channel allocation */ - num_cell_arfcns = 0; - for (i = 0; i < 1024; i++) { - if (bitvec_get_bit_pos(cell_chan, i)) - num_cell_arfcns++; - } - - /* pad it to octet-aligned number of bits */ - ts->hopping.ma_len = num_cell_arfcns / 8; - if (num_cell_arfcns % 8) - ts->hopping.ma_len++; - - n_chan = 0; - for (i = 0; i < 1024; i++) { - if (!bitvec_get_bit_pos(cell_chan, i)) - continue; - /* set the corresponding bit in the MA */ - bitnum = (ts->hopping.ma_len * 8) - 1 - n_chan; - if (bitvec_get_bit_pos(ts_arfcn, i)) - bitvec_set_bit_pos(ma, bitnum, 1); - else - bitvec_set_bit_pos(ma, bitnum, 0); - n_chan++; - } - - /* ARFCN 0 is special: It is coded last in the bitmask */ - if (bitvec_get_bit_pos(cell_chan, 0)) { - n_chan++; - /* set the corresponding bit in the MA */ - bitnum = (ts->hopping.ma_len * 8) - 1 - n_chan; - if (bitvec_get_bit_pos(ts_arfcn, 0)) - bitvec_set_bit_pos(ma, bitnum, 1); - else - bitvec_set_bit_pos(ma, bitnum, 0); - } - - return 0; -} - -static void bootstrap_rsl(struct gsm_bts_trx *trx) -{ - unsigned int i; - - LOGP(DRSL, LOGL_NOTICE, "bootstrapping RSL for BTS/TRX (%u/%u) " - "on ARFCN %u using MCC=%u MNC=%u LAC=%u CID=%u BSIC=%u\n", - trx->bts->nr, trx->nr, trx->arfcn, bsc_gsmnet->country_code, - bsc_gsmnet->network_code, trx->bts->location_area_code, - trx->bts->cell_identity, trx->bts->bsic); - - if (trx->bts->type == GSM_BTS_TYPE_NOKIA_SITE) { - rsl_nokia_si_begin(trx); - } - - gsm_bts_trx_set_system_infos(trx); - - if (trx->bts->type == GSM_BTS_TYPE_NOKIA_SITE) { - /* channel unspecific, power reduction in 2 dB steps */ - rsl_bs_power_control(trx, 0xFF, trx->max_power_red / 2); - rsl_nokia_si_end(trx); - } - - for (i = 0; i < ARRAY_SIZE(trx->ts); i++) - generate_ma_for_ts(&trx->ts[i]); -} - -/* Callback function to be called every time we receive a signal from INPUT */ -static int inp_sig_cb(unsigned int subsys, unsigned int signal, - void *handler_data, void *signal_data) -{ - struct input_signal_data *isd = signal_data; - struct gsm_bts_trx *trx = isd->trx; - int ts_no, lchan_no; - /* N. B: we rely on attribute order when parsing response in abis_nm_rx_get_attr_resp() */ - const uint8_t bts_attr[] = { NM_ATT_MANUF_ID, NM_ATT_SW_CONFIG, }; - const uint8_t trx_attr[] = { NM_ATT_MANUF_STATE, NM_ATT_SW_CONFIG, }; - - /* we should not request more attributes than we're ready to handle */ - OSMO_ASSERT(sizeof(bts_attr) < MAX_BTS_ATTR); - OSMO_ASSERT(sizeof(trx_attr) < MAX_BTS_ATTR); - - if (subsys != SS_L_INPUT) - return -EINVAL; - - LOGP(DLMI, LOGL_DEBUG, "%s(): Input signal '%s' received\n", __func__, - get_value_string(e1inp_signal_names, signal)); - switch (signal) { - case S_L_INP_TEI_UP: - if (isd->link_type == E1INP_SIGN_OML) { - /* TODO: this is required for the Nokia BTS, hopping is configured - during OML, other MA is not set. */ - struct gsm_bts_trx *cur_trx; - /* was static in system_information.c */ - extern int generate_cell_chan_list(uint8_t *chan_list, struct gsm_bts *bts); - uint8_t ca[20]; - /* has to be called before generate_ma_for_ts to - set bts->si_common.cell_alloc */ - generate_cell_chan_list(ca, trx->bts); - - /* Request generic BTS-level attributes */ - abis_nm_get_attr(trx->bts, NM_OC_BTS, trx->bts->nr, trx->nr, 0xFF, bts_attr, sizeof(bts_attr)); - - llist_for_each_entry(cur_trx, &trx->bts->trx_list, list) { - int i; - /* Request TRX-level attributes */ - abis_nm_get_attr(cur_trx->bts, NM_OC_BASEB_TRANSC, cur_trx->bts->nr, cur_trx->nr, 0xFF, - trx_attr, sizeof(trx_attr)); - for (i = 0; i < ARRAY_SIZE(cur_trx->ts); i++) - generate_ma_for_ts(&cur_trx->ts[i]); - } - } - if (isd->link_type == E1INP_SIGN_RSL) - bootstrap_rsl(trx); - break; - case S_L_INP_TEI_DN: - LOGP(DLMI, LOGL_ERROR, "Lost some E1 TEI link: %d %p\n", isd->link_type, trx); - - if (isd->link_type == E1INP_SIGN_OML) - rate_ctr_inc(&trx->bts->network->bsc_ctrs->ctr[BSC_CTR_BTS_OML_FAIL]); - else if (isd->link_type == E1INP_SIGN_RSL) - rate_ctr_inc(&trx->bts->network->bsc_ctrs->ctr[BSC_CTR_BTS_RSL_FAIL]); - - /* - * free all allocated channels. change the nm_state so the - * trx and trx_ts becomes unusable and chan_alloc.c can not - * allocate from it. - */ - for (ts_no = 0; ts_no < ARRAY_SIZE(trx->ts); ++ts_no) { - struct gsm_bts_trx_ts *ts = &trx->ts[ts_no]; - - for (lchan_no = 0; lchan_no < ARRAY_SIZE(ts->lchan); ++lchan_no) { - if (ts->lchan[lchan_no].state != LCHAN_S_NONE) - lchan_free(&ts->lchan[lchan_no]); - lchan_reset(&ts->lchan[lchan_no]); - } - } - - gsm_bts_mo_reset(trx->bts); - - abis_nm_clear_queue(trx->bts); - break; - default: - break; - } - - return 0; -} - -static int bootstrap_bts(struct gsm_bts *bts) -{ - int i, n; - - if (!bts->model) - return -EFAULT; - - if (bts->model->start && !bts->model->started) { - int ret = bts->model->start(bts->network); - if (ret < 0) - return ret; - - bts->model->started = true; - } - - /* FIXME: What about secondary TRX of a BTS? What about a BTS that has TRX - * in different bands? Why is 'band' a parameter of the BTS and not of the TRX? */ - switch (bts->band) { - case GSM_BAND_1800: - if (bts->c0->arfcn < 512 || bts->c0->arfcn > 885) { - LOGP(DNM, LOGL_ERROR, "GSM1800 channel must be between 512-885.\n"); - return -EINVAL; - } - break; - case GSM_BAND_1900: - if (bts->c0->arfcn < 512 || bts->c0->arfcn > 810) { - LOGP(DNM, LOGL_ERROR, "GSM1900 channel must be between 512-810.\n"); - return -EINVAL; - } - break; - case GSM_BAND_900: - if ((bts->c0->arfcn > 124 && bts->c0->arfcn < 955) || - bts->c0->arfcn > 1023) { - LOGP(DNM, LOGL_ERROR, "GSM900 channel must be between 0-124, 955-1023.\n"); - return -EINVAL; - } - break; - case GSM_BAND_850: - if (bts->c0->arfcn < 128 || bts->c0->arfcn > 251) { - LOGP(DNM, LOGL_ERROR, "GSM850 channel must be between 128-251.\n"); - return -EINVAL; - } - break; - default: - LOGP(DNM, LOGL_ERROR, "Unsupported frequency band.\n"); - return -EINVAL; - } - - if (bts->network->auth_policy == GSM_AUTH_POLICY_ACCEPT_ALL && - !bts->si_common.rach_control.cell_bar) - LOGP(DNM, LOGL_ERROR, "\nWARNING: You are running an 'accept-all' " - "network on a BTS that is not barred. This " - "configuration is likely to interfere with production " - "GSM networks and should only be used in a RF " - "shielded environment such as a faraday cage!\n\n"); - - /* Control Channel Description is set from vty/config */ - - /* T3212 is set from vty/config */ - - /* Set ccch config by looking at ts config */ - for (n=0, i=0; i<8; i++) - n += bts->c0->ts[i].pchan == GSM_PCHAN_CCCH ? 1 : 0; - - switch (n) { - case 0: - bts->si_common.chan_desc.ccch_conf = RSL_BCCH_CCCH_CONF_1_C; - /* Limit reserved block to 2 on combined channel according to - 3GPP TS 44.018 Table 10.5.2.11.1 */ - if (bts->si_common.chan_desc.bs_ag_blks_res > 2) { - LOGP(DNM, LOGL_NOTICE, "CCCH is combined with SDCCHs, " - "reducing BS-AG-BLKS-RES value %d -> 2\n", - bts->si_common.chan_desc.bs_ag_blks_res); - bts->si_common.chan_desc.bs_ag_blks_res = 2; - } - break; - case 1: - bts->si_common.chan_desc.ccch_conf = RSL_BCCH_CCCH_CONF_1_NC; - break; - case 2: - bts->si_common.chan_desc.ccch_conf = RSL_BCCH_CCCH_CONF_2_NC; - break; - case 3: - bts->si_common.chan_desc.ccch_conf = RSL_BCCH_CCCH_CONF_3_NC; - break; - case 4: - bts->si_common.chan_desc.ccch_conf = RSL_BCCH_CCCH_CONF_4_NC; - break; - default: - LOGP(DNM, LOGL_ERROR, "Unsupported CCCH timeslot configuration\n"); - return -EINVAL; - } - - bts->si_common.cell_options.pwrc = 0; /* PWRC not set */ - - bts->si_common.cell_sel_par.acs = 0; - - bts->si_common.ncc_permitted = 0xff; - - /* Initialize the BTS state */ - gsm_bts_mo_reset(bts); - - return 0; -} - -int bsc_network_alloc(mncc_recv_cb_t mncc_recv) -{ - /* initialize our data structures */ - bsc_gsmnet = bsc_network_init(tall_bsc_ctx, 1, 1, mncc_recv); - if (!bsc_gsmnet) - return -ENOMEM; - - bsc_gsmnet->name_long = talloc_strdup(bsc_gsmnet, "OpenBSC"); - bsc_gsmnet->name_short = talloc_strdup(bsc_gsmnet, "OpenBSC"); - - return 0; -} - -int bsc_network_configure(const char *config_file) -{ - struct gsm_bts *bts; - int rc; - - rc = vty_read_config_file(config_file, NULL); - if (rc < 0) { - LOGP(DNM, LOGL_FATAL, "Failed to parse the config file: '%s'\n", config_file); - return rc; - } - - /* start telnet after reading config for vty_get_bind_addr() */ - rc = telnet_init_dynif(tall_bsc_ctx, bsc_gsmnet, vty_get_bind_addr(), - OSMO_VTY_PORT_NITB_BSC); - if (rc < 0) - return rc; - - osmo_signal_register_handler(SS_NM, nm_sig_cb, NULL); - osmo_signal_register_handler(SS_L_INPUT, inp_sig_cb, NULL); - - llist_for_each_entry(bts, &bsc_gsmnet->bts_list, list) { - rc = bootstrap_bts(bts); - if (rc < 0) { - LOGP(DNM, LOGL_FATAL, "Error bootstrapping BTS\n"); - return rc; - } - rc = e1_reconfig_bts(bts); - if (rc < 0) { - LOGP(DNM, LOGL_FATAL, "Error enabling E1 input driver\n"); - return rc; - } - } - - return 0; -} diff --git a/openbsc/src/libbsc/bsc_msc.c b/openbsc/src/libbsc/bsc_msc.c deleted file mode 100644 index 82a572db..00000000 --- a/openbsc/src/libbsc/bsc_msc.c +++ /dev/null @@ -1,320 +0,0 @@ -/* Routines to talk to the MSC using the IPA Protocol */ -/* - * (C) 2010 by Holger Hans Peter Freyther - * (C) 2010 by On-Waves - * All Rights Reserved - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation; either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - * - */ - -#include -#include -#include - -#include -#include - -#include - -#include -#include -#include -#include -#include -#include -#include - -static void connection_loss(struct bsc_msc_connection *con) -{ - struct osmo_fd *fd; - - fd = &con->write_queue.bfd; - - if (con->pending_msg) { - LOGP(DMSC, LOGL_ERROR, - "MSC(%s) dropping incomplete message.\n", con->name); - msgb_free(con->pending_msg); - con->pending_msg = NULL; - } - - close(fd->fd); - fd->fd = -1; - fd->cb = osmo_wqueue_bfd_cb; - fd->when = 0; - - con->is_connected = 0; - con->first_contact = 0; - con->connection_loss(con); -} - -static void msc_con_timeout(void *_con) -{ - struct bsc_msc_connection *con = _con; - - LOGP(DMSC, LOGL_ERROR, - "MSC(%s) Connection timeout.\n", con->name); - bsc_msc_lost(con); -} - -/* called in the case of a non blocking connect */ -static int msc_connection_connect(struct osmo_fd *fd, unsigned int what) -{ - int rc; - int val; - struct bsc_msc_connection *con; - struct osmo_wqueue *queue; - - socklen_t len = sizeof(val); - - queue = container_of(fd, struct osmo_wqueue, bfd); - con = container_of(queue, struct bsc_msc_connection, write_queue); - - if ((what & BSC_FD_WRITE) == 0) { - LOGP(DMSC, LOGL_ERROR, - "MSC(%s) Callback but not writable.\n", con->name); - return -1; - } - - /* From here on we will either be connected or reconnect */ - osmo_timer_del(&con->timeout_timer); - - /* check the socket state */ - rc = getsockopt(fd->fd, SOL_SOCKET, SO_ERROR, &val, &len); - if (rc != 0) { - LOGP(DMSC, LOGL_ERROR, - "getsockopt for the MSC(%s) socket failed.\n", con->name); - goto error; - } - if (val != 0) { - LOGP(DMSC, LOGL_ERROR, - "Not connected to the MSC(%s): %d\n", - con->name, val); - goto error; - } - - - /* go to full operation */ - fd->cb = osmo_wqueue_bfd_cb; - fd->when = BSC_FD_READ | BSC_FD_EXCEPT; - - con->is_connected = 1; - LOGP(DMSC, LOGL_NOTICE, - "(Re)Connected to the MSC(%s).\n", con->name); - if (con->connected) - con->connected(con); - return 0; - -error: - osmo_fd_unregister(fd); - connection_loss(con); - return -1; -} -static void setnonblocking(struct osmo_fd *fd) -{ - int flags; - - flags = fcntl(fd->fd, F_GETFL); - if (flags < 0) { - perror("fcntl get failed"); - close(fd->fd); - fd->fd = -1; - return; - } - - flags |= O_NONBLOCK; - flags = fcntl(fd->fd, F_SETFL, flags); - if (flags < 0) { - perror("fcntl get failed"); - close(fd->fd); - fd->fd = -1; - return; - } -} - -int bsc_msc_connect(struct bsc_msc_connection *con) -{ - struct bsc_msc_dest *dest; - struct osmo_fd *fd; - struct sockaddr_in sin; - int on = 1, ret; - - if (llist_empty(con->dests)) { - LOGP(DMSC, LOGL_ERROR, - "No MSC(%s) connections configured.\n", - con->name); - connection_loss(con); - return -1; - } - - /* TODO: Why are we not using the libosmocore soecket - * abstraction, or libosmo-netif? */ - - /* move to the next connection */ - dest = (struct bsc_msc_dest *) con->dests->next; - llist_del(&dest->list); - llist_add_tail(&dest->list, con->dests); - - LOGP(DMSC, LOGL_NOTICE, - "Attempting to connect MSC(%s) at %s:%d\n", - con->name, dest->ip, dest->port); - - con->is_connected = 0; - - msgb_free(con->pending_msg); - con->pending_msg = NULL; - - fd = &con->write_queue.bfd; - fd->fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); - fd->priv_nr = 1; - - if (fd->fd < 0) { - perror("Creating TCP socket failed"); - return fd->fd; - } - - /* make it non blocking */ - setnonblocking(fd); - - /* set the socket priority */ - ret = setsockopt(fd->fd, IPPROTO_IP, IP_TOS, - &dest->dscp, sizeof(dest->dscp)); - if (ret != 0) - LOGP(DMSC, LOGL_ERROR, - "Failed to set DSCP to %d on MSC(%s). %s\n", - dest->dscp, con->name, strerror(errno)); - - memset(&sin, 0, sizeof(sin)); - sin.sin_family = AF_INET; - sin.sin_port = htons(dest->port); - inet_aton(dest->ip, &sin.sin_addr); - - ret = setsockopt(fd->fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)); - if (ret != 0) - LOGP(DMSC, LOGL_ERROR, - "Failed to set SO_REUSEADDR socket option\n"); - ret = connect(fd->fd, (struct sockaddr *) &sin, sizeof(sin)); - - if (ret == -1 && errno == EINPROGRESS) { - LOGP(DMSC, LOGL_ERROR, - "MSC(%s) Connection in progress\n", con->name); - fd->when = BSC_FD_WRITE; - fd->cb = msc_connection_connect; - osmo_timer_setup(&con->timeout_timer, msc_con_timeout, con); - osmo_timer_schedule(&con->timeout_timer, 20, 0); - } else if (ret < 0) { - perror("Connection failed"); - connection_loss(con); - return ret; - } else { - fd->when = BSC_FD_READ | BSC_FD_EXCEPT; - fd->cb = osmo_wqueue_bfd_cb; - con->is_connected = 1; - if (con->connected) - con->connected(con); - } - - ret = osmo_fd_register(fd); - if (ret < 0) { - perror("Registering the fd failed"); - close(fd->fd); - return ret; - } - - return ret; -} - -struct bsc_msc_connection *bsc_msc_create(void *ctx, struct llist_head *dests) -{ - struct bsc_msc_connection *con; - - con = talloc_zero(NULL, struct bsc_msc_connection); - if (!con) { - LOGP(DMSC, LOGL_FATAL, "Failed to create the MSC connection.\n"); - return NULL; - } - - con->dests = dests; - con->write_queue.bfd.fd = -1; - con->name = ""; - osmo_wqueue_init(&con->write_queue, 100); - return con; -} - -void bsc_msc_lost(struct bsc_msc_connection *con) -{ - osmo_wqueue_clear(&con->write_queue); - osmo_timer_del(&con->timeout_timer); - osmo_timer_del(&con->reconnect_timer); - - if (con->write_queue.bfd.fd >= 0) - osmo_fd_unregister(&con->write_queue.bfd); - connection_loss(con); -} - -static void reconnect_msc(void *_msc) -{ - struct bsc_msc_connection *con = _msc; - - LOGP(DMSC, LOGL_NOTICE, - "Attempting to reconnect to the MSC(%s).\n", con->name); - bsc_msc_connect(con); -} - -void bsc_msc_schedule_connect(struct bsc_msc_connection *con) -{ - LOGP(DMSC, LOGL_NOTICE, - "Attempting to reconnect to the MSC(%s)\n", con->name); - osmo_timer_setup(&con->reconnect_timer, reconnect_msc, con); - osmo_timer_schedule(&con->reconnect_timer, 5, 0); -} - -struct msgb *bsc_msc_id_get_resp(int fixed, const char *token, const uint8_t *res, int len) -{ - struct msgb *msg; - - if (!token) { - LOGP(DMSC, LOGL_ERROR, "No token specified.\n"); - return NULL; - } - - msg = msgb_alloc_headroom(4096, 128, "id resp"); - if (!msg) { - LOGP(DMSC, LOGL_ERROR, "Failed to create the message.\n"); - return NULL; - } - - /* - * The situation is bizarre. The encoding doesn't follow the - * TLV structure. It is more like a LV and old versions had - * it wrong but we want new versions to old servers so we - * introduce the quirk here. - */ - msg->l2h = msgb_v_put(msg, IPAC_MSGT_ID_RESP); - if (fixed) { - msgb_put_u8(msg, 0); - msgb_put_u8(msg, strlen(token) + 2); - msgb_tv_fixed_put(msg, IPAC_IDTAG_UNITNAME, strlen(token) + 1, (uint8_t *) token); - if (len > 0) { - msgb_put_u8(msg, 0); - msgb_put_u8(msg, len + 1); - msgb_tv_fixed_put(msg, 0x24, len, res); - } - } else { - msgb_l16tv_put(msg, strlen(token) + 1, - IPAC_IDTAG_UNITNAME, (uint8_t *) token); - } - - return msg; -} diff --git a/openbsc/src/libbsc/bsc_rf_ctrl.c b/openbsc/src/libbsc/bsc_rf_ctrl.c deleted file mode 100644 index b7b6fc81..00000000 --- a/openbsc/src/libbsc/bsc_rf_ctrl.c +++ /dev/null @@ -1,534 +0,0 @@ -/* RF Ctl handling socket */ - -/* (C) 2010 by Harald Welte - * (C) 2010-2014 by Holger Hans Peter Freyther - * (C) 2010-2014 by On-Waves - * All Rights Reserved - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation; either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - * - */ - -#include -#include -#include -#include -#include -#include - -#include -#include -#include - -#include -#include - -#include -#include - -#define RF_CMD_QUERY '?' -#define RF_CMD_OFF '0' -#define RF_CMD_ON '1' -#define RF_CMD_D_OFF 'd' -#define RF_CMD_ON_G 'g' - -static const struct value_string opstate_names[] = { - { OSMO_BSC_RF_OPSTATE_INOPERATIONAL, "inoperational" }, - { OSMO_BSC_RF_OPSTATE_OPERATIONAL, "operational" }, - { 0, NULL } -}; - -static const struct value_string adminstate_names[] = { - { OSMO_BSC_RF_ADMINSTATE_UNLOCKED, "unlocked" }, - { OSMO_BSC_RF_ADMINSTATE_LOCKED, "locked" }, - { 0, NULL } -}; - -static const struct value_string policy_names[] = { - { OSMO_BSC_RF_POLICY_OFF, "off" }, - { OSMO_BSC_RF_POLICY_ON, "on" }, - { OSMO_BSC_RF_POLICY_GRACE, "grace" }, - { OSMO_BSC_RF_POLICY_UNKNOWN, "unknown" }, - { 0, NULL } -}; - -const char *osmo_bsc_rf_get_opstate_name(enum osmo_bsc_rf_opstate opstate) -{ - return get_value_string(opstate_names, opstate); -} - -const char *osmo_bsc_rf_get_adminstate_name(enum osmo_bsc_rf_adminstate adminstate) -{ - return get_value_string(adminstate_names, adminstate); -} - -const char *osmo_bsc_rf_get_policy_name(enum osmo_bsc_rf_policy policy) -{ - return get_value_string(policy_names, policy); -} - -enum osmo_bsc_rf_opstate osmo_bsc_rf_get_opstate_by_bts(struct gsm_bts *bts) -{ - struct gsm_bts_trx *trx; - - llist_for_each_entry(trx, &bts->trx_list, list) { - if (trx->mo.nm_state.operational == NM_OPSTATE_ENABLED) - return OSMO_BSC_RF_OPSTATE_OPERATIONAL; - } - - /* No trx were active, so this bts is disabled */ - return OSMO_BSC_RF_OPSTATE_INOPERATIONAL; -} - -enum osmo_bsc_rf_adminstate osmo_bsc_rf_get_adminstate_by_bts(struct gsm_bts *bts) -{ - struct gsm_bts_trx *trx; - - llist_for_each_entry(trx, &bts->trx_list, list) { - if (trx->mo.nm_state.administrative == NM_STATE_UNLOCKED) - return OSMO_BSC_RF_ADMINSTATE_UNLOCKED; - } - - /* All trx administrative states were locked */ - return OSMO_BSC_RF_ADMINSTATE_LOCKED; -} - -enum osmo_bsc_rf_policy osmo_bsc_rf_get_policy_by_bts(struct gsm_bts *bts) -{ - struct osmo_bsc_data *bsc_data = bts->network->bsc_data; - - if (!bsc_data) - return OSMO_BSC_RF_POLICY_UNKNOWN; - - switch (bsc_data->rf_ctrl->policy) { - case S_RF_ON: - return OSMO_BSC_RF_POLICY_ON; - case S_RF_OFF: - return OSMO_BSC_RF_POLICY_OFF; - case S_RF_GRACE: - return OSMO_BSC_RF_POLICY_GRACE; - default: - return OSMO_BSC_RF_POLICY_UNKNOWN; - } -} - -static int lock_each_trx(struct gsm_network *net, int lock) -{ - struct gsm_bts *bts; - - llist_for_each_entry(bts, &net->bts_list, list) { - struct gsm_bts_trx *trx; - - /* Exclude the BTS from the global lock */ - if (bts->excl_from_rf_lock) { - LOGP(DLINP, LOGL_DEBUG, - "Excluding BTS(%d) from trx lock.\n", bts->nr); - continue; - } - - llist_for_each_entry(trx, &bts->trx_list, list) { - gsm_trx_lock_rf(trx, lock); - } - } - - return 0; -} - -static void send_resp(struct osmo_bsc_rf_conn *conn, char send) -{ - struct msgb *msg; - - msg = msgb_alloc(10, "RF Query"); - if (!msg) { - LOGP(DLINP, LOGL_ERROR, "Failed to allocate response msg.\n"); - return; - } - - msg->l2h = msgb_put(msg, 1); - msg->l2h[0] = send; - - if (osmo_wqueue_enqueue(&conn->queue, msg) != 0) { - LOGP(DLINP, LOGL_ERROR, "Failed to enqueue the answer.\n"); - msgb_free(msg); - return; - } - - return; -} - - -/* - * Send a - * 'g' when we are in grace mode - * '1' when one TRX is online, - * '0' otherwise - */ -static void handle_query(struct osmo_bsc_rf_conn *conn) -{ - struct gsm_bts *bts; - char send = RF_CMD_OFF; - - if (conn->rf->policy == S_RF_GRACE) - return send_resp(conn, RF_CMD_ON_G); - - llist_for_each_entry(bts, &conn->rf->gsm_network->bts_list, list) { - struct gsm_bts_trx *trx; - - /* Exclude the BTS from the global lock */ - if (bts->excl_from_rf_lock) { - LOGP(DLINP, LOGL_DEBUG, - "Excluding BTS(%d) from query.\n", bts->nr); - continue; - } - llist_for_each_entry(trx, &bts->trx_list, list) { - if (trx->mo.nm_state.availability == NM_AVSTATE_OK && - trx->mo.nm_state.operational != NM_OPSTATE_DISABLED) { - send = RF_CMD_ON; - break; - } - } - } - - send_resp(conn, send); -} - -static void rf_check_cb(void *_data) -{ - struct gsm_bts *bts; - struct osmo_bsc_rf *rf = _data; - - llist_for_each_entry(bts, &rf->gsm_network->bts_list, list) { - struct gsm_bts_trx *trx; - - /* don't bother to check a booting or missing BTS */ - if (!bts->oml_link || !is_ipaccess_bts(bts)) - continue; - - /* Exclude the BTS from the global lock */ - if (bts->excl_from_rf_lock) { - LOGP(DLINP, LOGL_DEBUG, - "Excluding BTS(%d) from query.\n", bts->nr); - continue; - } - - llist_for_each_entry(trx, &bts->trx_list, list) { - if (trx->mo.nm_state.availability != NM_AVSTATE_OK || - trx->mo.nm_state.operational != NM_OPSTATE_ENABLED || - trx->mo.nm_state.administrative != NM_STATE_UNLOCKED) { - LOGP(DNM, LOGL_ERROR, "RF activation failed. Starting again.\n"); - ipaccess_drop_oml(bts); - break; - } - } - } -} - -static void send_signal(struct osmo_bsc_rf *rf, int val) -{ - struct rf_signal_data sig; - sig.net = rf->gsm_network; - - rf->policy = val; - osmo_signal_dispatch(SS_RF, val, &sig); -} - -static int switch_rf_off(struct osmo_bsc_rf *rf) -{ - lock_each_trx(rf->gsm_network, 1); - send_signal(rf, S_RF_OFF); - - return 0; -} - -static void grace_timeout(void *_data) -{ - struct osmo_bsc_rf *rf = (struct osmo_bsc_rf *) _data; - - LOGP(DLINP, LOGL_NOTICE, "Grace timeout. Going to disable all BTS/TRX.\n"); - switch_rf_off(rf); -} - -static int enter_grace(struct osmo_bsc_rf *rf) -{ - if (osmo_timer_pending(&rf->grace_timeout)) { - LOGP(DLINP, LOGL_NOTICE, "RF Grace timer is pending. Not restarting.\n"); - return 0; - } - - osmo_timer_setup(&rf->grace_timeout, grace_timeout, rf); - osmo_timer_schedule(&rf->grace_timeout, rf->gsm_network->bsc_data->mid_call_timeout, 0); - LOGP(DLINP, LOGL_NOTICE, "Going to switch RF off in %d seconds.\n", - rf->gsm_network->bsc_data->mid_call_timeout); - - send_signal(rf, S_RF_GRACE); - return 0; -} - -static void rf_delay_cmd_cb(void *data) -{ - struct osmo_bsc_rf *rf = data; - - switch (rf->last_request) { - case RF_CMD_D_OFF: - rf->last_state_command = "RF Direct Off"; - osmo_timer_del(&rf->rf_check); - osmo_timer_del(&rf->grace_timeout); - switch_rf_off(rf); - break; - case RF_CMD_ON: - rf->last_state_command = "RF Direct On"; - osmo_timer_del(&rf->grace_timeout); - lock_each_trx(rf->gsm_network, 0); - send_signal(rf, S_RF_ON); - osmo_timer_schedule(&rf->rf_check, 3, 0); - break; - case RF_CMD_OFF: - rf->last_state_command = "RF Scheduled Off"; - osmo_timer_del(&rf->rf_check); - enter_grace(rf); - break; - } -} - -static int rf_read_cmd(struct osmo_fd *fd) -{ - struct osmo_bsc_rf_conn *conn = fd->data; - char buf[1]; - int rc; - - rc = read(fd->fd, buf, sizeof(buf)); - if (rc != sizeof(buf)) { - LOGP(DLINP, LOGL_ERROR, "Short read %d/%s\n", errno, strerror(errno)); - osmo_fd_unregister(fd); - close(fd->fd); - osmo_wqueue_clear(&conn->queue); - talloc_free(conn); - return -1; - } - - switch (buf[0]) { - case RF_CMD_QUERY: - handle_query(conn); - break; - case RF_CMD_D_OFF: - case RF_CMD_ON: - case RF_CMD_OFF: - osmo_bsc_rf_schedule_lock(conn->rf, buf[0]); - break; - default: - conn->rf->last_state_command = "Unknown command"; - LOGP(DLINP, LOGL_ERROR, "Unknown command %d\n", buf[0]); - break; - } - - return 0; -} - -static int rf_write_cmd(struct osmo_fd *fd, struct msgb *msg) -{ - int rc; - - rc = write(fd->fd, msg->data, msg->len); - if (rc != msg->len) { - LOGP(DLINP, LOGL_ERROR, "Short write %d/%s\n", errno, strerror(errno)); - return -1; - } - - return 0; -} - -static int rf_ctrl_accept(struct osmo_fd *bfd, unsigned int what) -{ - struct osmo_bsc_rf_conn *conn; - struct osmo_bsc_rf *rf = bfd->data; - struct sockaddr_un addr; - socklen_t len = sizeof(addr); - int fd; - - fd = accept(bfd->fd, (struct sockaddr *) &addr, &len); - if (fd < 0) { - LOGP(DLINP, LOGL_ERROR, "Failed to accept. errno: %d/%s\n", - errno, strerror(errno)); - return -1; - } - - conn = talloc_zero(rf, struct osmo_bsc_rf_conn); - if (!conn) { - LOGP(DLINP, LOGL_ERROR, "Failed to allocate mem.\n"); - close(fd); - return -1; - } - - osmo_wqueue_init(&conn->queue, 10); - conn->queue.bfd.data = conn; - conn->queue.bfd.fd = fd; - conn->queue.bfd.when = BSC_FD_READ | BSC_FD_WRITE; - conn->queue.read_cb = rf_read_cmd; - conn->queue.write_cb = rf_write_cmd; - conn->rf = rf; - - if (osmo_fd_register(&conn->queue.bfd) != 0) { - close(fd); - talloc_free(conn); - return -1; - } - - return 0; -} - -static void rf_auto_off_cb(void *_timer) -{ - struct osmo_bsc_rf *rf = _timer; - - LOGP(DLINP, LOGL_NOTICE, - "Going to switch off RF due lack of a MSC connection.\n"); - osmo_bsc_rf_schedule_lock(rf, RF_CMD_D_OFF); -} - -static int msc_signal_handler(unsigned int subsys, unsigned int signal, - void *handler_data, void *signal_data) -{ - struct gsm_network *net; - struct msc_signal_data *msc; - struct osmo_bsc_rf *rf; - - /* check if we want to handle this signal */ - if (subsys != SS_MSC) - return 0; - - net = handler_data; - msc = signal_data; - - /* check if we have the needed information */ - if (!net->bsc_data) - return 0; - if (msc->data->type != MSC_CON_TYPE_NORMAL) - return 0; - - rf = net->bsc_data->rf_ctrl; - switch (signal) { - case S_MSC_LOST: - if (net->bsc_data->auto_off_timeout < 0) - return 0; - if (osmo_timer_pending(&rf->auto_off_timer)) - return 0; - osmo_timer_schedule(&rf->auto_off_timer, - net->bsc_data->auto_off_timeout, 0); - break; - case S_MSC_CONNECTED: - osmo_timer_del(&rf->auto_off_timer); - break; - } - - return 0; -} - -static int rf_create_socket(struct osmo_bsc_rf *rf, const char *path) -{ - unsigned int namelen; - struct sockaddr_un local; - struct osmo_fd *bfd; - int rc; - - bfd = &rf->listen; - bfd->fd = socket(AF_UNIX, SOCK_STREAM, 0); - if (bfd->fd < 0) { - LOGP(DLINP, LOGL_ERROR, "Can not create socket. %d/%s\n", - errno, strerror(errno)); - return -1; - } - - local.sun_family = AF_UNIX; - osmo_strlcpy(local.sun_path, path, sizeof(local.sun_path)); - unlink(local.sun_path); - - /* we use the same magic that X11 uses in Xtranssock.c for - * calculating the proper length of the sockaddr */ -#if defined(BSD44SOCKETS) || defined(__UNIXWARE__) - local.sun_len = strlen(local.sun_path); -#endif -#if defined(BSD44SOCKETS) || defined(SUN_LEN) - namelen = SUN_LEN(&local); -#else - namelen = strlen(local.sun_path) + - offsetof(struct sockaddr_un, sun_path); -#endif - - rc = bind(bfd->fd, (struct sockaddr *) &local, namelen); - if (rc != 0) { - LOGP(DLINP, LOGL_ERROR, "Failed to bind '%s' errno: %d/%s\n", - local.sun_path, errno, strerror(errno)); - close(bfd->fd); - return -1; - } - - if (listen(bfd->fd, 0) != 0) { - LOGP(DLINP, LOGL_ERROR, "Failed to listen: %d/%s\n", errno, strerror(errno)); - close(bfd->fd); - return -1; - } - - bfd->when = BSC_FD_READ; - bfd->cb = rf_ctrl_accept; - bfd->data = rf; - - if (osmo_fd_register(bfd) != 0) { - LOGP(DLINP, LOGL_ERROR, "Failed to register bfd.\n"); - close(bfd->fd); - return -1; - } - - return 0; -} - -struct osmo_bsc_rf *osmo_bsc_rf_create(const char *path, struct gsm_network *net) -{ - struct osmo_bsc_rf *rf; - - rf = talloc_zero(NULL, struct osmo_bsc_rf); - if (!rf) { - LOGP(DLINP, LOGL_ERROR, "Failed to create osmo_bsc_rf.\n"); - return NULL; - } - - if (path && rf_create_socket(rf, path) != 0) { - talloc_free(rf); - return NULL; - } - - rf->gsm_network = net; - rf->policy = S_RF_ON; - rf->last_state_command = ""; - rf->last_rf_lock_ctrl_command = talloc_strdup(rf, ""); - - /* check the rf state */ - osmo_timer_setup(&rf->rf_check, rf_check_cb, rf); - - /* delay cmd handling */ - osmo_timer_setup(&rf->delay_cmd, rf_delay_cmd_cb, rf); - - osmo_timer_setup(&rf->auto_off_timer, rf_auto_off_cb, rf); - - /* listen to RF signals */ - osmo_signal_register_handler(SS_MSC, msc_signal_handler, net); - - return rf; -} - -void osmo_bsc_rf_schedule_lock(struct osmo_bsc_rf *rf, char cmd) -{ - rf->last_request = cmd; - if (!osmo_timer_pending(&rf->delay_cmd)) - osmo_timer_schedule(&rf->delay_cmd, 1, 0); -} diff --git a/openbsc/src/libbsc/bsc_rll.c b/openbsc/src/libbsc/bsc_rll.c deleted file mode 100644 index bb488da1..00000000 --- a/openbsc/src/libbsc/bsc_rll.c +++ /dev/null @@ -1,139 +0,0 @@ -/* GSM BSC Radio Link Layer API - * 3GPP TS 08.58 version 8.6.0 Release 1999 / ETSI TS 100 596 V8.6.0 */ - -/* (C) 2009 by Harald Welte - * - * All Rights Reserved - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation; either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - * - */ - -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -struct bsc_rll_req { - struct llist_head list; - struct osmo_timer_list timer; - - struct gsm_lchan *lchan; - uint8_t link_id; - - void (*cb)(struct gsm_lchan *lchan, uint8_t link_id, - void *data, enum bsc_rllr_ind); - void *data; -}; - -/* we only compare C1, C2 and SAPI */ -#define LINKID_MASK 0xC7 - -static LLIST_HEAD(bsc_rll_reqs); - -static void complete_rllr(struct bsc_rll_req *rllr, enum bsc_rllr_ind type) -{ - llist_del(&rllr->list); - rllr->cb(rllr->lchan, rllr->link_id, rllr->data, type); - talloc_free(rllr); -} - -static void timer_cb(void *_rllr) -{ - struct bsc_rll_req *rllr = _rllr; - - complete_rllr(rllr, BSC_RLLR_IND_TIMEOUT); -} - -/* establish a RLL connection with given SAPI / priority */ -int rll_establish(struct gsm_lchan *lchan, uint8_t sapi, - void (*cb)(struct gsm_lchan *, uint8_t, void *, - enum bsc_rllr_ind), - void *data) -{ - struct bsc_rll_req *rllr = talloc_zero(tall_bsc_ctx, struct bsc_rll_req); - uint8_t link_id; - if (!rllr) - return -ENOMEM; - - link_id = sapi; - - /* If we are a TCH and not in signalling mode, we need to - * indicate that the new RLL connection is to be made on the SACCH */ - if ((lchan->type == GSM_LCHAN_TCH_F || - lchan->type == GSM_LCHAN_TCH_H) && sapi != 0) - link_id |= 0x40; - - rllr->lchan = lchan; - rllr->link_id = link_id; - rllr->cb = cb; - rllr->data = data; - - llist_add(&rllr->list, &bsc_rll_reqs); - - osmo_timer_setup(&rllr->timer, timer_cb, rllr); - osmo_timer_schedule(&rllr->timer, 7, 0); - - /* send the RSL RLL ESTablish REQuest */ - return rsl_establish_request(rllr->lchan, rllr->link_id); -} - -/* Called from RSL code in case we have received an indication regarding - * any RLL link */ -void rll_indication(struct gsm_lchan *lchan, uint8_t link_id, uint8_t type) -{ - struct bsc_rll_req *rllr, *rllr2; - - llist_for_each_entry_safe(rllr, rllr2, &bsc_rll_reqs, list) { - if (rllr->lchan == lchan && - (rllr->link_id & LINKID_MASK) == (link_id & LINKID_MASK)) { - osmo_timer_del(&rllr->timer); - complete_rllr(rllr, type); - return; - } - } -} - -static int rll_lchan_signal(unsigned int subsys, unsigned int signal, - void *handler_data, void *signal_data) -{ - struct challoc_signal_data *challoc; - struct bsc_rll_req *rllr, *rllr2; - - if (subsys != SS_CHALLOC || signal != S_CHALLOC_FREED) - return 0; - - challoc = (struct challoc_signal_data *) signal_data; - - llist_for_each_entry_safe(rllr, rllr2, &bsc_rll_reqs, list) { - if (rllr->lchan == challoc->lchan) { - osmo_timer_del(&rllr->timer); - complete_rllr(rllr, BSC_RLLR_IND_ERR_IND); - } - } - - return 0; -} - -static __attribute__((constructor)) void on_dso_load_rll(void) -{ - osmo_signal_register_handler(SS_CHALLOC, rll_lchan_signal, NULL); -} diff --git a/openbsc/src/libbsc/bsc_subscriber.c b/openbsc/src/libbsc/bsc_subscriber.c deleted file mode 100644 index 73e61e80..00000000 --- a/openbsc/src/libbsc/bsc_subscriber.c +++ /dev/null @@ -1,168 +0,0 @@ -/* GSM subscriber details for use in BSC land */ - -/* - * (C) 2016 by sysmocom s.f.m.c. GmbH - * - * Author: Neels Hofmeyr - * - * All Rights Reserved - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation; either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public 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 bsc_subscr *bsc_subscr_alloc(struct llist_head *list) -{ - struct bsc_subscr *bsub; - - bsub = talloc_zero(list, struct bsc_subscr); - if (!bsub) - return NULL; - - llist_add_tail(&bsub->entry, list); - bsub->use_count = 1; - - return bsub; -} - -struct bsc_subscr *bsc_subscr_find_by_imsi(struct llist_head *list, - const char *imsi) -{ - struct bsc_subscr *bsub; - - if (!imsi || !*imsi) - return NULL; - - llist_for_each_entry(bsub, list, entry) { - if (!strcmp(bsub->imsi, imsi)) - return bsc_subscr_get(bsub); - } - return NULL; -} - -struct bsc_subscr *bsc_subscr_find_by_tmsi(struct llist_head *list, - uint32_t tmsi) -{ - struct bsc_subscr *bsub; - - if (tmsi == GSM_RESERVED_TMSI) - return NULL; - - llist_for_each_entry(bsub, list, entry) { - if (bsub->tmsi == tmsi) - return bsc_subscr_get(bsub); - } - return NULL; -} - -void bsc_subscr_set_imsi(struct bsc_subscr *bsub, const char *imsi) -{ - if (!bsub) - return; - osmo_strlcpy(bsub->imsi, imsi, sizeof(bsub->imsi)); -} - -struct bsc_subscr *bsc_subscr_find_or_create_by_imsi(struct llist_head *list, - const char *imsi) -{ - struct bsc_subscr *bsub; - bsub = bsc_subscr_find_by_imsi(list, imsi); - if (bsub) - return bsub; - bsub = bsc_subscr_alloc(list); - bsc_subscr_set_imsi(bsub, imsi); - return bsub; -} - -struct bsc_subscr *bsc_subscr_find_or_create_by_tmsi(struct llist_head *list, - uint32_t tmsi) -{ - struct bsc_subscr *bsub; - bsub = bsc_subscr_find_by_tmsi(list, tmsi); - if (bsub) - return bsub; - bsub = bsc_subscr_alloc(list); - bsub->tmsi = tmsi; - return bsub; -} - -const char *bsc_subscr_name(struct bsc_subscr *bsub) -{ - static char buf[32]; - if (!bsub) - return "unknown"; - if (bsub->imsi[0]) - snprintf(buf, sizeof(buf), "IMSI:%s", bsub->imsi); - else - snprintf(buf, sizeof(buf), "TMSI:0x%08x", bsub->tmsi); - return buf; -} - -static void bsc_subscr_free(struct bsc_subscr *bsub) -{ - llist_del(&bsub->entry); - talloc_free(bsub); -} - -struct bsc_subscr *_bsc_subscr_get(struct bsc_subscr *bsub, - const char *file, int line) -{ - OSMO_ASSERT(bsub->use_count < INT_MAX); - bsub->use_count++; - LOGPSRC(DREF, LOGL_DEBUG, file, line, - "BSC subscr %s usage increases to: %d\n", - bsc_subscr_name(bsub), bsub->use_count); - return bsub; -} - -struct bsc_subscr *_bsc_subscr_put(struct bsc_subscr *bsub, - const char *file, int line) -{ - bsub->use_count--; - LOGPSRC(DREF, bsub->use_count >= 0? LOGL_DEBUG : LOGL_ERROR, - file, line, - "BSC subscr %s usage decreases to: %d\n", - bsc_subscr_name(bsub), bsub->use_count); - if (bsub->use_count <= 0) - bsc_subscr_free(bsub); - return NULL; -} - -void log_set_filter_bsc_subscr(struct log_target *target, - struct bsc_subscr *bsc_subscr) -{ - struct bsc_subscr **fsub = (void*)&target->filter_data[LOG_FLT_BSC_SUBSCR]; - - /* free the old data */ - if (*fsub) { - bsc_subscr_put(*fsub); - *fsub = NULL; - } - - if (bsc_subscr) { - target->filter_map |= (1 << LOG_FLT_BSC_SUBSCR); - *fsub = bsc_subscr_get(bsc_subscr); - } else - target->filter_map &= ~(1 << LOG_FLT_BSC_SUBSCR); -} diff --git a/openbsc/src/libbsc/bsc_vty.c b/openbsc/src/libbsc/bsc_vty.c deleted file mode 100644 index 9fc28950..00000000 --- a/openbsc/src/libbsc/bsc_vty.c +++ /dev/null @@ -1,4304 +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 -#include -#include -#include -#include - -#include - -#include - -#include "../../bscconfig.h" - - -#define LCHAN_NR_STR "Logical Channel Number\n" - - -/* FIXME: this should go to some common file */ -static const struct value_string gprs_ns_timer_strs[] = { - { 0, "tns-block" }, - { 1, "tns-block-retries" }, - { 2, "tns-reset" }, - { 3, "tns-reset-retries" }, - { 4, "tns-test" }, - { 5, "tns-alive" }, - { 6, "tns-alive-retries" }, - { 0, NULL } -}; - -static const struct value_string gprs_bssgp_cfg_strs[] = { - { 0, "blocking-timer" }, - { 1, "blocking-retries" }, - { 2, "unblocking-retries" }, - { 3, "reset-timer" }, - { 4, "reset-retries" }, - { 5, "suspend-timer" }, - { 6, "suspend-retries" }, - { 7, "resume-timer" }, - { 8, "resume-retries" }, - { 9, "capability-update-timer" }, - { 10, "capability-update-retries" }, - { 0, NULL } -}; - -static const struct value_string bts_neigh_mode_strs[] = { - { NL_MODE_AUTOMATIC, "automatic" }, - { NL_MODE_MANUAL, "manual" }, - { NL_MODE_MANUAL_SI5SEP, "manual-si5" }, - { 0, NULL } -}; - -const struct value_string bts_loc_fix_names[] = { - { BTS_LOC_FIX_INVALID, "invalid" }, - { BTS_LOC_FIX_2D, "fix2d" }, - { BTS_LOC_FIX_3D, "fix3d" }, - { 0, NULL } -}; - -struct cmd_node bts_node = { - BTS_NODE, - "%s(config-net-bts)# ", - 1, -}; - -struct cmd_node trx_node = { - TRX_NODE, - "%s(config-net-bts-trx)# ", - 1, -}; - -struct cmd_node ts_node = { - TS_NODE, - "%s(config-net-bts-trx-ts)# ", - 1, -}; - -static int dummy_config_write(struct vty *v) -{ - return CMD_SUCCESS; -} - -static void net_dump_nmstate(struct vty *vty, struct gsm_nm_state *nms) -{ - vty_out(vty,"Oper '%s', Admin '%s', Avail '%s'%s", - abis_nm_opstate_name(nms->operational), - get_value_string(abis_nm_adm_state_names, nms->administrative), - abis_nm_avail_name(nms->availability), VTY_NEWLINE); -} - -static void dump_pchan_load_vty(struct vty *vty, char *prefix, - const struct pchan_load *pl) -{ - int i; - - for (i = 0; i < ARRAY_SIZE(pl->pchan); i++) { - const struct load_counter *lc = &pl->pchan[i]; - unsigned int percent; - - if (lc->total == 0) - continue; - - percent = (lc->used * 100) / lc->total; - - vty_out(vty, "%s%20s: %3u%% (%u/%u)%s", prefix, - gsm_pchan_name(i), percent, lc->used, lc->total, - VTY_NEWLINE); - } -} - -static void net_dump_vty(struct vty *vty, struct gsm_network *net) -{ - struct pchan_load pl; - - vty_out(vty, "BSC is on Country Code %u, Network Code %u " - "and has %u BTS%s", net->country_code, net->network_code, - net->num_bts, VTY_NEWLINE); - vty_out(vty, " Long network name: '%s'%s", - net->name_long, VTY_NEWLINE); - vty_out(vty, " Short network name: '%s'%s", - net->name_short, VTY_NEWLINE); - vty_out(vty, " Authentication policy: %s", - gsm_auth_policy_name(net->auth_policy)); - if (net->authorized_reg_str) - vty_out(vty, ", authorized regexp: %s", net->authorized_reg_str); - vty_out(vty, "%s", VTY_NEWLINE); - vty_out(vty, " Auto create subscriber: %s%s", - net->auto_create_subscr ? "yes" : "no", VTY_NEWLINE); - vty_out(vty, " Auto assign extension: %s%s", - net->auto_assign_exten ? "yes" : "no", VTY_NEWLINE); - vty_out(vty, " Location updating reject cause: %u%s", - net->reject_cause, VTY_NEWLINE); - vty_out(vty, " Encryption: A5/%u%s", net->a5_encryption, - VTY_NEWLINE); - vty_out(vty, " NECI (TCH/H): %u%s", net->neci, - VTY_NEWLINE); - vty_out(vty, " Use TCH for Paging any: %d%s", net->pag_any_tch, - VTY_NEWLINE); - vty_out(vty, " RRLP Mode: %s%s", rrlp_mode_name(net->rrlp.mode), - VTY_NEWLINE); - vty_out(vty, " MM Info: %s%s", net->send_mm_info ? "On" : "Off", - VTY_NEWLINE); - vty_out(vty, " Handover: %s%s", net->handover.active ? "On" : "Off", - VTY_NEWLINE); - network_chan_load(&pl, net); - vty_out(vty, " Current Channel Load:%s", VTY_NEWLINE); - dump_pchan_load_vty(vty, " ", &pl); - - /* show rf */ - if (net->bsc_data) - vty_out(vty, " Last RF Command: %s%s", - net->bsc_data->rf_ctrl->last_state_command, - VTY_NEWLINE); - if (net->bsc_data) - vty_out(vty, " Last RF Lock Command: %s%s", - net->bsc_data->rf_ctrl->last_rf_lock_ctrl_command, - VTY_NEWLINE); -} - -DEFUN(bsc_show_net, bsc_show_net_cmd, "show network", - SHOW_STR "Display information about a GSM NETWORK\n") -{ - struct gsm_network *net = gsmnet_from_vty(vty); - net_dump_vty(vty, net); - - return CMD_SUCCESS; -} - -static void e1isl_dump_vty(struct vty *vty, struct e1inp_sign_link *e1l) -{ - struct e1inp_line *line; - - if (!e1l) { - vty_out(vty, " None%s", VTY_NEWLINE); - return; - } - - line = e1l->ts->line; - - vty_out(vty, " E1 Line %u, Type %s: Timeslot %u, Mode %s%s", - line->num, line->driver->name, e1l->ts->num, - e1inp_signtype_name(e1l->type), VTY_NEWLINE); - vty_out(vty, " E1 TEI %u, SAPI %u%s", - e1l->tei, e1l->sapi, VTY_NEWLINE); -} - -static void bts_dump_vty(struct vty *vty, struct gsm_bts *bts) -{ - struct pchan_load pl; - - vty_out(vty, "BTS %u is of %s type in band %s, has CI %u LAC %u, " - "BSIC %u (NCC=%u, BCC=%u) and %u TRX%s", - bts->nr, btstype2str(bts->type), gsm_band_name(bts->band), - bts->cell_identity, - bts->location_area_code, bts->bsic, - bts->bsic >> 3, bts->bsic & 7, - bts->num_trx, VTY_NEWLINE); - vty_out(vty, "Description: %s%s", - bts->description ? bts->description : "(null)", VTY_NEWLINE); - if (strnlen(bts->pcu_version, MAX_VERSION_LENGTH)) - vty_out(vty, "PCU version %s connected%s", bts->pcu_version, - VTY_NEWLINE); - vty_out(vty, "MS Max power: %u dBm%s", bts->ms_max_power, VTY_NEWLINE); - vty_out(vty, "Minimum Rx Level for Access: %i dBm%s", - rxlev2dbm(bts->si_common.cell_sel_par.rxlev_acc_min), - VTY_NEWLINE); - vty_out(vty, "Cell Reselection Hysteresis: %u dBm%s", - bts->si_common.cell_sel_par.cell_resel_hyst*2, VTY_NEWLINE); - vty_out(vty, "RACH TX-Integer: %u%s", bts->si_common.rach_control.tx_integer, - VTY_NEWLINE); - vty_out(vty, "RACH Max transmissions: %u%s", - rach_max_trans_raw2val(bts->si_common.rach_control.max_trans), - VTY_NEWLINE); - if (bts->si_common.rach_control.cell_bar) - vty_out(vty, " CELL IS BARRED%s", VTY_NEWLINE); - if (bts->dtxu != GSM48_DTX_SHALL_NOT_BE_USED) - vty_out(vty, "Uplink DTX: %s%s", - (bts->dtxu != GSM48_DTX_SHALL_BE_USED) ? - "enabled" : "forced", VTY_NEWLINE); - else - vty_out(vty, "Uplink DTX: not enabled%s", VTY_NEWLINE); - vty_out(vty, "Downlink DTX: %senabled%s", bts->dtxd ? "" : "not ", - VTY_NEWLINE); - vty_out(vty, "Channel Description Attachment: %s%s", - (bts->si_common.chan_desc.att) ? "yes" : "no", VTY_NEWLINE); - vty_out(vty, "Channel Description BS-PA-MFRMS: %u%s", - bts->si_common.chan_desc.bs_pa_mfrms + 2, VTY_NEWLINE); - vty_out(vty, "Channel Description BS-AG_BLKS-RES: %u%s", - bts->si_common.chan_desc.bs_ag_blks_res, VTY_NEWLINE); - vty_out(vty, "System Information present: 0x%08x, static: 0x%08x%s", - bts->si_valid, bts->si_mode_static, VTY_NEWLINE); - vty_out(vty, "Early Classmark Sending: %s%s", - bts->early_classmark_allowed ? "allowed" : "forbidden", - VTY_NEWLINE); - if (bts->pcu_sock_path) - vty_out(vty, "PCU Socket Path: %s%s", bts->pcu_sock_path, VTY_NEWLINE); - if (is_ipaccess_bts(bts)) - vty_out(vty, " Unit ID: %u/%u/0, OML Stream ID 0x%02x%s", - bts->ip_access.site_id, bts->ip_access.bts_id, - bts->oml_tei, VTY_NEWLINE); - else if (bts->type == GSM_BTS_TYPE_NOKIA_SITE) - vty_out(vty, " Skip Reset: %d%s", - bts->nokia.skip_reset, VTY_NEWLINE); - vty_out(vty, " NM State: "); - net_dump_nmstate(vty, &bts->mo.nm_state); - vty_out(vty, " Site Mgr NM State: "); - net_dump_nmstate(vty, &bts->site_mgr.mo.nm_state); - vty_out(vty, " GPRS NSE: "); - net_dump_nmstate(vty, &bts->gprs.nse.mo.nm_state); - vty_out(vty, " GPRS CELL: "); - net_dump_nmstate(vty, &bts->gprs.cell.mo.nm_state); - vty_out(vty, " GPRS NSVC0: "); - net_dump_nmstate(vty, &bts->gprs.nsvc[0].mo.nm_state); - vty_out(vty, " GPRS NSVC1: "); - net_dump_nmstate(vty, &bts->gprs.nsvc[1].mo.nm_state); - vty_out(vty, " Paging: %u pending requests, %u free slots%s", - paging_pending_requests_nr(bts), - bts->paging.available_slots, VTY_NEWLINE); - if (is_ipaccess_bts(bts)) { - vty_out(vty, " OML Link state: %s.%s", - bts->oml_link ? "connected" : "disconnected", VTY_NEWLINE); - } else { - vty_out(vty, " E1 Signalling Link:%s", VTY_NEWLINE); - e1isl_dump_vty(vty, bts->oml_link); - } - - /* FIXME: chan_desc */ - memset(&pl, 0, sizeof(pl)); - bts_chan_load(&pl, bts); - vty_out(vty, " Current Channel Load:%s", VTY_NEWLINE); - dump_pchan_load_vty(vty, " ", &pl); -} - -DEFUN(show_bts, show_bts_cmd, "show bts [<0-255>]", - SHOW_STR "Display information about a BTS\n" - "BTS number") -{ - struct gsm_network *net = gsmnet_from_vty(vty); - int bts_nr; - - if (argc != 0) { - /* use the BTS number that the user has specified */ - bts_nr = atoi(argv[0]); - if (bts_nr >= net->num_bts) { - vty_out(vty, "%% can't find BTS '%s'%s", argv[0], - VTY_NEWLINE); - return CMD_WARNING; - } - bts_dump_vty(vty, gsm_bts_num(net, bts_nr)); - return CMD_SUCCESS; - } - /* print all BTS's */ - for (bts_nr = 0; bts_nr < net->num_bts; bts_nr++) - bts_dump_vty(vty, gsm_bts_num(net, bts_nr)); - - return CMD_SUCCESS; -} - -/* utility functions */ -static void parse_e1_link(struct gsm_e1_subslot *e1_link, const char *line, - const char *ts, const char *ss) -{ - e1_link->e1_nr = atoi(line); - e1_link->e1_ts = atoi(ts); - if (!strcmp(ss, "full")) - e1_link->e1_ts_ss = 255; - else - e1_link->e1_ts_ss = atoi(ss); -} - -static void config_write_e1_link(struct vty *vty, struct gsm_e1_subslot *e1_link, - const char *prefix) -{ - if (!e1_link->e1_ts) - return; - - if (e1_link->e1_ts_ss == 255) - vty_out(vty, "%se1 line %u timeslot %u sub-slot full%s", - prefix, e1_link->e1_nr, e1_link->e1_ts, VTY_NEWLINE); - else - vty_out(vty, "%se1 line %u timeslot %u sub-slot %u%s", - prefix, e1_link->e1_nr, e1_link->e1_ts, - e1_link->e1_ts_ss, VTY_NEWLINE); -} - - -static void config_write_ts_single(struct vty *vty, struct gsm_bts_trx_ts *ts) -{ - vty_out(vty, " timeslot %u%s", ts->nr, VTY_NEWLINE); - if (ts->tsc != -1) - vty_out(vty, " training_sequence_code %u%s", ts->tsc, VTY_NEWLINE); - if (ts->pchan != GSM_PCHAN_NONE) - vty_out(vty, " phys_chan_config %s%s", - gsm_pchan_name(ts->pchan), VTY_NEWLINE); - vty_out(vty, " hopping enabled %u%s", - ts->hopping.enabled, VTY_NEWLINE); - if (ts->hopping.enabled) { - unsigned int i; - vty_out(vty, " hopping sequence-number %u%s", - ts->hopping.hsn, VTY_NEWLINE); - vty_out(vty, " hopping maio %u%s", - ts->hopping.maio, VTY_NEWLINE); - for (i = 0; i < ts->hopping.arfcns.data_len*8; i++) { - if (!bitvec_get_bit_pos(&ts->hopping.arfcns, i)) - continue; - vty_out(vty, " hopping arfcn add %u%s", - i, VTY_NEWLINE); - } - } - config_write_e1_link(vty, &ts->e1_link, " "); - - if (ts->trx->bts->model->config_write_ts) - ts->trx->bts->model->config_write_ts(vty, ts); -} - -static void config_write_trx_single(struct vty *vty, struct gsm_bts_trx *trx) -{ - int i; - - vty_out(vty, " trx %u%s", trx->nr, VTY_NEWLINE); - if (trx->description) - vty_out(vty, " description %s%s", trx->description, - VTY_NEWLINE); - vty_out(vty, " rf_locked %u%s", - trx->mo.nm_state.administrative == NM_STATE_LOCKED ? 1 : 0, - VTY_NEWLINE); - vty_out(vty, " arfcn %u%s", trx->arfcn, VTY_NEWLINE); - vty_out(vty, " nominal power %u%s", trx->nominal_power, VTY_NEWLINE); - vty_out(vty, " max_power_red %u%s", trx->max_power_red, VTY_NEWLINE); - config_write_e1_link(vty, &trx->rsl_e1_link, " rsl "); - vty_out(vty, " rsl e1 tei %u%s", trx->rsl_tei, VTY_NEWLINE); - - if (trx->bts->model->config_write_trx) - trx->bts->model->config_write_trx(vty, trx); - - for (i = 0; i < TRX_NR_TS; i++) - config_write_ts_single(vty, &trx->ts[i]); -} - -static void config_write_bts_gprs(struct vty *vty, struct gsm_bts *bts) -{ - unsigned int i; - vty_out(vty, " gprs mode %s%s", bts_gprs_mode_name(bts->gprs.mode), - VTY_NEWLINE); - if (bts->gprs.mode == BTS_GPRS_NONE) - return; - - vty_out(vty, " gprs 11bit_rach_support_for_egprs %u%s", - bts->gprs.supports_egprs_11bit_rach, VTY_NEWLINE); - - vty_out(vty, " gprs routing area %u%s", bts->gprs.rac, - VTY_NEWLINE); - vty_out(vty, " gprs network-control-order nc%u%s", - bts->gprs.net_ctrl_ord, VTY_NEWLINE); - if (!bts->gprs.ctrl_ack_type_use_block) - vty_out(vty, " gprs control-ack-type-rach%s", VTY_NEWLINE); - vty_out(vty, " gprs cell bvci %u%s", bts->gprs.cell.bvci, - VTY_NEWLINE); - for (i = 0; i < ARRAY_SIZE(bts->gprs.cell.timer); i++) - vty_out(vty, " gprs cell timer %s %u%s", - get_value_string(gprs_bssgp_cfg_strs, i), - bts->gprs.cell.timer[i], VTY_NEWLINE); - vty_out(vty, " gprs nsei %u%s", bts->gprs.nse.nsei, - VTY_NEWLINE); - for (i = 0; i < ARRAY_SIZE(bts->gprs.nse.timer); i++) - vty_out(vty, " gprs ns timer %s %u%s", - get_value_string(gprs_ns_timer_strs, i), - bts->gprs.nse.timer[i], VTY_NEWLINE); - for (i = 0; i < ARRAY_SIZE(bts->gprs.nsvc); i++) { - struct gsm_bts_gprs_nsvc *nsvc = - &bts->gprs.nsvc[i]; - struct in_addr ia; - - ia.s_addr = htonl(nsvc->remote_ip); - vty_out(vty, " gprs nsvc %u nsvci %u%s", i, - nsvc->nsvci, VTY_NEWLINE); - vty_out(vty, " gprs nsvc %u local udp port %u%s", i, - nsvc->local_port, VTY_NEWLINE); - vty_out(vty, " gprs nsvc %u remote udp port %u%s", i, - nsvc->remote_port, VTY_NEWLINE); - vty_out(vty, " gprs nsvc %u remote ip %s%s", i, - inet_ntoa(ia), VTY_NEWLINE); - } -} - -/* Write the model data if there is one */ -static void config_write_bts_model(struct vty *vty, struct gsm_bts *bts) -{ - struct gsm_bts_trx *trx; - - if (!bts->model) - return; - - if (bts->model->config_write_bts) - bts->model->config_write_bts(vty, bts); - - llist_for_each_entry(trx, &bts->trx_list, list) - config_write_trx_single(vty, trx); -} - -static void write_amr_modes(struct vty *vty, const char *prefix, - const char *name, struct amr_mode *modes, int num) -{ - int i; - - vty_out(vty, " %s threshold %s", prefix, name); - for (i = 0; i < num - 1; i++) - vty_out(vty, " %d", modes[i].threshold); - vty_out(vty, "%s", VTY_NEWLINE); - vty_out(vty, " %s hysteresis %s", prefix, name); - for (i = 0; i < num - 1; i++) - vty_out(vty, " %d", modes[i].hysteresis); - vty_out(vty, "%s", VTY_NEWLINE); -} - -static void config_write_bts_amr(struct vty *vty, struct gsm_bts *bts, - struct amr_multirate_conf *mr, int full) -{ - struct gsm48_multi_rate_conf *mr_conf; - const char *prefix = (full) ? "amr tch-f" : "amr tch-h"; - int i, num; - - if (!(mr->gsm48_ie[1])) - return; - - mr_conf = (struct gsm48_multi_rate_conf *) mr->gsm48_ie; - - num = 0; - vty_out(vty, " %s modes", prefix); - for (i = 0; i < ((full) ? 8 : 6); i++) { - if ((mr->gsm48_ie[1] & (1 << i))) { - vty_out(vty, " %d", i); - num++; - } - } - vty_out(vty, "%s", VTY_NEWLINE); - if (num > 4) - num = 4; - if (num > 1) { - write_amr_modes(vty, prefix, "ms", mr->ms_mode, num); - write_amr_modes(vty, prefix, "bts", mr->bts_mode, num); - } - vty_out(vty, " %s start-mode ", prefix); - if (mr_conf->icmi) { - num = 0; - for (i = 0; i < ((full) ? 8 : 6) && num < 4; i++) { - if ((mr->gsm48_ie[1] & (1 << i))) - num++; - if (mr_conf->smod == num - 1) { - vty_out(vty, "%d%s", num, VTY_NEWLINE); - break; - } - } - } else - vty_out(vty, "auto%s", VTY_NEWLINE); -} - -static void config_write_bts_single(struct vty *vty, struct gsm_bts *bts) -{ - int i; - uint8_t tmp; - - vty_out(vty, " bts %u%s", bts->nr, VTY_NEWLINE); - vty_out(vty, " type %s%s", btstype2str(bts->type), VTY_NEWLINE); - if (bts->description) - vty_out(vty, " description %s%s", bts->description, VTY_NEWLINE); - vty_out(vty, " band %s%s", gsm_band_name(bts->band), VTY_NEWLINE); - vty_out(vty, " cell_identity %u%s", bts->cell_identity, VTY_NEWLINE); - vty_out(vty, " location_area_code %u%s", bts->location_area_code, - VTY_NEWLINE); - if (bts->dtxu != GSM48_DTX_SHALL_NOT_BE_USED) - vty_out(vty, " dtx uplink%s%s", - (bts->dtxu != GSM48_DTX_SHALL_BE_USED) ? "" : " force", - VTY_NEWLINE); - if (bts->dtxd) - vty_out(vty, " dtx downlink%s", VTY_NEWLINE); - vty_out(vty, " base_station_id_code %u%s", bts->bsic, VTY_NEWLINE); - vty_out(vty, " ms max power %u%s", bts->ms_max_power, VTY_NEWLINE); - vty_out(vty, " cell reselection hysteresis %u%s", - bts->si_common.cell_sel_par.cell_resel_hyst*2, VTY_NEWLINE); - vty_out(vty, " rxlev access min %u%s", - bts->si_common.cell_sel_par.rxlev_acc_min, VTY_NEWLINE); - - if (bts->si_common.cell_ro_sel_par.present) { - struct gsm48_si_selection_params *sp; - sp = &bts->si_common.cell_ro_sel_par; - - if (sp->cbq) - vty_out(vty, " cell bar qualify %u%s", - sp->cbq, VTY_NEWLINE); - - if (sp->cell_resel_off) - vty_out(vty, " cell reselection offset %u%s", - sp->cell_resel_off*2, VTY_NEWLINE); - - if (sp->temp_offs == 7) - vty_out(vty, " temporary offset infinite%s", - VTY_NEWLINE); - else if (sp->temp_offs) - vty_out(vty, " temporary offset %u%s", - sp->temp_offs*10, VTY_NEWLINE); - - if (sp->penalty_time == 31) - vty_out(vty, " penalty time reserved%s", - VTY_NEWLINE); - else if (sp->penalty_time) - vty_out(vty, " penalty time %u%s", - (sp->penalty_time*20)+20, VTY_NEWLINE); - } - - /* Is periodic LU enabled or disabled? */ - if (bts->si_common.chan_desc.t3212 == 0) - vty_out(vty, " no periodic location update%s", VTY_NEWLINE); - else - vty_out(vty, " periodic location update %u%s", - bts->si_common.chan_desc.t3212 * 6, VTY_NEWLINE); - - if (gsm_bts_get_radio_link_timeout(bts) < 0) - vty_out(vty, " radio-link-timeout infinite%s", VTY_NEWLINE); - else - vty_out(vty, " radio-link-timeout %d%s", - gsm_bts_get_radio_link_timeout(bts), VTY_NEWLINE); - vty_out(vty, " channel allocator %s%s", - bts->chan_alloc_reverse ? "descending" : "ascending", - VTY_NEWLINE); - vty_out(vty, " rach tx integer %u%s", - bts->si_common.rach_control.tx_integer, VTY_NEWLINE); - vty_out(vty, " rach max transmission %u%s", - rach_max_trans_raw2val(bts->si_common.rach_control.max_trans), - VTY_NEWLINE); - - vty_out(vty, " channel-descrption attach %u%s", - bts->si_common.chan_desc.att, VTY_NEWLINE); - vty_out(vty, " channel-descrption bs-pa-mfrms %u%s", - bts->si_common.chan_desc.bs_pa_mfrms + 2, VTY_NEWLINE); - vty_out(vty, " channel-descrption bs-ag-blks-res %u%s", - bts->si_common.chan_desc.bs_ag_blks_res, VTY_NEWLINE); - - if (bts->rach_b_thresh != -1) - vty_out(vty, " rach nm busy threshold %u%s", - bts->rach_b_thresh, VTY_NEWLINE); - if (bts->rach_ldavg_slots != -1) - vty_out(vty, " rach nm load average %u%s", - bts->rach_ldavg_slots, VTY_NEWLINE); - if (bts->si_common.rach_control.cell_bar) - vty_out(vty, " cell barred 1%s", VTY_NEWLINE); - if ((bts->si_common.rach_control.t2 & 0x4) == 0) - vty_out(vty, " rach emergency call allowed 1%s", VTY_NEWLINE); - if ((bts->si_common.rach_control.t3) != 0) - for (i = 0; i < 8; i++) - if (bts->si_common.rach_control.t3 & (0x1 << i)) - vty_out(vty, " rach access-control-class %d barred%s", i, VTY_NEWLINE); - if ((bts->si_common.rach_control.t2 & 0xfb) != 0) - for (i = 0; i < 8; i++) - if ((i != 2) && (bts->si_common.rach_control.t2 & (0x1 << i))) - vty_out(vty, " rach access-control-class %d barred%s", i+8, VTY_NEWLINE); - for (i = SYSINFO_TYPE_1; i < _MAX_SYSINFO_TYPE; i++) { - if (bts->si_mode_static & (1 << i)) { - vty_out(vty, " system-information %s mode static%s", - get_value_string(osmo_sitype_strs, i), VTY_NEWLINE); - vty_out(vty, " system-information %s static %s%s", - get_value_string(osmo_sitype_strs, i), - osmo_hexdump_nospc(GSM_BTS_SI(bts, i), GSM_MACBLOCK_LEN), - VTY_NEWLINE); - } - } - vty_out(vty, " early-classmark-sending %s%s", - bts->early_classmark_allowed ? "allowed" : "forbidden", VTY_NEWLINE); - switch (bts->type) { - case GSM_BTS_TYPE_NANOBTS: - case GSM_BTS_TYPE_OSMOBTS: - vty_out(vty, " ip.access unit_id %u %u%s", - bts->ip_access.site_id, bts->ip_access.bts_id, VTY_NEWLINE); - if (bts->ip_access.rsl_ip) { - struct in_addr ia; - ia.s_addr = htonl(bts->ip_access.rsl_ip); - vty_out(vty, " ip.access rsl-ip %s%s", inet_ntoa(ia), - VTY_NEWLINE); - } - vty_out(vty, " oml ip.access stream_id %u line %u%s", - bts->oml_tei, bts->oml_e1_link.e1_nr, VTY_NEWLINE); - break; - case GSM_BTS_TYPE_NOKIA_SITE: - vty_out(vty, " nokia_site skip-reset %d%s", bts->nokia.skip_reset, VTY_NEWLINE); - vty_out(vty, " nokia_site no-local-rel-conf %d%s", - bts->nokia.no_loc_rel_cnf, VTY_NEWLINE); - vty_out(vty, " nokia_site bts-reset-timer %d%s", bts->nokia.bts_reset_timer_cnf, VTY_NEWLINE); - /* fall through: Nokia requires "oml e1" parameters also */ - default: - config_write_e1_link(vty, &bts->oml_e1_link, " oml "); - vty_out(vty, " oml e1 tei %u%s", bts->oml_tei, VTY_NEWLINE); - break; - } - - /* if we have a limit, write it */ - if (bts->paging.free_chans_need >= 0) - vty_out(vty, " paging free %d%s", bts->paging.free_chans_need, VTY_NEWLINE); - - vty_out(vty, " neighbor-list mode %s%s", - get_value_string(bts_neigh_mode_strs, bts->neigh_list_manual_mode), VTY_NEWLINE); - if (bts->neigh_list_manual_mode != NL_MODE_AUTOMATIC) { - for (i = 0; i < 1024; i++) { - if (bitvec_get_bit_pos(&bts->si_common.neigh_list, i)) - vty_out(vty, " neighbor-list add arfcn %u%s", - i, VTY_NEWLINE); - } - } - if (bts->neigh_list_manual_mode == NL_MODE_MANUAL_SI5SEP) { - for (i = 0; i < 1024; i++) { - if (bitvec_get_bit_pos(&bts->si_common.si5_neigh_list, i)) - vty_out(vty, " si5 neighbor-list add arfcn %u%s", - i, VTY_NEWLINE); - } - } - - for (i = 0; i < MAX_EARFCN_LIST; i++) { - struct osmo_earfcn_si2q *e = &bts->si_common.si2quater_neigh_list; - if (e->arfcn[i] != OSMO_EARFCN_INVALID) { - vty_out(vty, " si2quater neighbor-list add earfcn %u " - "thresh-hi %u", e->arfcn[i], e->thresh_hi); - - vty_out(vty, " thresh-lo %u", - e->thresh_lo_valid ? e->thresh_lo : 32); - - vty_out(vty, " prio %u", - e->prio_valid ? e->prio : 8); - - vty_out(vty, " qrxlv %u", - e->qrxlm_valid ? e->qrxlm : 32); - - tmp = e->meas_bw[i]; - vty_out(vty, " meas %u", - (tmp != OSMO_EARFCN_MEAS_INVALID) ? tmp : 8); - - vty_out(vty, "%s", VTY_NEWLINE); - } - } - - for (i = 0; i < bts->si_common.uarfcn_length; i++) { - vty_out(vty, " si2quater neighbor-list add uarfcn %u %u %u%s", - bts->si_common.data.uarfcn_list[i], - bts->si_common.data.scramble_list[i] & ~(1 << 9), - (bts->si_common.data.scramble_list[i] >> 9) & 1, - VTY_NEWLINE); - } - - vty_out(vty, " codec-support fr"); - if (bts->codec.hr) - vty_out(vty, " hr"); - if (bts->codec.efr) - vty_out(vty, " efr"); - if (bts->codec.amr) - vty_out(vty, " amr"); - vty_out(vty, "%s", VTY_NEWLINE); - - config_write_bts_amr(vty, bts, &bts->mr_full, 1); - config_write_bts_amr(vty, bts, &bts->mr_half, 0); - - config_write_bts_gprs(vty, bts); - - if (bts->excl_from_rf_lock) - vty_out(vty, " rf-lock-exclude%s", VTY_NEWLINE); - - vty_out(vty, " %sforce-combined-si%s", - bts->force_combined_si ? "" : "no ", VTY_NEWLINE); - - for (i = 0; i < ARRAY_SIZE(bts->depends_on); ++i) { - int j; - - if (bts->depends_on[i] == 0) - continue; - - for (j = 0; j < sizeof(bts->depends_on[i]) * 8; ++j) { - int bts_nr; - - if ((bts->depends_on[i] & (1<depends_on[i]) * 8) + j; - vty_out(vty, " depends-on-bts %d%s", bts_nr, VTY_NEWLINE); - } - } - if (bts->pcu_sock_path) - vty_out(vty, " pcu-socket %s%s", bts->pcu_sock_path, VTY_NEWLINE); - - config_write_bts_model(vty, bts); -} - -static int config_write_bts(struct vty *v) -{ - struct gsm_network *gsmnet = gsmnet_from_vty(v); - struct gsm_bts *bts; - - llist_for_each_entry(bts, &gsmnet->bts_list, list) - config_write_bts_single(v, bts); - - return CMD_SUCCESS; -} - -static int config_write_net(struct vty *vty) -{ - struct gsm_network *gsmnet = gsmnet_from_vty(vty); - - vty_out(vty, "network%s", VTY_NEWLINE); - vty_out(vty, " network country code %u%s", gsmnet->country_code, VTY_NEWLINE); - vty_out(vty, " mobile network code %u%s", gsmnet->network_code, VTY_NEWLINE); - vty_out(vty, " short name %s%s", gsmnet->name_short, VTY_NEWLINE); - vty_out(vty, " long name %s%s", gsmnet->name_long, VTY_NEWLINE); - vty_out(vty, " auth policy %s%s", gsm_auth_policy_name(gsmnet->auth_policy), VTY_NEWLINE); - if (gsmnet->authorized_reg_str) - vty_out(vty, " authorized-regexp %s%s", gsmnet->authorized_reg_str, VTY_NEWLINE); - vty_out(vty, " location updating reject cause %u%s", - gsmnet->reject_cause, VTY_NEWLINE); - vty_out(vty, " encryption a5 %u%s", gsmnet->a5_encryption, VTY_NEWLINE); - vty_out(vty, " neci %u%s", gsmnet->neci, VTY_NEWLINE); - vty_out(vty, " paging any use tch %d%s", gsmnet->pag_any_tch, VTY_NEWLINE); - vty_out(vty, " rrlp mode %s%s", rrlp_mode_name(gsmnet->rrlp.mode), - VTY_NEWLINE); - vty_out(vty, " mm info %u%s", gsmnet->send_mm_info, VTY_NEWLINE); - vty_out(vty, " handover %u%s", gsmnet->handover.active, VTY_NEWLINE); - vty_out(vty, " handover window rxlev averaging %u%s", - gsmnet->handover.win_rxlev_avg, VTY_NEWLINE); - vty_out(vty, " handover window rxqual averaging %u%s", - gsmnet->handover.win_rxqual_avg, VTY_NEWLINE); - vty_out(vty, " handover window rxlev neighbor averaging %u%s", - gsmnet->handover.win_rxlev_avg_neigh, VTY_NEWLINE); - vty_out(vty, " handover power budget interval %u%s", - gsmnet->handover.pwr_interval, VTY_NEWLINE); - vty_out(vty, " handover power budget hysteresis %u%s", - gsmnet->handover.pwr_hysteresis, VTY_NEWLINE); - vty_out(vty, " handover maximum distance %u%s", - gsmnet->handover.max_distance, VTY_NEWLINE); - vty_out(vty, " timer t3101 %u%s", gsmnet->T3101, VTY_NEWLINE); - vty_out(vty, " timer t3103 %u%s", gsmnet->T3103, VTY_NEWLINE); - vty_out(vty, " timer t3105 %u%s", gsmnet->T3105, VTY_NEWLINE); - vty_out(vty, " timer t3107 %u%s", gsmnet->T3107, VTY_NEWLINE); - vty_out(vty, " timer t3109 %u%s", gsmnet->T3109, VTY_NEWLINE); - vty_out(vty, " timer t3111 %u%s", gsmnet->T3111, VTY_NEWLINE); - vty_out(vty, " timer t3113 %u%s", gsmnet->T3113, VTY_NEWLINE); - vty_out(vty, " timer t3115 %u%s", gsmnet->T3115, VTY_NEWLINE); - vty_out(vty, " timer t3117 %u%s", gsmnet->T3117, VTY_NEWLINE); - vty_out(vty, " timer t3119 %u%s", gsmnet->T3119, VTY_NEWLINE); - vty_out(vty, " timer t3122 %u%s", gsmnet->T3122, VTY_NEWLINE); - vty_out(vty, " timer t3141 %u%s", gsmnet->T3141, VTY_NEWLINE); - vty_out(vty, " dyn_ts_allow_tch_f %d%s", - gsmnet->dyn_ts_allow_tch_f ? 1 : 0, VTY_NEWLINE); - vty_out(vty, " subscriber-keep-in-ram %d%s", - gsmnet->subscr_group->keep_subscr, VTY_NEWLINE); - if (gsmnet->tz.override != 0) { - if (gsmnet->tz.dst) - vty_out(vty, " timezone %d %d %d%s", - gsmnet->tz.hr, gsmnet->tz.mn, gsmnet->tz.dst, - VTY_NEWLINE); - else - vty_out(vty, " timezone %d %d%s", - gsmnet->tz.hr, gsmnet->tz.mn, VTY_NEWLINE); - } - - return CMD_SUCCESS; -} - -static void trx_dump_vty(struct vty *vty, struct gsm_bts_trx *trx) -{ - vty_out(vty, "TRX %u of BTS %u is on ARFCN %u%s", - trx->nr, trx->bts->nr, trx->arfcn, VTY_NEWLINE); - vty_out(vty, "Description: %s%s", - trx->description ? trx->description : "(null)", VTY_NEWLINE); - vty_out(vty, " RF Nominal Power: %d dBm, reduced by %u dB, " - "resulting BS power: %d dBm%s", - trx->nominal_power, trx->max_power_red, - trx->nominal_power - trx->max_power_red, VTY_NEWLINE); - vty_out(vty, " NM State: "); - net_dump_nmstate(vty, &trx->mo.nm_state); - vty_out(vty, " Baseband Transceiver NM State: "); - net_dump_nmstate(vty, &trx->bb_transc.mo.nm_state); - if (is_ipaccess_bts(trx->bts)) { - vty_out(vty, " ip.access stream ID: 0x%02x%s", - trx->rsl_tei, VTY_NEWLINE); - } else { - vty_out(vty, " E1 Signalling Link:%s", VTY_NEWLINE); - e1isl_dump_vty(vty, trx->rsl_link); - } -} - -DEFUN(show_trx, - show_trx_cmd, - "show trx [<0-255>] [<0-255>]", - SHOW_STR "Display information about a TRX\n" - "BTS Number\n" - "TRX Number\n") -{ - struct gsm_network *net = gsmnet_from_vty(vty); - struct gsm_bts *bts = NULL; - struct gsm_bts_trx *trx; - int bts_nr, trx_nr; - - if (argc >= 1) { - /* use the BTS number that the user has specified */ - bts_nr = atoi(argv[0]); - if (bts_nr >= net->num_bts) { - vty_out(vty, "%% can't find BTS '%s'%s", argv[0], - VTY_NEWLINE); - return CMD_WARNING; - } - bts = gsm_bts_num(net, bts_nr); - } - if (argc >= 2) { - trx_nr = atoi(argv[1]); - if (trx_nr >= bts->num_trx) { - vty_out(vty, "%% can't find TRX '%s'%s", argv[1], - VTY_NEWLINE); - return CMD_WARNING; - } - trx = gsm_bts_trx_num(bts, trx_nr); - trx_dump_vty(vty, trx); - return CMD_SUCCESS; - } - if (bts) { - /* print all TRX in this BTS */ - for (trx_nr = 0; trx_nr < bts->num_trx; trx_nr++) { - trx = gsm_bts_trx_num(bts, trx_nr); - trx_dump_vty(vty, trx); - } - return CMD_SUCCESS; - } - - for (bts_nr = 0; bts_nr < net->num_bts; bts_nr++) { - bts = gsm_bts_num(net, bts_nr); - for (trx_nr = 0; trx_nr < bts->num_trx; trx_nr++) { - trx = gsm_bts_trx_num(bts, trx_nr); - trx_dump_vty(vty, trx); - } - } - - return CMD_SUCCESS; -} - - -static void ts_dump_vty(struct vty *vty, struct gsm_bts_trx_ts *ts) -{ - vty_out(vty, "BTS %u, TRX %u, Timeslot %u, phys cfg %s, TSC %u", - ts->trx->bts->nr, ts->trx->nr, ts->nr, - gsm_pchan_name(ts->pchan), gsm_ts_tsc(ts)); - if (ts->pchan == GSM_PCHAN_TCH_F_PDCH) - vty_out(vty, " (%s mode)", - ts->flags & TS_F_PDCH_ACTIVE ? "PDCH" : "TCH/F"); - vty_out(vty, "%s", VTY_NEWLINE); - vty_out(vty, " NM State: "); - net_dump_nmstate(vty, &ts->mo.nm_state); - if (!is_ipaccess_bts(ts->trx->bts)) - vty_out(vty, " E1 Line %u, Timeslot %u, Subslot %u%s", - ts->e1_link.e1_nr, ts->e1_link.e1_ts, - ts->e1_link.e1_ts_ss, VTY_NEWLINE); -} - -DEFUN(show_ts, - show_ts_cmd, - "show timeslot [<0-255>] [<0-255>] [<0-7>]", - SHOW_STR "Display information about a TS\n" - "BTS Number\n" "TRX Number\n" "Timeslot Number\n") -{ - struct gsm_network *net = gsmnet_from_vty(vty); - struct gsm_bts *bts = NULL; - struct gsm_bts_trx *trx = NULL; - struct gsm_bts_trx_ts *ts = NULL; - int bts_nr, trx_nr, ts_nr; - - if (argc >= 1) { - /* use the BTS number that the user has specified */ - bts_nr = atoi(argv[0]); - if (bts_nr >= net->num_bts) { - vty_out(vty, "%% can't find BTS '%s'%s", argv[0], - VTY_NEWLINE); - return CMD_WARNING; - } - bts = gsm_bts_num(net, bts_nr); - } - if (argc >= 2) { - trx_nr = atoi(argv[1]); - if (trx_nr >= bts->num_trx) { - vty_out(vty, "%% can't find TRX '%s'%s", argv[1], - VTY_NEWLINE); - return CMD_WARNING; - } - trx = gsm_bts_trx_num(bts, trx_nr); - } - if (argc >= 3) { - ts_nr = atoi(argv[2]); - if (ts_nr >= TRX_NR_TS) { - vty_out(vty, "%% can't find TS '%s'%s", argv[2], - VTY_NEWLINE); - return CMD_WARNING; - } - /* Fully Specified: print and exit */ - ts = &trx->ts[ts_nr]; - ts_dump_vty(vty, ts); - return CMD_SUCCESS; - } - - if (bts && trx) { - /* Iterate over all TS in this TRX */ - for (ts_nr = 0; ts_nr < TRX_NR_TS; ts_nr++) { - ts = &trx->ts[ts_nr]; - ts_dump_vty(vty, ts); - } - } else if (bts) { - /* Iterate over all TRX in this BTS, TS in each TRX */ - for (trx_nr = 0; trx_nr < bts->num_trx; trx_nr++) { - trx = gsm_bts_trx_num(bts, trx_nr); - for (ts_nr = 0; ts_nr < TRX_NR_TS; ts_nr++) { - ts = &trx->ts[ts_nr]; - ts_dump_vty(vty, ts); - } - } - } else { - /* Iterate over all BTS, TRX in each BTS, TS in each TRX */ - for (bts_nr = 0; bts_nr < net->num_bts; bts_nr++) { - bts = gsm_bts_num(net, bts_nr); - for (trx_nr = 0; trx_nr < bts->num_trx; trx_nr++) { - trx = gsm_bts_trx_num(bts, trx_nr); - for (ts_nr = 0; ts_nr < TRX_NR_TS; ts_nr++) { - ts = &trx->ts[ts_nr]; - ts_dump_vty(vty, ts); - } - } - } - } - - return CMD_SUCCESS; -} - -static void subscr_dump_vty(struct vty *vty, struct gsm_subscriber *subscr) -{ - vty_out(vty, " ID: %llu, Authorized: %d%s", subscr->id, - subscr->authorized, VTY_NEWLINE); - if (strlen(subscr->name)) - vty_out(vty, " Name: '%s'%s", subscr->name, VTY_NEWLINE); - if (strlen(subscr->extension)) - vty_out(vty, " Extension: %s%s", subscr->extension, - VTY_NEWLINE); - vty_out(vty, " IMSI: %s%s", subscr->imsi, VTY_NEWLINE); - if (subscr->tmsi != GSM_RESERVED_TMSI) - vty_out(vty, " TMSI: %08X%s", subscr->tmsi, - VTY_NEWLINE); - - vty_out(vty, " Use count: %u%s", subscr->use_count, VTY_NEWLINE); -} - -static void bsc_subscr_dump_vty(struct vty *vty, struct bsc_subscr *bsub) -{ - if (strlen(bsub->imsi)) - vty_out(vty, " IMSI: %s%s", bsub->imsi, VTY_NEWLINE); - if (bsub->tmsi != GSM_RESERVED_TMSI) - vty_out(vty, " TMSI: 0x%08x%s", bsub->tmsi, - VTY_NEWLINE); - vty_out(vty, " Use count: %d%s", bsub->use_count, VTY_NEWLINE); -} - -static void meas_rep_dump_uni_vty(struct vty *vty, - struct gsm_meas_rep_unidir *mru, - const char *prefix, - const char *dir) -{ - vty_out(vty, "%s RXL-FULL-%s: %4d dBm, RXL-SUB-%s: %4d dBm ", - prefix, dir, rxlev2dbm(mru->full.rx_lev), - dir, rxlev2dbm(mru->sub.rx_lev)); - vty_out(vty, "RXQ-FULL-%s: %d, RXQ-SUB-%s: %d%s", - dir, mru->full.rx_qual, dir, mru->sub.rx_qual, - VTY_NEWLINE); -} - -static void meas_rep_dump_vty(struct vty *vty, struct gsm_meas_rep *mr, - const char *prefix) -{ - vty_out(vty, "%sMeasurement Report:%s", prefix, VTY_NEWLINE); - vty_out(vty, "%s Flags: %s%s%s%s%s", prefix, - mr->flags & MEAS_REP_F_UL_DTX ? "DTXu " : "", - mr->flags & MEAS_REP_F_DL_DTX ? "DTXd " : "", - mr->flags & MEAS_REP_F_FPC ? "FPC " : "", - mr->flags & MEAS_REP_F_DL_VALID ? " " : "DLinval ", - VTY_NEWLINE); - if (mr->flags & MEAS_REP_F_MS_TO) - vty_out(vty, "%s MS Timing Offset: %d%s", prefix, mr->ms_timing_offset, VTY_NEWLINE); - if (mr->flags & MEAS_REP_F_MS_L1) - vty_out(vty, "%s L1 MS Power: %u dBm, Timing Advance: %u%s", - prefix, mr->ms_l1.pwr, mr->ms_l1.ta, VTY_NEWLINE); - if (mr->flags & MEAS_REP_F_DL_VALID) - meas_rep_dump_uni_vty(vty, &mr->dl, prefix, "dl"); - meas_rep_dump_uni_vty(vty, &mr->ul, prefix, "ul"); -} - -/* FIXME: move this to libosmogsm */ -static const struct value_string gsm48_cmode_names[] = { - { GSM48_CMODE_SIGN, "signalling" }, - { GSM48_CMODE_SPEECH_V1, "FR or HR" }, - { GSM48_CMODE_SPEECH_EFR, "EFR" }, - { GSM48_CMODE_SPEECH_AMR, "AMR" }, - { GSM48_CMODE_DATA_14k5, "CSD(14k5)" }, - { GSM48_CMODE_DATA_12k0, "CSD(12k0)" }, - { GSM48_CMODE_DATA_6k0, "CSD(6k0)" }, - { GSM48_CMODE_DATA_3k6, "CSD(3k6)" }, - { 0, NULL } -}; - -/* call vty_out() to print a string like " as TCH/H" for dynamic timeslots. - * Don't do anything if the ts is not dynamic. */ -static void vty_out_dyn_ts_status(struct vty *vty, struct gsm_bts_trx_ts *ts) -{ - switch (ts->pchan) { - case GSM_PCHAN_TCH_F_TCH_H_PDCH: - if (ts->dyn.pchan_is == ts->dyn.pchan_want) - vty_out(vty, " as %s", - gsm_pchan_name(ts->dyn.pchan_is)); - else - vty_out(vty, " switching %s -> %s", - gsm_pchan_name(ts->dyn.pchan_is), - gsm_pchan_name(ts->dyn.pchan_want)); - break; - case GSM_PCHAN_TCH_F_PDCH: - if ((ts->flags & TS_F_PDCH_PENDING_MASK) == 0) - vty_out(vty, " as %s", - (ts->flags & TS_F_PDCH_ACTIVE)? "PDCH" - : "TCH/F"); - else - vty_out(vty, " switching %s -> %s", - (ts->flags & TS_F_PDCH_ACTIVE)? "PDCH" - : "TCH/F", - (ts->flags & TS_F_PDCH_ACT_PENDING)? "PDCH" - : "TCH/F"); - break; - default: - /* no dyn ts */ - break; - } -} - -static void lchan_dump_full_vty(struct vty *vty, struct gsm_lchan *lchan) -{ - int idx; - - vty_out(vty, "BTS %u, TRX %u, Timeslot %u, Lchan %u: Type %s%s", - lchan->ts->trx->bts->nr, lchan->ts->trx->nr, lchan->ts->nr, - lchan->nr, gsm_lchant_name(lchan->type), VTY_NEWLINE); - /* show dyn TS details, if applicable */ - switch (lchan->ts->pchan) { - case GSM_PCHAN_TCH_F_TCH_H_PDCH: - vty_out(vty, " Osmocom Dyn TS:"); - vty_out_dyn_ts_status(vty, lchan->ts); - vty_out(vty, VTY_NEWLINE); - break; - case GSM_PCHAN_TCH_F_PDCH: - vty_out(vty, " IPACC Dyn PDCH TS:"); - vty_out_dyn_ts_status(vty, lchan->ts); - vty_out(vty, VTY_NEWLINE); - break; - default: - /* no dyn ts */ - break; - } - vty_out(vty, " Connection: %u, State: %s%s%s%s", - lchan->conn ? 1: 0, - gsm_lchans_name(lchan->state), - lchan->state == LCHAN_S_BROKEN ? " Error reason: " : "", - lchan->state == LCHAN_S_BROKEN ? lchan->broken_reason : "", - VTY_NEWLINE); - vty_out(vty, " BS Power: %u dBm, MS Power: %u dBm%s", - lchan->ts->trx->nominal_power - lchan->ts->trx->max_power_red - - lchan->bs_power*2, - ms_pwr_dbm(lchan->ts->trx->bts->band, lchan->ms_power), - VTY_NEWLINE); - vty_out(vty, " Channel Mode / Codec: %s%s", - get_value_string(gsm48_cmode_names, lchan->tch_mode), - VTY_NEWLINE); - if (lchan->conn && lchan->conn->subscr) { - vty_out(vty, " Subscriber:%s", VTY_NEWLINE); - subscr_dump_vty(vty, lchan->conn->subscr); - } else - vty_out(vty, " No Subscriber%s", VTY_NEWLINE); - if (is_ipaccess_bts(lchan->ts->trx->bts)) { - struct in_addr ia; - ia.s_addr = htonl(lchan->abis_ip.bound_ip); - vty_out(vty, " Bound IP: %s Port %u RTP_TYPE2=%u CONN_ID=%u%s", - inet_ntoa(ia), lchan->abis_ip.bound_port, - lchan->abis_ip.rtp_payload2, lchan->abis_ip.conn_id, - VTY_NEWLINE); - } - - /* we want to report the last measurement report */ - idx = calc_initial_idx(ARRAY_SIZE(lchan->meas_rep), - lchan->meas_rep_idx, 1); - meas_rep_dump_vty(vty, &lchan->meas_rep[idx], " "); -} - -static void lchan_dump_short_vty(struct vty *vty, struct gsm_lchan *lchan) -{ - struct gsm_meas_rep *mr; - int idx; - - /* we want to report the last measurement report */ - idx = calc_initial_idx(ARRAY_SIZE(lchan->meas_rep), - lchan->meas_rep_idx, 1); - mr = &lchan->meas_rep[idx]; - - vty_out(vty, "BTS %u, TRX %u, Timeslot %u %s", - lchan->ts->trx->bts->nr, lchan->ts->trx->nr, lchan->ts->nr, - gsm_pchan_name(lchan->ts->pchan)); - vty_out_dyn_ts_status(vty, lchan->ts); - vty_out(vty, ", Lchan %u, Type %s, State %s - " - "L1 MS Power: %u dBm RXL-FULL-dl: %4d dBm RXL-FULL-ul: %4d dBm%s", - lchan->nr, - gsm_lchant_name(lchan->type), gsm_lchans_name(lchan->state), - mr->ms_l1.pwr, - rxlev2dbm(mr->dl.full.rx_lev), - rxlev2dbm(mr->ul.full.rx_lev), - VTY_NEWLINE); -} - - -static int dump_lchan_trx_ts(struct gsm_bts_trx_ts *ts, struct vty *vty, - void (*dump_cb)(struct vty *, struct gsm_lchan *)) -{ - int lchan_nr; - for (lchan_nr = 0; lchan_nr < TS_MAX_LCHAN; lchan_nr++) { - struct gsm_lchan *lchan = &ts->lchan[lchan_nr]; - if ((lchan->type == GSM_LCHAN_NONE) && (lchan->state == LCHAN_S_NONE)) - continue; - dump_cb(vty, lchan); - } - - return CMD_SUCCESS; -} - -static int dump_lchan_trx(struct gsm_bts_trx *trx, struct vty *vty, - void (*dump_cb)(struct vty *, struct gsm_lchan *)) -{ - int ts_nr; - - for (ts_nr = 0; ts_nr < TRX_NR_TS; ts_nr++) { - struct gsm_bts_trx_ts *ts = &trx->ts[ts_nr]; - dump_lchan_trx_ts(ts, vty, dump_cb); - } - - return CMD_SUCCESS; -} - -static int dump_lchan_bts(struct gsm_bts *bts, struct vty *vty, - void (*dump_cb)(struct vty *, struct gsm_lchan *)) -{ - int trx_nr; - - for (trx_nr = 0; trx_nr < bts->num_trx; trx_nr++) { - struct gsm_bts_trx *trx = gsm_bts_trx_num(bts, trx_nr); - dump_lchan_trx(trx, vty, dump_cb); - } - - return CMD_SUCCESS; -} - -static int lchan_summary(struct vty *vty, int argc, const char **argv, - void (*dump_cb)(struct vty *, struct gsm_lchan *)) -{ - struct gsm_network *net = gsmnet_from_vty(vty); - struct gsm_bts *bts; - struct gsm_bts_trx *trx; - struct gsm_bts_trx_ts *ts; - struct gsm_lchan *lchan; - int bts_nr, trx_nr, ts_nr, lchan_nr; - - if (argc >= 1) { - /* use the BTS number that the user has specified */ - bts_nr = atoi(argv[0]); - if (bts_nr >= net->num_bts) { - vty_out(vty, "%% can't find BTS %s%s", argv[0], - VTY_NEWLINE); - return CMD_WARNING; - } - bts = gsm_bts_num(net, bts_nr); - - if (argc == 1) - return dump_lchan_bts(bts, vty, dump_cb); - } - if (argc >= 2) { - trx_nr = atoi(argv[1]); - if (trx_nr >= bts->num_trx) { - vty_out(vty, "%% can't find TRX %s%s", argv[1], - VTY_NEWLINE); - return CMD_WARNING; - } - trx = gsm_bts_trx_num(bts, trx_nr); - - if (argc == 2) - return dump_lchan_trx(trx, vty, dump_cb); - } - if (argc >= 3) { - ts_nr = atoi(argv[2]); - if (ts_nr >= TRX_NR_TS) { - vty_out(vty, "%% can't find TS %s%s", argv[2], - VTY_NEWLINE); - return CMD_WARNING; - } - ts = &trx->ts[ts_nr]; - - if (argc == 3) - return dump_lchan_trx_ts(ts, vty, dump_cb); - } - if (argc >= 4) { - lchan_nr = atoi(argv[3]); - if (lchan_nr >= TS_MAX_LCHAN) { - vty_out(vty, "%% can't find LCHAN %s%s", argv[3], - VTY_NEWLINE); - return CMD_WARNING; - } - lchan = &ts->lchan[lchan_nr]; - dump_cb(vty, lchan); - return CMD_SUCCESS; - } - - - for (bts_nr = 0; bts_nr < net->num_bts; bts_nr++) { - bts = gsm_bts_num(net, bts_nr); - dump_lchan_bts(bts, vty, dump_cb); - } - - return CMD_SUCCESS; -} - - -DEFUN(show_lchan, - show_lchan_cmd, - "show lchan [<0-255>] [<0-255>] [<0-7>] [lchan_nr]", - SHOW_STR "Display information about a logical channel\n" - "BTS Number\n" "TRX Number\n" "Timeslot Number\n" - LCHAN_NR_STR) - -{ - return lchan_summary(vty, argc, argv, lchan_dump_full_vty); -} - -DEFUN(show_lchan_summary, - show_lchan_summary_cmd, - "show lchan summary [<0-255>] [<0-255>] [<0-7>] [lchan_nr]", - SHOW_STR "Display information about a logical channel\n" - "Short summary\n" - "BTS Number\n" "TRX Number\n" "Timeslot Number\n" - LCHAN_NR_STR) -{ - return lchan_summary(vty, argc, argv, lchan_dump_short_vty); -} - -static void paging_dump_vty(struct vty *vty, struct gsm_paging_request *pag) -{ - vty_out(vty, "Paging on BTS %u%s", pag->bts->nr, VTY_NEWLINE); - bsc_subscr_dump_vty(vty, pag->bsub); -} - -static void bts_paging_dump_vty(struct vty *vty, struct gsm_bts *bts) -{ - struct gsm_paging_request *pag; - - if (!bts->paging.bts) - return; - - llist_for_each_entry(pag, &bts->paging.pending_requests, entry) - paging_dump_vty(vty, pag); -} - -DEFUN(show_paging, - show_paging_cmd, - "show paging [<0-255>]", - SHOW_STR "Display information about paging reuqests of a BTS\n" - "BTS Number\n") -{ - struct gsm_network *net = gsmnet_from_vty(vty); - struct gsm_bts *bts; - int bts_nr; - - if (argc >= 1) { - /* use the BTS number that the user has specified */ - bts_nr = atoi(argv[0]); - if (bts_nr >= net->num_bts) { - vty_out(vty, "%% can't find BTS %s%s", argv[0], - VTY_NEWLINE); - return CMD_WARNING; - } - bts = gsm_bts_num(net, bts_nr); - bts_paging_dump_vty(vty, bts); - - return CMD_SUCCESS; - } - for (bts_nr = 0; bts_nr < net->num_bts; bts_nr++) { - bts = gsm_bts_num(net, bts_nr); - bts_paging_dump_vty(vty, bts); - } - - return CMD_SUCCESS; -} - -DEFUN(show_paging_group, - show_paging_group_cmd, - "show paging-group <0-255> IMSI", - SHOW_STR "Display the paging group\n" - "BTS Number\n" "IMSI\n") -{ - struct gsm_network *net = gsmnet_from_vty(vty); - struct gsm_bts *bts; - unsigned int page_group; - int bts_nr = atoi(argv[0]); - - if (bts_nr >= net->num_bts) { - vty_out(vty, "%% can't find BTS %s%s", argv[0], VTY_NEWLINE); - return CMD_WARNING; - } - - bts = gsm_bts_num(net, bts_nr); - if (!bts) { - vty_out(vty, "%% can't find BTS %s%s", argv[0], VTY_NEWLINE); - return CMD_WARNING; - } - - page_group = gsm0502_calc_paging_group(&bts->si_common.chan_desc, - str_to_imsi(argv[1])); - vty_out(vty, "%%Paging group for IMSI %" PRIu64 " on BTS #%d is %u%s", - str_to_imsi(argv[1]), bts->nr, - page_group, VTY_NEWLINE); - return CMD_SUCCESS; -} - -DEFUN(cfg_net_neci, - cfg_net_neci_cmd, - "neci (0|1)", - "New Establish Cause Indication\n" - "Don't set the NECI bit\n" "Set the NECI bit\n") -{ - struct gsm_network *gsmnet = gsmnet_from_vty(vty); - - gsmnet->neci = atoi(argv[0]); - gsm_net_update_ctype(gsmnet); - return CMD_SUCCESS; -} - -#define HANDOVER_STR "Handover Options\n" - -DEFUN(cfg_net_handover, cfg_net_handover_cmd, - "handover (0|1)", - HANDOVER_STR - "Don't perform in-call handover\n" - "Perform in-call handover\n") -{ - int enable = atoi(argv[0]); - struct gsm_network *gsmnet = gsmnet_from_vty(vty); - - if (enable && ipacc_rtp_direct) { - vty_out(vty, "%% Cannot enable handover unless RTP Proxy mode " - "is enabled by using the -P command line option%s", - VTY_NEWLINE); - return CMD_WARNING; - } - gsmnet->handover.active = enable; - - return CMD_SUCCESS; -} - -#define HO_WIN_STR HANDOVER_STR "Measurement Window\n" -#define HO_WIN_RXLEV_STR HO_WIN_STR "Received Level Averaging\n" -#define HO_WIN_RXQUAL_STR HO_WIN_STR "Received Quality Averaging\n" -#define HO_PBUDGET_STR HANDOVER_STR "Power Budget\n" -#define HO_AVG_COUNT_STR "Amount to use for Averaging\n" - -DEFUN(cfg_net_ho_win_rxlev_avg, cfg_net_ho_win_rxlev_avg_cmd, - "handover window rxlev averaging <1-10>", - HO_WIN_RXLEV_STR - "How many RxLev measurements are used for averaging\n" - HO_AVG_COUNT_STR) -{ - struct gsm_network *gsmnet = gsmnet_from_vty(vty); - gsmnet->handover.win_rxlev_avg = atoi(argv[0]); - return CMD_SUCCESS; -} - -DEFUN(cfg_net_ho_win_rxqual_avg, cfg_net_ho_win_rxqual_avg_cmd, - "handover window rxqual averaging <1-10>", - HO_WIN_RXQUAL_STR - "How many RxQual measurements are used for averaging\n" - HO_AVG_COUNT_STR) -{ - struct gsm_network *gsmnet = gsmnet_from_vty(vty); - gsmnet->handover.win_rxqual_avg = atoi(argv[0]); - return CMD_SUCCESS; -} - -DEFUN(cfg_net_ho_win_rxlev_neigh_avg, cfg_net_ho_win_rxlev_avg_neigh_cmd, - "handover window rxlev neighbor averaging <1-10>", - HO_WIN_RXLEV_STR "Neighbor\n" - "How many RxQual measurements are used for averaging\n" - HO_AVG_COUNT_STR) -{ - struct gsm_network *gsmnet = gsmnet_from_vty(vty); - gsmnet->handover.win_rxlev_avg_neigh = atoi(argv[0]); - return CMD_SUCCESS; -} - -DEFUN(cfg_net_ho_pwr_interval, cfg_net_ho_pwr_interval_cmd, - "handover power budget interval <1-99>", - HO_PBUDGET_STR - "How often to check if we have a better cell (SACCH frames)\n" - "Interval\n" "Number\n") -{ - struct gsm_network *gsmnet = gsmnet_from_vty(vty); - gsmnet->handover.pwr_interval = atoi(argv[0]); - return CMD_SUCCESS; -} - -DEFUN(cfg_net_ho_pwr_hysteresis, cfg_net_ho_pwr_hysteresis_cmd, - "handover power budget hysteresis <0-999>", - HO_PBUDGET_STR - "How many dB does a neighbor to be stronger to become a HO candidate\n" - "Hysteresis\n" "Number\n") -{ - struct gsm_network *gsmnet = gsmnet_from_vty(vty); - gsmnet->handover.pwr_hysteresis = atoi(argv[0]); - return CMD_SUCCESS; -} - -DEFUN(cfg_net_ho_max_distance, cfg_net_ho_max_distance_cmd, - "handover maximum distance <0-9999>", - HANDOVER_STR - "How big is the maximum timing advance before HO is forced\n" - "Distance\n" "Number\n") -{ - struct gsm_network *gsmnet = gsmnet_from_vty(vty); - gsmnet->handover.max_distance = atoi(argv[0]); - return CMD_SUCCESS; -} - -DEFUN(cfg_net_pag_any_tch, - cfg_net_pag_any_tch_cmd, - "paging any use tch (0|1)", - "Assign a TCH when receiving a Paging Any request\n" - "Any Channel\n" "Use\n" "TCH\n" - "Do not use TCH for Paging Request Any\n" - "Do use TCH for Paging Request Any\n") -{ - struct gsm_network *gsmnet = gsmnet_from_vty(vty); - gsmnet->pag_any_tch = atoi(argv[0]); - gsm_net_update_ctype(gsmnet); - return CMD_SUCCESS; -} - -#define DECLARE_TIMER(number, doc) \ - DEFUN(cfg_net_T##number, \ - cfg_net_T##number##_cmd, \ - "timer t" #number " <0-65535>", \ - "Configure GSM Timers\n" \ - doc "Timer Value in seconds\n") \ -{ \ - struct gsm_network *gsmnet = gsmnet_from_vty(vty); \ - int value = atoi(argv[0]); \ - \ - if (value < 0 || value > 65535) { \ - vty_out(vty, "Timer value %s out of range.%s", \ - argv[0], VTY_NEWLINE); \ - return CMD_WARNING; \ - } \ - \ - gsmnet->T##number = value; \ - return CMD_SUCCESS; \ -} - -DECLARE_TIMER(3101, "Set the timeout value for IMMEDIATE ASSIGNMENT.\n") -DECLARE_TIMER(3103, "Set the timeout value for HANDOVER.\n") -DECLARE_TIMER(3105, "Set the timer for repetition of PHYSICAL INFORMATION.\n") -DECLARE_TIMER(3107, "Currently not used.\n") -DECLARE_TIMER(3109, "Set the RSL SACCH deactivation timeout.\n") -DECLARE_TIMER(3111, "Set the RSL timeout to wait before releasing the RF Channel.\n") -DECLARE_TIMER(3113, "Set the time to try paging a subscriber.\n") -DECLARE_TIMER(3115, "Currently not used.\n") -DECLARE_TIMER(3117, "Currently not used.\n") -DECLARE_TIMER(3119, "Currently not used.\n") -DECLARE_TIMER(3122, "Waiting time (seconds) after IMM ASS REJECT\n") -DECLARE_TIMER(3141, "Currently not used.\n") - -DEFUN_DEPRECATED(cfg_net_dtx, - cfg_net_dtx_cmd, - "dtx-used (0|1)", - ".HIDDEN\n""Obsolete\n""Obsolete\n") -{ - vty_out(vty, "%% 'dtx-used' is now deprecated: use dtx * " - "configuration options of BTS instead%s", VTY_NEWLINE); - return CMD_SUCCESS; -} - -/* per-BTS configuration */ -DEFUN(cfg_bts, - cfg_bts_cmd, - "bts <0-255>", - "Select a BTS to configure\n" - "BTS Number\n") -{ - struct gsm_network *gsmnet = gsmnet_from_vty(vty); - int bts_nr = atoi(argv[0]); - struct gsm_bts *bts; - - if (bts_nr > gsmnet->num_bts) { - vty_out(vty, "%% The next unused BTS number is %u%s", - gsmnet->num_bts, VTY_NEWLINE); - return CMD_WARNING; - } else if (bts_nr == gsmnet->num_bts) { - /* allocate a new one */ - bts = gsm_bts_alloc_register(gsmnet, GSM_BTS_TYPE_UNKNOWN, - HARDCODED_BSIC); - } else - bts = gsm_bts_num(gsmnet, bts_nr); - - if (!bts) { - vty_out(vty, "%% Unable to allocate BTS %u%s", - gsmnet->num_bts, VTY_NEWLINE); - return CMD_WARNING; - } - - vty->index = bts; - vty->index_sub = &bts->description; - vty->node = BTS_NODE; - - return CMD_SUCCESS; -} - -DEFUN(cfg_bts_type, - cfg_bts_type_cmd, - "type TYPE", /* dynamically created */ - "Set the BTS type\n" "Type\n") -{ - struct gsm_bts *bts = vty->index; - int rc; - - rc = gsm_set_bts_type(bts, str2btstype(argv[0])); - if (rc < 0) - return CMD_WARNING; - - return CMD_SUCCESS; -} - -DEFUN(cfg_bts_band, - cfg_bts_band_cmd, - "band BAND", - "Set the frequency band of this BTS\n" "Frequency band\n") -{ - struct gsm_bts *bts = vty->index; - int band = gsm_band_parse(argv[0]); - - if (band < 0) { - vty_out(vty, "%% BAND %d is not a valid GSM band%s", - band, VTY_NEWLINE); - return CMD_WARNING; - } - - bts->band = band; - - return CMD_SUCCESS; -} - -DEFUN(cfg_bts_dtxu, cfg_bts_dtxu_cmd, "dtx uplink [force]", - "Configure discontinuous transmission\n" - "Enable Uplink DTX for this BTS\n" - "MS 'shall' use DTXu instead of 'may' use (might not be supported by " - "older phones).\n") -{ - struct gsm_bts *bts = vty->index; - - bts->dtxu = (argc > 0) ? GSM48_DTX_SHALL_BE_USED : GSM48_DTX_MAY_BE_USED; - if (!is_ipaccess_bts(bts)) - vty_out(vty, "%% DTX enabled on non-IP BTS: this configuration " - "neither supported nor tested!%s", VTY_NEWLINE); - return CMD_SUCCESS; -} - -DEFUN(cfg_bts_no_dtxu, cfg_bts_no_dtxu_cmd, "no dtx uplink", - NO_STR - "Configure discontinuous transmission\n" - "Disable Uplink DTX for this BTS\n") -{ - struct gsm_bts *bts = vty->index; - - bts->dtxu = GSM48_DTX_SHALL_NOT_BE_USED; - - return CMD_SUCCESS; -} - -DEFUN(cfg_bts_dtxd, cfg_bts_dtxd_cmd, "dtx downlink", - "Configure discontinuous transmission\n" - "Enable Downlink DTX for this BTS\n") -{ - struct gsm_bts *bts = vty->index; - - bts->dtxd = true; - if (!is_ipaccess_bts(bts)) - vty_out(vty, "%% DTX enabled on non-IP BTS: this configuration " - "neither supported nor tested!%s", VTY_NEWLINE); - return CMD_SUCCESS; -} - -DEFUN(cfg_bts_no_dtxd, cfg_bts_no_dtxd_cmd, "no dtx downlink", - NO_STR - "Configure discontinuous transmission\n" - "Disable Downlink DTX for this BTS\n") -{ - struct gsm_bts *bts = vty->index; - - bts->dtxd = false; - - return CMD_SUCCESS; -} - -DEFUN(cfg_bts_ci, - cfg_bts_ci_cmd, - "cell_identity <0-65535>", - "Set the Cell identity of this BTS\n" "Cell Identity\n") -{ - struct gsm_bts *bts = vty->index; - int ci = atoi(argv[0]); - - if (ci < 0 || ci > 0xffff) { - vty_out(vty, "%% CI %d is not in the valid range (0-65535)%s", - ci, VTY_NEWLINE); - return CMD_WARNING; - } - bts->cell_identity = ci; - - return CMD_SUCCESS; -} - -DEFUN(cfg_bts_lac, - cfg_bts_lac_cmd, - "location_area_code <0-65535>", - "Set the Location Area Code (LAC) of this BTS\n" "LAC\n") -{ - struct gsm_bts *bts = vty->index; - int lac = atoi(argv[0]); - - if (lac < 0 || lac > 0xffff) { - vty_out(vty, "%% LAC %d is not in the valid range (0-65535)%s", - lac, VTY_NEWLINE); - return CMD_WARNING; - } - - if (lac == GSM_LAC_RESERVED_DETACHED || lac == GSM_LAC_RESERVED_ALL_BTS) { - vty_out(vty, "%% LAC %d is reserved by GSM 04.08%s", - lac, VTY_NEWLINE); - return CMD_WARNING; - } - - bts->location_area_code = lac; - - return CMD_SUCCESS; -} - - -/* compatibility wrapper for old config files */ -DEFUN_HIDDEN(cfg_bts_tsc, - cfg_bts_tsc_cmd, - "training_sequence_code <0-7>", - "Set the Training Sequence Code (TSC) of this BTS\n" "TSC\n") -{ - return CMD_SUCCESS; -} - -DEFUN(cfg_bts_bsic, - cfg_bts_bsic_cmd, - "base_station_id_code <0-63>", - "Set the Base Station Identity Code (BSIC) of this BTS\n" - "BSIC of this BTS\n") -{ - struct gsm_bts *bts = vty->index; - int bsic = atoi(argv[0]); - - if (bsic < 0 || bsic > 0x3f) { - vty_out(vty, "%% BSIC %d is not in the valid range (0-255)%s", - bsic, VTY_NEWLINE); - return CMD_WARNING; - } - bts->bsic = bsic; - - return CMD_SUCCESS; -} - -DEFUN(cfg_bts_unit_id, - cfg_bts_unit_id_cmd, - "ip.access unit_id <0-65534> <0-255>", - "Abis/IP specific options\n" - "Set the IPA BTS Unit ID\n" - "Unit ID (Site)\n" - "Unit ID (BTS)\n") -{ - struct gsm_bts *bts = vty->index; - int site_id = atoi(argv[0]); - int bts_id = atoi(argv[1]); - - if (!is_ipaccess_bts(bts)) { - vty_out(vty, "%% BTS is not of ip.access type%s", VTY_NEWLINE); - return CMD_WARNING; - } - - bts->ip_access.site_id = site_id; - bts->ip_access.bts_id = bts_id; - - return CMD_SUCCESS; -} - -DEFUN(cfg_bts_rsl_ip, - cfg_bts_rsl_ip_cmd, - "ip.access rsl-ip A.B.C.D", - "Abis/IP specific options\n" - "Set the IPA RSL IP Address of the BSC\n" - "Destination IP address for RSL connection\n") -{ - struct gsm_bts *bts = vty->index; - struct in_addr ia; - - if (!is_ipaccess_bts(bts)) { - vty_out(vty, "%% BTS is not of ip.access type%s", VTY_NEWLINE); - return CMD_WARNING; - } - - inet_aton(argv[0], &ia); - bts->ip_access.rsl_ip = ntohl(ia.s_addr); - - return CMD_SUCCESS; -} - -#define NOKIA_STR "Nokia *Site related commands\n" - -DEFUN(cfg_bts_nokia_site_skip_reset, - cfg_bts_nokia_site_skip_reset_cmd, - "nokia_site skip-reset (0|1)", - NOKIA_STR - "Skip the reset step during bootstrap process of this BTS\n" - "Do NOT skip the reset\n" "Skip the reset\n") -{ - struct gsm_bts *bts = vty->index; - - if (bts->type != GSM_BTS_TYPE_NOKIA_SITE) { - vty_out(vty, "%% BTS is not of Nokia *Site type%s", VTY_NEWLINE); - return CMD_WARNING; - } - - bts->nokia.skip_reset = atoi(argv[0]); - - return CMD_SUCCESS; -} - -DEFUN(cfg_bts_nokia_site_no_loc_rel_cnf, - cfg_bts_nokia_site_no_loc_rel_cnf_cmd, - "nokia_site no-local-rel-conf (0|1)", - NOKIA_STR - "Do not wait for RELease CONFirm message when releasing channel locally\n" - "Wait for RELease CONFirm\n" "Do not wait for RELease CONFirm\n") -{ - struct gsm_bts *bts = vty->index; - - if (!is_nokia_bts(bts)) { - vty_out(vty, "%% BTS is not of Nokia *Site type%s", - VTY_NEWLINE); - return CMD_WARNING; - } - - bts->nokia.no_loc_rel_cnf = atoi(argv[0]); - - return CMD_SUCCESS; -} - -DEFUN(cfg_bts_nokia_site_bts_reset_timer_cnf, - cfg_bts_nokia_site_bts_reset_timer_cnf_cmd, - "nokia_site bts-reset-timer <15-100>", - NOKIA_STR - "The amount of time (in sec.) between BTS_RESET is sent,\n" - "and the BTS is being bootstrapped.\n") -{ - struct gsm_bts *bts = vty->index; - - if (!is_nokia_bts(bts)) { - vty_out(vty, "%% BTS is not of Nokia *Site type%s", - VTY_NEWLINE); - return CMD_WARNING; - } - - bts->nokia.bts_reset_timer_cnf = atoi(argv[0]); - - return CMD_SUCCESS; -} -#define OML_STR "Organization & Maintenance Link\n" -#define IPA_STR "A-bis/IP Specific Options\n" - -DEFUN(cfg_bts_stream_id, - cfg_bts_stream_id_cmd, - "oml ip.access stream_id <0-255> line E1_LINE", - OML_STR IPA_STR - "Set the ip.access Stream ID of the OML link of this BTS\n" - "Stream Identifier\n" "Virtual E1 Line Number\n" "Virtual E1 Line Number\n") -{ - struct gsm_bts *bts = vty->index; - int stream_id = atoi(argv[0]), linenr = atoi(argv[1]); - - if (!is_ipaccess_bts(bts)) { - vty_out(vty, "%% BTS is not of ip.access type%s", VTY_NEWLINE); - return CMD_WARNING; - } - - bts->oml_tei = stream_id; - /* This is used by e1inp_bind_ops callback for each BTS model. */ - bts->oml_e1_link.e1_nr = linenr; - - return CMD_SUCCESS; -} - -#define OML_E1_STR OML_STR "OML E1/T1 Configuration\n" - -DEFUN(cfg_bts_oml_e1, - cfg_bts_oml_e1_cmd, - "oml e1 line E1_LINE timeslot <1-31> sub-slot (0|1|2|3|full)", - OML_E1_STR - "E1/T1 line number to be used for OML\n" - "E1/T1 line number to be used for OML\n" - "E1/T1 timeslot to be used for OML\n" - "E1/T1 timeslot to be used for OML\n" - "E1/T1 sub-slot to be used for OML\n" - "Use E1/T1 sub-slot 0\n" - "Use E1/T1 sub-slot 1\n" - "Use E1/T1 sub-slot 2\n" - "Use E1/T1 sub-slot 3\n" - "Use full E1 slot 3\n" - ) -{ - struct gsm_bts *bts = vty->index; - - parse_e1_link(&bts->oml_e1_link, argv[0], argv[1], argv[2]); - - return CMD_SUCCESS; -} - - -DEFUN(cfg_bts_oml_e1_tei, - cfg_bts_oml_e1_tei_cmd, - "oml e1 tei <0-63>", - OML_E1_STR - "Set the TEI to be used for OML\n" - "TEI Number\n") -{ - struct gsm_bts *bts = vty->index; - - bts->oml_tei = atoi(argv[0]); - - return CMD_SUCCESS; -} - -DEFUN(cfg_bts_challoc, cfg_bts_challoc_cmd, - "channel allocator (ascending|descending)", - "Channnel Allocator\n" "Channel Allocator\n" - "Allocate Timeslots and Transceivers in ascending order\n" - "Allocate Timeslots and Transceivers in descending order\n") -{ - struct gsm_bts *bts = vty->index; - - if (!strcmp(argv[0], "ascending")) - bts->chan_alloc_reverse = 0; - else - bts->chan_alloc_reverse = 1; - - return CMD_SUCCESS; -} - -#define RACH_STR "Random Access Control Channel\n" - -DEFUN(cfg_bts_rach_tx_integer, - cfg_bts_rach_tx_integer_cmd, - "rach tx integer <0-15>", - RACH_STR - "Set the raw tx integer value in RACH Control parameters IE\n" - "Set the raw tx integer value in RACH Control parameters IE\n" - "Raw tx integer value in RACH Control parameters IE\n") -{ - struct gsm_bts *bts = vty->index; - bts->si_common.rach_control.tx_integer = atoi(argv[0]) & 0xf; - return CMD_SUCCESS; -} - -DEFUN(cfg_bts_rach_max_trans, - cfg_bts_rach_max_trans_cmd, - "rach max transmission (1|2|4|7)", - RACH_STR - "Set the maximum number of RACH burst transmissions\n" - "Set the maximum number of RACH burst transmissions\n" - "Maximum number of 1 RACH burst transmissions\n" - "Maximum number of 2 RACH burst transmissions\n" - "Maximum number of 4 RACH burst transmissions\n" - "Maximum number of 7 RACH burst transmissions\n") -{ - struct gsm_bts *bts = vty->index; - bts->si_common.rach_control.max_trans = rach_max_trans_val2raw(atoi(argv[0])); - return CMD_SUCCESS; -} - -#define CD_STR "Channel Description\n" - -DEFUN(cfg_bts_chan_desc_att, - cfg_bts_chan_desc_att_cmd, - "channel-descrption attach (0|1)", - CD_STR - "Set if attachment is required\n" - "Attachment is NOT required\n" - "Attachment is required (standard)\n") -{ - struct gsm_bts *bts = vty->index; - bts->si_common.chan_desc.att = atoi(argv[0]); - return CMD_SUCCESS; -} - -DEFUN(cfg_bts_chan_desc_bs_pa_mfrms, - cfg_bts_chan_desc_bs_pa_mfrms_cmd, - "channel-descrption bs-pa-mfrms <2-9>", - CD_STR - "Set number of multiframe periods for paging groups\n" - "Number of multiframe periods for paging groups\n") -{ - struct gsm_bts *bts = vty->index; - int bs_pa_mfrms = atoi(argv[0]); - - bts->si_common.chan_desc.bs_pa_mfrms = bs_pa_mfrms - 2; - return CMD_SUCCESS; -} - -DEFUN(cfg_bts_chan_desc_bs_ag_blks_res, - cfg_bts_chan_desc_bs_ag_blks_res_cmd, - "channel-descrption bs-ag-blks-res <0-7>", - CD_STR - "Set number of blocks reserved for access grant\n" - "Number of blocks reserved for access grant\n") -{ - struct gsm_bts *bts = vty->index; - int bs_ag_blks_res = atoi(argv[0]); - - bts->si_common.chan_desc.bs_ag_blks_res = bs_ag_blks_res; - return CMD_SUCCESS; -} - -#define NM_STR "Network Management\n" - -DEFUN(cfg_bts_rach_nm_b_thresh, - cfg_bts_rach_nm_b_thresh_cmd, - "rach nm busy threshold <0-255>", - RACH_STR NM_STR - "Set the NM Busy Threshold\n" - "Set the NM Busy Threshold\n" - "NM Busy Threshold in dB") -{ - struct gsm_bts *bts = vty->index; - bts->rach_b_thresh = atoi(argv[0]); - return CMD_SUCCESS; -} - -DEFUN(cfg_bts_rach_nm_ldavg, - cfg_bts_rach_nm_ldavg_cmd, - "rach nm load average <0-65535>", - RACH_STR NM_STR - "Set the NM Loadaverage Slots value\n" - "Set the NM Loadaverage Slots value\n" - "NM Loadaverage Slots value\n") -{ - struct gsm_bts *bts = vty->index; - bts->rach_ldavg_slots = atoi(argv[0]); - return CMD_SUCCESS; -} - -DEFUN(cfg_bts_cell_barred, cfg_bts_cell_barred_cmd, - "cell barred (0|1)", - "Should this cell be barred from access?\n" - "Should this cell be barred from access?\n" - "Cell should NOT be barred\n" - "Cell should be barred\n") - -{ - struct gsm_bts *bts = vty->index; - - bts->si_common.rach_control.cell_bar = atoi(argv[0]); - - return CMD_SUCCESS; -} - -DEFUN(cfg_bts_rach_ec_allowed, cfg_bts_rach_ec_allowed_cmd, - "rach emergency call allowed (0|1)", - RACH_STR - "Should this cell allow emergency calls?\n" - "Should this cell allow emergency calls?\n" - "Should this cell allow emergency calls?\n" - "Do NOT allow emergency calls\n" - "Allow emergency calls\n") -{ - struct gsm_bts *bts = vty->index; - - if (atoi(argv[0]) == 0) - bts->si_common.rach_control.t2 |= 0x4; - else - bts->si_common.rach_control.t2 &= ~0x4; - - return CMD_SUCCESS; -} - -DEFUN(cfg_bts_rach_ac_class, cfg_bts_rach_ac_class_cmd, - "rach access-control-class (0|1|2|3|4|5|6|7|8|9|11|12|13|14|15) (barred|allowed)", - RACH_STR - "Set access control class\n" - "Access control class 0\n" - "Access control class 1\n" - "Access control class 2\n" - "Access control class 3\n" - "Access control class 4\n" - "Access control class 5\n" - "Access control class 6\n" - "Access control class 7\n" - "Access control class 8\n" - "Access control class 9\n" - "Access control class 11 for PLMN use\n" - "Access control class 12 for security services\n" - "Access control class 13 for public utilities (e.g. water/gas suppliers)\n" - "Access control class 14 for emergency services\n" - "Access control class 15 for PLMN staff\n" - "barred to use access control class\n" - "allowed to use access control class\n") -{ - struct gsm_bts *bts = vty->index; - - uint8_t control_class; - uint8_t allowed = 0; - - if (strcmp(argv[1], "allowed") == 0) - allowed = 1; - - control_class = atoi(argv[0]); - if (control_class < 8) - if (allowed) - bts->si_common.rach_control.t3 &= ~(0x1 << control_class); - else - bts->si_common.rach_control.t3 |= (0x1 << control_class); - else - if (allowed) - bts->si_common.rach_control.t2 &= ~(0x1 << (control_class - 8)); - else - bts->si_common.rach_control.t2 |= (0x1 << (control_class - 8)); - - return CMD_SUCCESS; -} - -DEFUN(cfg_bts_ms_max_power, cfg_bts_ms_max_power_cmd, - "ms max power <0-40>", - "MS Options\n" - "Maximum transmit power of the MS\n" - "Maximum transmit power of the MS\n" - "Maximum transmit power of the MS in dBm") -{ - struct gsm_bts *bts = vty->index; - - bts->ms_max_power = atoi(argv[0]); - - return CMD_SUCCESS; -} - -#define CELL_STR "Cell Parameters\n" - -DEFUN(cfg_bts_cell_resel_hyst, cfg_bts_cell_resel_hyst_cmd, - "cell reselection hysteresis <0-14>", - CELL_STR "Cell re-selection parameters\n" - "Cell Re-Selection Hysteresis in dB\n" - "Cell Re-Selection Hysteresis in dB") -{ - struct gsm_bts *bts = vty->index; - - bts->si_common.cell_sel_par.cell_resel_hyst = atoi(argv[0])/2; - - return CMD_SUCCESS; -} - -DEFUN(cfg_bts_rxlev_acc_min, cfg_bts_rxlev_acc_min_cmd, - "rxlev access min <0-63>", - "Minimum RxLev needed for cell access\n" - "Minimum RxLev needed for cell access\n" - "Minimum RxLev needed for cell access\n" - "Minimum RxLev needed for cell access (better than -110dBm)") -{ - struct gsm_bts *bts = vty->index; - - bts->si_common.cell_sel_par.rxlev_acc_min = atoi(argv[0]); - - return CMD_SUCCESS; -} - -DEFUN(cfg_bts_cell_bar_qualify, cfg_bts_cell_bar_qualify_cmd, - "cell bar qualify (0|1)", - CELL_STR "Cell Bar Qualify\n" "Cell Bar Qualify\n" - "Set CBQ to 0\n" "Set CBQ to 1\n") -{ - struct gsm_bts *bts = vty->index; - - bts->si_common.cell_ro_sel_par.present = 1; - bts->si_common.cell_ro_sel_par.cbq = atoi(argv[0]); - - return CMD_SUCCESS; -} - -DEFUN(cfg_bts_cell_resel_ofs, cfg_bts_cell_resel_ofs_cmd, - "cell reselection offset <0-126>", - CELL_STR "Cell Re-Selection Parameters\n" - "Cell Re-Selection Offset (CRO) in dB\n" - "Cell Re-Selection Offset (CRO) in dB\n" - ) -{ - struct gsm_bts *bts = vty->index; - - bts->si_common.cell_ro_sel_par.present = 1; - bts->si_common.cell_ro_sel_par.cell_resel_off = atoi(argv[0])/2; - - return CMD_SUCCESS; -} - -DEFUN(cfg_bts_temp_ofs, cfg_bts_temp_ofs_cmd, - "temporary offset <0-60>", - "Cell selection temporary negative offset\n" - "Cell selection temporary negative offset\n" - "Cell selection temporary negative offset in dB") -{ - struct gsm_bts *bts = vty->index; - - bts->si_common.cell_ro_sel_par.present = 1; - bts->si_common.cell_ro_sel_par.temp_offs = atoi(argv[0])/10; - - return CMD_SUCCESS; -} - -DEFUN(cfg_bts_temp_ofs_inf, cfg_bts_temp_ofs_inf_cmd, - "temporary offset infinite", - "Cell selection temporary negative offset\n" - "Cell selection temporary negative offset\n" - "Sets cell selection temporary negative offset to infinity") -{ - struct gsm_bts *bts = vty->index; - - bts->si_common.cell_ro_sel_par.present = 1; - bts->si_common.cell_ro_sel_par.temp_offs = 7; - - return CMD_SUCCESS; -} - -DEFUN(cfg_bts_penalty_time, cfg_bts_penalty_time_cmd, - "penalty time <20-620>", - "Cell selection penalty time\n" - "Cell selection penalty time\n" - "Cell selection penalty time in seconds (by 20s increments)\n") -{ - struct gsm_bts *bts = vty->index; - - bts->si_common.cell_ro_sel_par.present = 1; - bts->si_common.cell_ro_sel_par.penalty_time = (atoi(argv[0])-20)/20; - - return CMD_SUCCESS; -} - -DEFUN(cfg_bts_penalty_time_rsvd, cfg_bts_penalty_time_rsvd_cmd, - "penalty time reserved", - "Cell selection penalty time\n" - "Cell selection penalty time\n" - "Set cell selection penalty time to reserved value 31, " - "(indicate that CELL_RESELECT_OFFSET is subtracted from C2 " - "and TEMPORARY_OFFSET is ignored)") -{ - struct gsm_bts *bts = vty->index; - - bts->si_common.cell_ro_sel_par.present = 1; - bts->si_common.cell_ro_sel_par.penalty_time = 31; - - return CMD_SUCCESS; -} - -DEFUN(cfg_bts_per_loc_upd, cfg_bts_per_loc_upd_cmd, - "periodic location update <6-1530>", - "Periodic Location Updating Interval\n" - "Periodic Location Updating Interval\n" - "Periodic Location Updating Interval\n" - "Periodic Location Updating Interval in Minutes\n") -{ - struct gsm_bts *bts = vty->index; - - bts->si_common.chan_desc.t3212 = atoi(argv[0]) / 6; - - return CMD_SUCCESS; -} - -DEFUN(cfg_bts_no_per_loc_upd, cfg_bts_no_per_loc_upd_cmd, - "no periodic location update", - NO_STR - "Periodic Location Updating Interval\n" - "Periodic Location Updating Interval\n" - "Periodic Location Updating Interval\n") -{ - struct gsm_bts *bts = vty->index; - - bts->si_common.chan_desc.t3212 = 0; - - return CMD_SUCCESS; -} - -DEFUN(cfg_bts_radio_link_timeout, cfg_bts_radio_link_timeout_cmd, - "radio-link-timeout <4-64>", - "Radio link timeout criterion (BTS side)\n" - "Radio link timeout value (lost SACCH block)\n") -{ - struct gsm_bts *bts = vty->index; - - gsm_bts_set_radio_link_timeout(bts, atoi(argv[0])); - - return CMD_SUCCESS; -} - -DEFUN(cfg_bts_radio_link_timeout_inf, cfg_bts_radio_link_timeout_inf_cmd, - "radio-link-timeout infinite", - "Radio link timeout criterion (BTS side)\n" - "Infinite Radio link timeout value (use only for BTS RF testing)\n") -{ - struct gsm_bts *bts = vty->index; - - if (bts->type != GSM_BTS_TYPE_OSMOBTS) { - vty_out(vty, "%% infinite radio link timeout not supported by this BTS%s", VTY_NEWLINE); - return CMD_WARNING; - } - - vty_out(vty, "%% INFINITE RADIO LINK TIMEOUT, USE ONLY FOR BTS RF TESTING%s", VTY_NEWLINE); - gsm_bts_set_radio_link_timeout(bts, -1); - - return CMD_SUCCESS; -} - -#define GPRS_TEXT "GPRS Packet Network\n" - -DEFUN(cfg_bts_prs_bvci, cfg_bts_gprs_bvci_cmd, - "gprs cell bvci <2-65535>", - GPRS_TEXT - "GPRS Cell Settings\n" - "GPRS BSSGP VC Identifier\n" - "GPRS BSSGP VC Identifier") -{ - struct gsm_bts *bts = vty->index; - - if (bts->gprs.mode == BTS_GPRS_NONE) { - vty_out(vty, "%% GPRS not enabled on this BTS%s", VTY_NEWLINE); - return CMD_WARNING; - } - - bts->gprs.cell.bvci = atoi(argv[0]); - - return CMD_SUCCESS; -} - -DEFUN(cfg_bts_gprs_nsei, cfg_bts_gprs_nsei_cmd, - "gprs nsei <0-65535>", - GPRS_TEXT - "GPRS NS Entity Identifier\n" - "GPRS NS Entity Identifier") -{ - struct gsm_bts *bts = vty->index; - - if (bts->gprs.mode == BTS_GPRS_NONE) { - vty_out(vty, "%% GPRS not enabled on this BTS%s", VTY_NEWLINE); - return CMD_WARNING; - } - - bts->gprs.nse.nsei = atoi(argv[0]); - - return CMD_SUCCESS; -} - -#define NSVC_TEXT "Network Service Virtual Connection (NS-VC)\n" \ - "NSVC Logical Number\n" - -DEFUN(cfg_bts_gprs_nsvci, cfg_bts_gprs_nsvci_cmd, - "gprs nsvc <0-1> nsvci <0-65535>", - GPRS_TEXT NSVC_TEXT - "NS Virtual Connection Identifier\n" - "GPRS NS VC Identifier") -{ - struct gsm_bts *bts = vty->index; - int idx = atoi(argv[0]); - - if (bts->gprs.mode == BTS_GPRS_NONE) { - vty_out(vty, "%% GPRS not enabled on this BTS%s", VTY_NEWLINE); - return CMD_WARNING; - } - - bts->gprs.nsvc[idx].nsvci = atoi(argv[1]); - - return CMD_SUCCESS; -} - -DEFUN(cfg_bts_gprs_nsvc_lport, cfg_bts_gprs_nsvc_lport_cmd, - "gprs nsvc <0-1> local udp port <0-65535>", - GPRS_TEXT NSVC_TEXT - "GPRS NS Local UDP Port\n" - "GPRS NS Local UDP Port\n" - "GPRS NS Local UDP Port\n" - "GPRS NS Local UDP Port Number\n") -{ - struct gsm_bts *bts = vty->index; - int idx = atoi(argv[0]); - - if (bts->gprs.mode == BTS_GPRS_NONE) { - vty_out(vty, "%% GPRS not enabled on this BTS%s", VTY_NEWLINE); - return CMD_WARNING; - } - - bts->gprs.nsvc[idx].local_port = atoi(argv[1]); - - return CMD_SUCCESS; -} - -DEFUN(cfg_bts_gprs_nsvc_rport, cfg_bts_gprs_nsvc_rport_cmd, - "gprs nsvc <0-1> remote udp port <0-65535>", - GPRS_TEXT NSVC_TEXT - "GPRS NS Remote UDP Port\n" - "GPRS NS Remote UDP Port\n" - "GPRS NS Remote UDP Port\n" - "GPRS NS Remote UDP Port Number\n") -{ - struct gsm_bts *bts = vty->index; - int idx = atoi(argv[0]); - - if (bts->gprs.mode == BTS_GPRS_NONE) { - vty_out(vty, "%% GPRS not enabled on this BTS%s", VTY_NEWLINE); - return CMD_WARNING; - } - - bts->gprs.nsvc[idx].remote_port = atoi(argv[1]); - - return CMD_SUCCESS; -} - -DEFUN(cfg_bts_gprs_nsvc_rip, cfg_bts_gprs_nsvc_rip_cmd, - "gprs nsvc <0-1> remote ip A.B.C.D", - GPRS_TEXT NSVC_TEXT - "GPRS NS Remote IP Address\n" - "GPRS NS Remote IP Address\n" - "GPRS NS Remote IP Address\n") -{ - struct gsm_bts *bts = vty->index; - int idx = atoi(argv[0]); - struct in_addr ia; - - if (bts->gprs.mode == BTS_GPRS_NONE) { - vty_out(vty, "%% GPRS not enabled on this BTS%s", VTY_NEWLINE); - return CMD_WARNING; - } - - inet_aton(argv[1], &ia); - bts->gprs.nsvc[idx].remote_ip = ntohl(ia.s_addr); - - return CMD_SUCCESS; -} - -DEFUN(cfg_bts_pag_free, cfg_bts_pag_free_cmd, - "paging free <-1-1024>", - "Paging options\n" - "Only page when having a certain amount of free slots\n" - "amount of required free paging slots. -1 to disable\n") -{ - struct gsm_bts *bts = vty->index; - - bts->paging.free_chans_need = atoi(argv[0]); - return CMD_SUCCESS; -} - -DEFUN(cfg_bts_gprs_ns_timer, cfg_bts_gprs_ns_timer_cmd, - "gprs ns timer " NS_TIMERS " <0-255>", - GPRS_TEXT "Network Service\n" - "Network Service Timer\n" - NS_TIMERS_HELP "Timer Value\n") -{ - struct gsm_bts *bts = vty->index; - int idx = get_string_value(gprs_ns_timer_strs, argv[0]); - int val = atoi(argv[1]); - - if (bts->gprs.mode == BTS_GPRS_NONE) { - vty_out(vty, "%% GPRS not enabled on this BTS%s", VTY_NEWLINE); - return CMD_WARNING; - } - - if (idx < 0 || idx >= ARRAY_SIZE(bts->gprs.nse.timer)) - return CMD_WARNING; - - bts->gprs.nse.timer[idx] = val; - - return CMD_SUCCESS; -} - -#define BSSGP_TIMERS "(blocking-timer|blocking-retries|unblocking-retries|reset-timer|reset-retries|suspend-timer|suspend-retries|resume-timer|resume-retries|capability-update-timer|capability-update-retries)" -#define BSSGP_TIMERS_HELP \ - "Tbvc-block timeout\n" \ - "Tbvc-block retries\n" \ - "Tbvc-unblock retries\n" \ - "Tbvcc-reset timeout\n" \ - "Tbvc-reset retries\n" \ - "Tbvc-suspend timeout\n" \ - "Tbvc-suspend retries\n" \ - "Tbvc-resume timeout\n" \ - "Tbvc-resume retries\n" \ - "Tbvc-capa-update timeout\n" \ - "Tbvc-capa-update retries\n" - -DEFUN(cfg_bts_gprs_cell_timer, cfg_bts_gprs_cell_timer_cmd, - "gprs cell timer " BSSGP_TIMERS " <0-255>", - GPRS_TEXT "Cell / BSSGP\n" - "Cell/BSSGP Timer\n" - BSSGP_TIMERS_HELP "Timer Value\n") -{ - struct gsm_bts *bts = vty->index; - int idx = get_string_value(gprs_bssgp_cfg_strs, argv[0]); - int val = atoi(argv[1]); - - if (bts->gprs.mode == BTS_GPRS_NONE) { - vty_out(vty, "%% GPRS not enabled on this BTS%s", VTY_NEWLINE); - return CMD_WARNING; - } - - if (idx < 0 || idx >= ARRAY_SIZE(bts->gprs.cell.timer)) - return CMD_WARNING; - - bts->gprs.cell.timer[idx] = val; - - return CMD_SUCCESS; -} - -DEFUN(cfg_bts_gprs_rac, cfg_bts_gprs_rac_cmd, - "gprs routing area <0-255>", - GPRS_TEXT - "GPRS Routing Area Code\n" - "GPRS Routing Area Code\n" - "GPRS Routing Area Code\n") -{ - struct gsm_bts *bts = vty->index; - - if (bts->gprs.mode == BTS_GPRS_NONE) { - vty_out(vty, "%% GPRS not enabled on this BTS%s", VTY_NEWLINE); - return CMD_WARNING; - } - - bts->gprs.rac = atoi(argv[0]); - - return CMD_SUCCESS; -} - -DEFUN(cfg_bts_gprs_ctrl_ack, cfg_bts_gprs_ctrl_ack_cmd, - "gprs control-ack-type-rach", GPRS_TEXT - "Set GPRS Control Ack Type for PACKET CONTROL ACKNOWLEDGMENT message to " - "four access bursts format instead of default RLC/MAC control block\n") -{ - struct gsm_bts *bts = vty->index; - - if (bts->gprs.mode == BTS_GPRS_NONE) { - vty_out(vty, "%% GPRS not enabled on this BTS%s", VTY_NEWLINE); - return CMD_WARNING; - } - - bts->gprs.ctrl_ack_type_use_block = false; - - return CMD_SUCCESS; -} - -DEFUN(cfg_no_bts_gprs_ctrl_ack, cfg_no_bts_gprs_ctrl_ack_cmd, - "no gprs control-ack-type-rach", NO_STR GPRS_TEXT - "Set GPRS Control Ack Type for PACKET CONTROL ACKNOWLEDGMENT message to " - "four access bursts format instead of default RLC/MAC control block\n") -{ - struct gsm_bts *bts = vty->index; - - if (bts->gprs.mode == BTS_GPRS_NONE) { - vty_out(vty, "%% GPRS not enabled on this BTS%s", VTY_NEWLINE); - return CMD_WARNING; - } - - bts->gprs.ctrl_ack_type_use_block = true; - - return CMD_SUCCESS; -} - -DEFUN(cfg_bts_gprs_net_ctrl_ord, cfg_bts_gprs_net_ctrl_ord_cmd, - "gprs network-control-order (nc0|nc1|nc2)", - GPRS_TEXT - "GPRS Network Control Order\n" - "MS controlled cell re-selection, no measurement reporting\n" - "MS controlled cell re-selection, MS sends measurement reports\n" - "Network controlled cell re-selection, MS sends measurement reports\n") -{ - struct gsm_bts *bts = vty->index; - - if (bts->gprs.mode == BTS_GPRS_NONE) { - vty_out(vty, "%% GPRS not enabled on this BTS%s", VTY_NEWLINE); - return CMD_WARNING; - } - - bts->gprs.net_ctrl_ord = atoi(argv[0] + 2); - - return CMD_SUCCESS; -} - -DEFUN(cfg_bts_gprs_mode, cfg_bts_gprs_mode_cmd, - "gprs mode (none|gprs|egprs)", - GPRS_TEXT - "GPRS Mode for this BTS\n" - "GPRS Disabled on this BTS\n" - "GPRS Enabled on this BTS\n" - "EGPRS (EDGE) Enabled on this BTS\n") -{ - struct gsm_bts *bts = vty->index; - enum bts_gprs_mode mode = bts_gprs_mode_parse(argv[0], NULL); - - if (!bts_gprs_mode_is_compat(bts, mode)) { - vty_out(vty, "This BTS type does not support %s%s", argv[0], - VTY_NEWLINE); - return CMD_WARNING; - } - - bts->gprs.mode = mode; - - return CMD_SUCCESS; -} - -DEFUN(cfg_bts_gprs_11bit_rach_support_for_egprs, - cfg_bts_gprs_11bit_rach_support_for_egprs_cmd, - "gprs 11bit_rach_support_for_egprs (0|1)", - GPRS_TEXT "11 bit RACH options\n" - "Disable 11 bit RACH for EGPRS\n" - "Enable 11 bit RACH for EGPRS") -{ - struct gsm_bts *bts = vty->index; - - bts->gprs.supports_egprs_11bit_rach = atoi(argv[0]); - - if (bts->gprs.supports_egprs_11bit_rach > 1) { - vty_out(vty, "Error in RACH type%s", VTY_NEWLINE); - return CMD_WARNING; - } - - if ((bts->gprs.mode == BTS_GPRS_NONE) && - (bts->gprs.supports_egprs_11bit_rach == 1)) { - vty_out(vty, "Error:gprs mode is none and 11bit rach is" - " enabled%s", VTY_NEWLINE); - return CMD_WARNING; - } - - return CMD_SUCCESS; -} - -#define SI_TEXT "System Information Messages\n" -#define SI_TYPE_TEXT "(1|2|3|4|5|6|7|8|9|10|13|16|17|18|19|20|2bis|2ter|2quater|5bis|5ter)" -#define SI_TYPE_HELP "System Information Type 1\n" \ - "System Information Type 2\n" \ - "System Information Type 3\n" \ - "System Information Type 4\n" \ - "System Information Type 5\n" \ - "System Information Type 6\n" \ - "System Information Type 7\n" \ - "System Information Type 8\n" \ - "System Information Type 9\n" \ - "System Information Type 10\n" \ - "System Information Type 13\n" \ - "System Information Type 16\n" \ - "System Information Type 17\n" \ - "System Information Type 18\n" \ - "System Information Type 19\n" \ - "System Information Type 20\n" \ - "System Information Type 2bis\n" \ - "System Information Type 2ter\n" \ - "System Information Type 2quater\n" \ - "System Information Type 5bis\n" \ - "System Information Type 5ter\n" - -DEFUN(cfg_bts_si_mode, cfg_bts_si_mode_cmd, - "system-information " SI_TYPE_TEXT " mode (static|computed)", - SI_TEXT SI_TYPE_HELP - "System Information Mode\n" - "Static user-specified\n" - "Dynamic, BSC-computed\n") -{ - struct gsm_bts *bts = vty->index; - int type; - - type = get_string_value(osmo_sitype_strs, argv[0]); - if (type < 0) { - vty_out(vty, "Error SI Type%s", VTY_NEWLINE); - return CMD_WARNING; - } - - if (!strcmp(argv[1], "static")) - bts->si_mode_static |= (1 << type); - else - bts->si_mode_static &= ~(1 << type); - - return CMD_SUCCESS; -} - -DEFUN(cfg_bts_si_static, cfg_bts_si_static_cmd, - "system-information " SI_TYPE_TEXT " static HEXSTRING", - SI_TEXT SI_TYPE_HELP - "Static System Information filling\n" - "Static user-specified SI content in HEX notation\n") -{ - struct gsm_bts *bts = vty->index; - int rc, type; - - type = get_string_value(osmo_sitype_strs, argv[0]); - if (type < 0) { - vty_out(vty, "Error SI Type%s", VTY_NEWLINE); - return CMD_WARNING; - } - - if (!(bts->si_mode_static & (1 << type))) { - vty_out(vty, "SI Type %s is not configured in static mode%s", - get_value_string(osmo_sitype_strs, type), VTY_NEWLINE); - return CMD_WARNING; - } - - /* Fill buffer with padding pattern */ - memset(GSM_BTS_SI(bts, type), 0x2b, GSM_MACBLOCK_LEN); - - /* Parse the user-specified SI in hex format, [partially] overwriting padding */ - rc = osmo_hexparse(argv[1], GSM_BTS_SI(bts, type), GSM_MACBLOCK_LEN); - if (rc < 0 || rc > GSM_MACBLOCK_LEN) { - vty_out(vty, "Error parsing HEXSTRING%s", VTY_NEWLINE); - return CMD_WARNING; - } - - /* Mark this SI as present */ - bts->si_valid |= (1 << type); - - return CMD_SUCCESS; -} - -DEFUN(cfg_bts_early_cm, cfg_bts_early_cm_cmd, - "early-classmark-sending (allowed|forbidden)", - "Early Classmark Sending\n" - "Early Classmark Sending is allowed\n" - "Early Classmark Sending is forbidden\n") -{ - struct gsm_bts *bts = vty->index; - - if (!strcmp(argv[0], "allowed")) - bts->early_classmark_allowed = true; - else - bts->early_classmark_allowed = false; - - return CMD_SUCCESS; -} - -DEFUN(cfg_bts_neigh_mode, cfg_bts_neigh_mode_cmd, - "neighbor-list mode (automatic|manual|manual-si5)", - "Neighbor List\n" "Mode of Neighbor List generation\n" - "Automatically from all BTS in this OpenBSC\n" "Manual\n" - "Manual with different lists for SI2 and SI5\n") -{ - struct gsm_bts *bts = vty->index; - int mode = get_string_value(bts_neigh_mode_strs, argv[0]); - - switch (mode) { - case NL_MODE_MANUAL_SI5SEP: - case NL_MODE_MANUAL: - /* make sure we clear the current list when switching to - * manual mode */ - if (bts->neigh_list_manual_mode == 0) - memset(&bts->si_common.data.neigh_list, 0, - sizeof(bts->si_common.data.neigh_list)); - break; - default: - break; - } - - bts->neigh_list_manual_mode = mode; - - return CMD_SUCCESS; -} - -DEFUN(cfg_bts_neigh, cfg_bts_neigh_cmd, - "neighbor-list (add|del) arfcn <0-1023>", - "Neighbor List\n" "Add to manual neighbor list\n" - "Delete from manual neighbor list\n" "ARFCN of neighbor\n" - "ARFCN of neighbor\n") -{ - struct gsm_bts *bts = vty->index; - struct bitvec *bv = &bts->si_common.neigh_list; - uint16_t arfcn = atoi(argv[1]); - - if (!bts->neigh_list_manual_mode) { - vty_out(vty, "%% Cannot configure neighbor list in " - "automatic mode%s", VTY_NEWLINE); - return CMD_WARNING; - } - - if (!strcmp(argv[0], "add")) - bitvec_set_bit_pos(bv, arfcn, 1); - else - bitvec_set_bit_pos(bv, arfcn, 0); - - return CMD_SUCCESS; -} - -/* help text should be kept in sync with EARFCN_*_INVALID defines */ -DEFUN(cfg_bts_si2quater_neigh_add, cfg_bts_si2quater_neigh_add_cmd, - "si2quater neighbor-list add earfcn <0-65535> thresh-hi <0-31> " - "thresh-lo <0-32> prio <0-8> qrxlv <0-32> meas <0-8>", - "SI2quater Neighbor List\n" "SI2quater Neighbor List\n" - "Add to manual SI2quater neighbor list\n" - "EARFCN of neighbor\n" "EARFCN of neighbor\n" - "threshold high bits\n" "threshold high bits\n" - "threshold low bits\n" "threshold low bits (32 means NA)\n" - "priority\n" "priority (8 means NA)\n" - "QRXLEVMIN\n" "QRXLEVMIN (32 means NA)\n" - "measurement bandwidth\n" "measurement bandwidth (8 means NA)\n") -{ - struct gsm_bts *bts = vty->index; - struct osmo_earfcn_si2q *e = &bts->si_common.si2quater_neigh_list; - uint16_t arfcn = atoi(argv[0]); - uint8_t thresh_hi = atoi(argv[1]), thresh_lo = atoi(argv[2]), - prio = atoi(argv[3]), qrx = atoi(argv[4]), meas = atoi(argv[5]); - int r = bts_earfcn_add(bts, arfcn, thresh_hi, thresh_lo, prio, qrx, meas); - - switch (r) { - case 1: - vty_out(vty, "Warning: multiple threshold-high are not supported, overriding with %u%s", - thresh_hi, VTY_NEWLINE); - break; - case EARFCN_THRESH_LOW_INVALID: - vty_out(vty, "Warning: multiple threshold-low are not supported, overriding with %u%s", - thresh_lo, VTY_NEWLINE); - break; - case EARFCN_QRXLV_INVALID + 1: - vty_out(vty, "Warning: multiple QRXLEVMIN are not supported, overriding with %u%s", - qrx, VTY_NEWLINE); - break; - case EARFCN_PRIO_INVALID: - vty_out(vty, "Warning: multiple priorities are not supported, overriding with %u%s", - prio, VTY_NEWLINE); - break; - default: - if (r < 0) { - vty_out(vty, "Unable to add ARFCN %u: %s%s", arfcn, strerror(-r), VTY_NEWLINE); - return CMD_WARNING; - } - } - - if (si2q_num(bts) <= SI2Q_MAX_NUM) - return CMD_SUCCESS; - - vty_out(vty, "Warning: not enough space in SI2quater (%u/%u used) for a given EARFCN %u%s", - bts->si2q_count, SI2Q_MAX_NUM, arfcn, VTY_NEWLINE); - osmo_earfcn_del(e, arfcn); - - return CMD_WARNING; -} - -DEFUN(cfg_bts_si2quater_neigh_del, cfg_bts_si2quater_neigh_del_cmd, - "si2quater neighbor-list del earfcn <0-65535>", - "SI2quater Neighbor List\n" - "SI2quater Neighbor List\n" - "Delete from SI2quater manual neighbor list\n" - "EARFCN of neighbor\n" - "EARFCN\n") -{ - struct gsm_bts *bts = vty->index; - struct osmo_earfcn_si2q *e = &bts->si_common.si2quater_neigh_list; - uint16_t arfcn = atoi(argv[0]); - int r = osmo_earfcn_del(e, arfcn); - if (r < 0) { - vty_out(vty, "Unable to delete arfcn %u: %s%s", arfcn, - strerror(-r), VTY_NEWLINE); - return CMD_WARNING; - } - - return CMD_SUCCESS; -} - -DEFUN(cfg_bts_si2quater_uarfcn_add, cfg_bts_si2quater_uarfcn_add_cmd, - "si2quater neighbor-list add uarfcn <0-16383> <0-511> <0-1>", - "SI2quater Neighbor List\n" - "SI2quater Neighbor List\n" "Add to manual SI2quater neighbor list\n" - "UARFCN of neighbor\n" "UARFCN of neighbor\n" "scrambling code\n" - "diversity bit\n") -{ - struct gsm_bts *bts = vty->index; - uint16_t arfcn = atoi(argv[0]), scramble = atoi(argv[1]); - - switch(bts_uarfcn_add(bts, arfcn, scramble, atoi(argv[2]))) { - case -ENOMEM: - vty_out(vty, "Unable to add UARFCN: max number of UARFCNs (%u) reached%s", MAX_EARFCN_LIST, VTY_NEWLINE); - return CMD_WARNING; - case -ENOSPC: - vty_out(vty, "Warning: not enough space in SI2quater for a given UARFCN (%u, %u)%s", - arfcn, scramble, VTY_NEWLINE); - return CMD_WARNING; - case -EADDRINUSE: - vty_out(vty, "Unable to add UARFCN: (%u, %u) is already added%s", arfcn, scramble, VTY_NEWLINE); - return CMD_WARNING; - } - - return CMD_SUCCESS; -} - -DEFUN(cfg_bts_si2quater_uarfcn_del, cfg_bts_si2quater_uarfcn_del_cmd, - "si2quater neighbor-list del uarfcn <0-16383> <0-511>", - "SI2quater Neighbor List\n" - "SI2quater Neighbor List\n" - "Delete from SI2quater manual neighbor list\n" - "UARFCN of neighbor\n" - "UARFCN\n" - "scrambling code\n") -{ - struct gsm_bts *bts = vty->index; - - if (bts_uarfcn_del(bts, atoi(argv[0]), atoi(argv[1])) < 0) { - vty_out(vty, "Unable to delete uarfcn: pair not found%s", - VTY_NEWLINE); - return CMD_WARNING; - } - - return CMD_SUCCESS; -} - -DEFUN(cfg_bts_si5_neigh, cfg_bts_si5_neigh_cmd, - "si5 neighbor-list (add|del) arfcn <0-1023>", - "SI5 Neighbor List\n" - "SI5 Neighbor List\n" "Add to manual SI5 neighbor list\n" - "Delete from SI5 manual neighbor list\n" "ARFCN of neighbor\n" - "ARFCN of neighbor\n") -{ - struct gsm_bts *bts = vty->index; - struct bitvec *bv = &bts->si_common.si5_neigh_list; - uint16_t arfcn = atoi(argv[1]); - - if (!bts->neigh_list_manual_mode) { - vty_out(vty, "%% Cannot configure neighbor list in " - "automatic mode%s", VTY_NEWLINE); - return CMD_WARNING; - } - - if (!strcmp(argv[0], "add")) - bitvec_set_bit_pos(bv, arfcn, 1); - else - bitvec_set_bit_pos(bv, arfcn, 0); - - return CMD_SUCCESS; -} - -DEFUN(cfg_bts_pcu_sock, cfg_bts_pcu_sock_cmd, - "pcu-socket PATH", - "PCU Socket Path for using OsmoPCU co-located with BSC (legacy BTS)\n" - "Path in the file system for the unix-domain PCU socket\n") -{ - struct gsm_bts *bts = vty->index; - int rc; - - osmo_talloc_replace_string(bts, &bts->pcu_sock_path, argv[0]); - pcu_sock_exit(bts); - rc = pcu_sock_init(bts->pcu_sock_path, bts); - if (rc < 0) { - vty_out(vty, "%% Error creating PCU socket `%s' for BTS %u%s", - bts->pcu_sock_path, bts->nr, VTY_NEWLINE); - return CMD_WARNING; - } - - return CMD_SUCCESS; -} - -#define EXCL_RFLOCK_STR "Exclude this BTS from the global RF Lock\n" - -DEFUN(cfg_bts_excl_rf_lock, - cfg_bts_excl_rf_lock_cmd, - "rf-lock-exclude", - EXCL_RFLOCK_STR) -{ - struct gsm_bts *bts = vty->index; - bts->excl_from_rf_lock = 1; - return CMD_SUCCESS; -} - -DEFUN(cfg_bts_no_excl_rf_lock, - cfg_bts_no_excl_rf_lock_cmd, - "no rf-lock-exclude", - NO_STR EXCL_RFLOCK_STR) -{ - struct gsm_bts *bts = vty->index; - bts->excl_from_rf_lock = 0; - return CMD_SUCCESS; -} - -#define FORCE_COMB_SI_STR "Force the generation of a single SI (no ter/bis)\n" - -DEFUN(cfg_bts_force_comb_si, - cfg_bts_force_comb_si_cmd, - "force-combined-si", - FORCE_COMB_SI_STR) -{ - struct gsm_bts *bts = vty->index; - bts->force_combined_si = 1; - return CMD_SUCCESS; -} - -DEFUN(cfg_bts_no_force_comb_si, - cfg_bts_no_force_comb_si_cmd, - "no force-combined-si", - NO_STR FORCE_COMB_SI_STR) -{ - struct gsm_bts *bts = vty->index; - bts->force_combined_si = 0; - return CMD_SUCCESS; -} - -static void _get_codec_from_arg(struct vty *vty, int argc, const char *argv[]) -{ - struct gsm_bts *bts = vty->index; - struct bts_codec_conf *codec = &bts->codec; - int i; - - codec->hr = 0; - codec->efr = 0; - codec->amr = 0; - for (i = 0; i < argc; i++) { - if (!strcmp(argv[i], "hr")) - codec->hr = 1; - if (!strcmp(argv[i], "efr")) - codec->efr = 1; - if (!strcmp(argv[i], "amr")) - codec->amr = 1; - } -} - -#define CODEC_PAR_STR " (hr|efr|amr)" -#define CODEC_HELP_STR "Half Rate\n" \ - "Enhanced Full Rate\nAdaptive Multirate\n" - -DEFUN(cfg_bts_codec0, cfg_bts_codec0_cmd, - "codec-support fr", - "Codec Support settings\nFullrate\n") -{ - _get_codec_from_arg(vty, 0, argv); - return CMD_SUCCESS; -} - -DEFUN(cfg_bts_codec1, cfg_bts_codec1_cmd, - "codec-support fr" CODEC_PAR_STR, - "Codec Support settings\nFullrate\n" - CODEC_HELP_STR) -{ - _get_codec_from_arg(vty, 1, argv); - return CMD_SUCCESS; -} - -DEFUN(cfg_bts_codec2, cfg_bts_codec2_cmd, - "codec-support fr" CODEC_PAR_STR CODEC_PAR_STR, - "Codec Support settings\nFullrate\n" - CODEC_HELP_STR CODEC_HELP_STR) -{ - _get_codec_from_arg(vty, 2, argv); - return CMD_SUCCESS; -} - -DEFUN(cfg_bts_codec3, cfg_bts_codec3_cmd, - "codec-support fr" CODEC_PAR_STR CODEC_PAR_STR CODEC_PAR_STR, - "Codec Support settings\nFullrate\n" - CODEC_HELP_STR CODEC_HELP_STR CODEC_HELP_STR) -{ - _get_codec_from_arg(vty, 3, argv); - return CMD_SUCCESS; -} - -DEFUN(cfg_bts_codec4, cfg_bts_codec4_cmd, - "codec-support fr" CODEC_PAR_STR CODEC_PAR_STR CODEC_PAR_STR CODEC_PAR_STR, - "Codec Support settings\nFullrate\n" - CODEC_HELP_STR CODEC_HELP_STR CODEC_HELP_STR CODEC_HELP_STR) -{ - _get_codec_from_arg(vty, 4, argv); - return CMD_SUCCESS; -} - -DEFUN(cfg_bts_depends_on, cfg_bts_depends_on_cmd, - "depends-on-bts <0-255>", - "This BTS can only be started if another one is up\n" "BTS Number\n") -{ - struct gsm_bts *bts = vty->index; - struct gsm_bts *other_bts; - int dep = atoi(argv[0]); - - - if (!is_ipaccess_bts(bts)) { - vty_out(vty, "This feature is only available for IP systems.%s", - VTY_NEWLINE); - return CMD_WARNING; - } - - other_bts = gsm_bts_num(bts->network, dep); - if (!other_bts || !is_ipaccess_bts(other_bts)) { - vty_out(vty, "This feature is only available for IP systems.%s", - VTY_NEWLINE); - return CMD_WARNING; - } - - if (dep >= bts->nr) { - vty_out(vty, "%%Need to depend on an already declared unit.%s", - VTY_NEWLINE); - return CMD_WARNING; - } - - bts_depend_mark(bts, dep); - return CMD_SUCCESS; -} - -DEFUN(cfg_bts_no_depends_on, cfg_bts_no_depends_on_cmd, - "depeneds-on-bts <0-255>", - NO_STR "This BTS can only be started if another one is up\n" - "BTS Number\n") -{ - struct gsm_bts *bts = vty->index; - int dep = atoi(argv[0]); - - bts_depend_clear(bts, dep); - return CMD_SUCCESS; -} - -#define AMR_TEXT "Adaptive Multi Rate settings\n" -#define AMR_MODE_TEXT "Codec modes to use with AMR codec\n" -#define AMR_START_TEXT "Initial codec to use with AMR\n" \ - "Automatically\nFirst codec\nSecond codec\nThird codec\nFourth codec\n" -#define AMR_TH_TEXT "AMR threshold between codecs\nMS side\nBTS side\n" -#define AMR_HY_TEXT "AMR hysteresis between codecs\nMS side\nBTS side\n" - -static void get_amr_from_arg(struct vty *vty, int argc, const char *argv[], int full) -{ - struct gsm_bts *bts = vty->index; - struct amr_multirate_conf *mr = (full) ? &bts->mr_full: &bts->mr_half; - struct gsm48_multi_rate_conf *mr_conf = - (struct gsm48_multi_rate_conf *) mr->gsm48_ie; - int i; - - mr->gsm48_ie[1] = 0; - for (i = 0; i < argc; i++) - mr->gsm48_ie[1] |= 1 << atoi(argv[i]); - mr_conf->icmi = 0; -} - -static void get_amr_th_from_arg(struct vty *vty, int argc, const char *argv[], int full) -{ - struct gsm_bts *bts = vty->index; - struct amr_multirate_conf *mr = (full) ? &bts->mr_full: &bts->mr_half; - struct amr_mode *modes; - int i; - - modes = argv[0][0]=='m' ? mr->ms_mode : mr->bts_mode; - for (i = 0; i < argc - 1; i++) - modes[i].threshold = atoi(argv[i + 1]); -} - -static void get_amr_hy_from_arg(struct vty *vty, int argc, const char *argv[], int full) -{ - struct gsm_bts *bts = vty->index; - struct amr_multirate_conf *mr = (full) ? &bts->mr_full: &bts->mr_half; - struct amr_mode *modes; - int i; - - modes = argv[0][0]=='m' ? mr->ms_mode : mr->bts_mode; - for (i = 0; i < argc - 1; i++) - modes[i].hysteresis = atoi(argv[i + 1]); -} - -static void get_amr_start_from_arg(struct vty *vty, const char *argv[], int full) -{ - struct gsm_bts *bts = vty->index; - struct amr_multirate_conf *mr = (full) ? &bts->mr_full: &bts->mr_half; - struct gsm48_multi_rate_conf *mr_conf = - (struct gsm48_multi_rate_conf *) mr->gsm48_ie; - int num = 0, i; - - for (i = 0; i < ((full) ? 8 : 6); i++) { - if ((mr->gsm48_ie[1] & (1 << i))) { - num++; - } - } - - if (argv[0][0] == 'a' || num == 0) - mr_conf->icmi = 0; - else { - mr_conf->icmi = 1; - if (num < atoi(argv[0])) - mr_conf->smod = num - 1; - else - mr_conf->smod = atoi(argv[0]) - 1; - } -} - -#define AMR_TCHF_PAR_STR " (0|1|2|3|4|5|6|7)" -#define AMR_TCHF_HELP_STR "4,75k\n5,15k\n5,90k\n6,70k\n7,40k\n7,95k\n" \ - "10,2k\n12,2k\n" - -#define AMR_TCHH_PAR_STR " (0|1|2|3|4|5)" -#define AMR_TCHH_HELP_STR "4,75k\n5,15k\n5,90k\n6,70k\n7,40k\n7,95k\n" - -#define AMR_TH_HELP_STR "Threshold between codec 1 and 2\n" -#define AMR_HY_HELP_STR "Hysteresis between codec 1 and 2\n" - -DEFUN(cfg_bts_amr_fr_modes1, cfg_bts_amr_fr_modes1_cmd, - "amr tch-f modes" AMR_TCHF_PAR_STR, - AMR_TEXT "Full Rate\n" AMR_MODE_TEXT - AMR_TCHF_HELP_STR) -{ - get_amr_from_arg(vty, 1, argv, 1); - return CMD_SUCCESS; -} - -DEFUN(cfg_bts_amr_fr_modes2, cfg_bts_amr_fr_modes2_cmd, - "amr tch-f modes" AMR_TCHF_PAR_STR AMR_TCHF_PAR_STR, - AMR_TEXT "Full Rate\n" AMR_MODE_TEXT - AMR_TCHF_HELP_STR AMR_TCHF_HELP_STR) -{ - get_amr_from_arg(vty, 2, argv, 1); - return CMD_SUCCESS; -} - -DEFUN(cfg_bts_amr_fr_modes3, cfg_bts_amr_fr_modes3_cmd, - "amr tch-f modes" AMR_TCHF_PAR_STR AMR_TCHF_PAR_STR AMR_TCHF_PAR_STR, - AMR_TEXT "Full Rate\n" AMR_MODE_TEXT - AMR_TCHF_HELP_STR AMR_TCHF_HELP_STR AMR_TCHF_HELP_STR) -{ - get_amr_from_arg(vty, 3, argv, 1); - return CMD_SUCCESS; -} - -DEFUN(cfg_bts_amr_fr_modes4, cfg_bts_amr_fr_modes4_cmd, - "amr tch-f modes" AMR_TCHF_PAR_STR AMR_TCHF_PAR_STR AMR_TCHF_PAR_STR AMR_TCHF_PAR_STR, - AMR_TEXT "Full Rate\n" AMR_MODE_TEXT - AMR_TCHF_HELP_STR AMR_TCHF_HELP_STR AMR_TCHF_HELP_STR AMR_TCHF_HELP_STR) -{ - get_amr_from_arg(vty, 4, argv, 1); - return CMD_SUCCESS; -} - -DEFUN(cfg_bts_amr_fr_start_mode, cfg_bts_amr_fr_start_mode_cmd, - "amr tch-f start-mode (auto|1|2|3|4)", - AMR_TEXT "Full Rate\n" AMR_START_TEXT) -{ - get_amr_start_from_arg(vty, argv, 1); - return CMD_SUCCESS; -} - -DEFUN(cfg_bts_amr_fr_thres1, cfg_bts_amr_fr_thres1_cmd, - "amr tch-f threshold (ms|bts) <0-63>", - AMR_TEXT "Full Rate\n" AMR_TH_TEXT - AMR_TH_HELP_STR) -{ - get_amr_th_from_arg(vty, 2, argv, 1); - return CMD_SUCCESS; -} - -DEFUN(cfg_bts_amr_fr_thres2, cfg_bts_amr_fr_thres2_cmd, - "amr tch-f threshold (ms|bts) <0-63> <0-63>", - AMR_TEXT "Full Rate\n" AMR_TH_TEXT - AMR_TH_HELP_STR AMR_TH_HELP_STR) -{ - get_amr_th_from_arg(vty, 3, argv, 1); - return CMD_SUCCESS; -} - -DEFUN(cfg_bts_amr_fr_thres3, cfg_bts_amr_fr_thres3_cmd, - "amr tch-f threshold (ms|bts) <0-63> <0-63> <0-63>", - AMR_TEXT "Full Rate\n" AMR_TH_TEXT - AMR_TH_HELP_STR AMR_TH_HELP_STR AMR_TH_HELP_STR) -{ - get_amr_th_from_arg(vty, 4, argv, 1); - return CMD_SUCCESS; -} - -DEFUN(cfg_bts_amr_fr_hyst1, cfg_bts_amr_fr_hyst1_cmd, - "amr tch-f hysteresis (ms|bts) <0-15>", - AMR_TEXT "Full Rate\n" AMR_HY_TEXT - AMR_HY_HELP_STR) -{ - get_amr_hy_from_arg(vty, 2, argv, 1); - return CMD_SUCCESS; -} - -DEFUN(cfg_bts_amr_fr_hyst2, cfg_bts_amr_fr_hyst2_cmd, - "amr tch-f hysteresis (ms|bts) <0-15> <0-15>", - AMR_TEXT "Full Rate\n" AMR_HY_TEXT - AMR_HY_HELP_STR AMR_HY_HELP_STR) -{ - get_amr_hy_from_arg(vty, 3, argv, 1); - return CMD_SUCCESS; -} - -DEFUN(cfg_bts_amr_fr_hyst3, cfg_bts_amr_fr_hyst3_cmd, - "amr tch-f hysteresis (ms|bts) <0-15> <0-15> <0-15>", - AMR_TEXT "Full Rate\n" AMR_HY_TEXT - AMR_HY_HELP_STR AMR_HY_HELP_STR AMR_HY_HELP_STR) -{ - get_amr_hy_from_arg(vty, 4, argv, 1); - return CMD_SUCCESS; -} - -DEFUN(cfg_bts_amr_hr_modes1, cfg_bts_amr_hr_modes1_cmd, - "amr tch-h modes" AMR_TCHH_PAR_STR, - AMR_TEXT "Half Rate\n" AMR_MODE_TEXT - AMR_TCHH_HELP_STR) -{ - get_amr_from_arg(vty, 1, argv, 0); - return CMD_SUCCESS; -} - -DEFUN(cfg_bts_amr_hr_modes2, cfg_bts_amr_hr_modes2_cmd, - "amr tch-h modes" AMR_TCHH_PAR_STR AMR_TCHH_PAR_STR, - AMR_TEXT "Half Rate\n" AMR_MODE_TEXT - AMR_TCHH_HELP_STR AMR_TCHH_HELP_STR) -{ - get_amr_from_arg(vty, 2, argv, 0); - return CMD_SUCCESS; -} - -DEFUN(cfg_bts_amr_hr_modes3, cfg_bts_amr_hr_modes3_cmd, - "amr tch-h modes" AMR_TCHH_PAR_STR AMR_TCHH_PAR_STR AMR_TCHH_PAR_STR, - AMR_TEXT "Half Rate\n" AMR_MODE_TEXT - AMR_TCHH_HELP_STR AMR_TCHH_HELP_STR AMR_TCHH_HELP_STR) -{ - get_amr_from_arg(vty, 3, argv, 0); - return CMD_SUCCESS; -} - -DEFUN(cfg_bts_amr_hr_modes4, cfg_bts_amr_hr_modes4_cmd, - "amr tch-h modes" AMR_TCHH_PAR_STR AMR_TCHH_PAR_STR AMR_TCHH_PAR_STR AMR_TCHH_PAR_STR, - AMR_TEXT "Half Rate\n" AMR_MODE_TEXT - AMR_TCHH_HELP_STR AMR_TCHH_HELP_STR AMR_TCHH_HELP_STR AMR_TCHH_HELP_STR) -{ - get_amr_from_arg(vty, 4, argv, 0); - return CMD_SUCCESS; -} - -DEFUN(cfg_bts_amr_hr_start_mode, cfg_bts_amr_hr_start_mode_cmd, - "amr tch-h start-mode (auto|1|2|3|4)", - AMR_TEXT "Half Rate\n" AMR_START_TEXT) -{ - get_amr_start_from_arg(vty, argv, 0); - return CMD_SUCCESS; -} - -DEFUN(cfg_bts_amr_hr_thres1, cfg_bts_amr_hr_thres1_cmd, - "amr tch-h threshold (ms|bts) <0-63>", - AMR_TEXT "Half Rate\n" AMR_TH_TEXT - AMR_TH_HELP_STR) -{ - get_amr_th_from_arg(vty, 2, argv, 0); - return CMD_SUCCESS; -} - -DEFUN(cfg_bts_amr_hr_thres2, cfg_bts_amr_hr_thres2_cmd, - "amr tch-h threshold (ms|bts) <0-63> <0-63>", - AMR_TEXT "Half Rate\n" AMR_TH_TEXT - AMR_TH_HELP_STR AMR_TH_HELP_STR) -{ - get_amr_th_from_arg(vty, 3, argv, 0); - return CMD_SUCCESS; -} - -DEFUN(cfg_bts_amr_hr_thres3, cfg_bts_amr_hr_thres3_cmd, - "amr tch-h threshold (ms|bts) <0-63> <0-63> <0-63>", - AMR_TEXT "Half Rate\n" AMR_TH_TEXT - AMR_TH_HELP_STR AMR_TH_HELP_STR AMR_TH_HELP_STR) -{ - get_amr_th_from_arg(vty, 4, argv, 0); - return CMD_SUCCESS; -} - -DEFUN(cfg_bts_amr_hr_hyst1, cfg_bts_amr_hr_hyst1_cmd, - "amr tch-h hysteresis (ms|bts) <0-15>", - AMR_TEXT "Half Rate\n" AMR_HY_TEXT - AMR_HY_HELP_STR) -{ - get_amr_hy_from_arg(vty, 2, argv, 0); - return CMD_SUCCESS; -} - -DEFUN(cfg_bts_amr_hr_hyst2, cfg_bts_amr_hr_hyst2_cmd, - "amr tch-h hysteresis (ms|bts) <0-15> <0-15>", - AMR_TEXT "Half Rate\n" AMR_HY_TEXT - AMR_HY_HELP_STR AMR_HY_HELP_STR) -{ - get_amr_hy_from_arg(vty, 3, argv, 0); - return CMD_SUCCESS; -} - -DEFUN(cfg_bts_amr_hr_hyst3, cfg_bts_amr_hr_hyst3_cmd, - "amr tch-h hysteresis (ms|bts) <0-15> <0-15> <0-15>", - AMR_TEXT "Half Rate\n" AMR_HY_TEXT - AMR_HY_HELP_STR AMR_HY_HELP_STR AMR_HY_HELP_STR) -{ - get_amr_hy_from_arg(vty, 4, argv, 0); - return CMD_SUCCESS; -} - -#define TRX_TEXT "Radio Transceiver\n" - -/* per TRX configuration */ -DEFUN(cfg_trx, - cfg_trx_cmd, - "trx <0-255>", - TRX_TEXT - "Select a TRX to configure") -{ - int trx_nr = atoi(argv[0]); - struct gsm_bts *bts = vty->index; - struct gsm_bts_trx *trx; - - if (trx_nr > bts->num_trx) { - vty_out(vty, "%% The next unused TRX number in this BTS is %u%s", - bts->num_trx, VTY_NEWLINE); - return CMD_WARNING; - } else if (trx_nr == bts->num_trx) { - /* we need to allocate a new one */ - trx = gsm_bts_trx_alloc(bts); - } else - trx = gsm_bts_trx_num(bts, trx_nr); - - if (!trx) - return CMD_WARNING; - - vty->index = trx; - vty->index_sub = &trx->description; - vty->node = TRX_NODE; - - return CMD_SUCCESS; -} - -DEFUN(cfg_trx_arfcn, - cfg_trx_arfcn_cmd, - "arfcn <0-1023>", - "Set the ARFCN for this TRX\n" - "Absolute Radio Frequency Channel Number\n") -{ - int arfcn = atoi(argv[0]); - struct gsm_bts_trx *trx = vty->index; - - /* FIXME: check if this ARFCN is supported by this TRX */ - - trx->arfcn = arfcn; - - /* FIXME: patch ARFCN into SYSTEM INFORMATION */ - /* FIXME: use OML layer to update the ARFCN */ - /* FIXME: use RSL layer to update SYSTEM INFORMATION */ - - return CMD_SUCCESS; -} - -DEFUN(cfg_trx_nominal_power, - cfg_trx_nominal_power_cmd, - "nominal power <0-100>", - "Nominal TRX RF Power in dBm\n" - "Nominal TRX RF Power in dBm\n" - "Nominal TRX RF Power in dBm\n") -{ - struct gsm_bts_trx *trx = vty->index; - - trx->nominal_power = atoi(argv[0]); - - return CMD_SUCCESS; -} - -DEFUN(cfg_trx_max_power_red, - cfg_trx_max_power_red_cmd, - "max_power_red <0-100>", - "Reduction of maximum BS RF Power (relative to nominal power)\n" - "Reduction of maximum BS RF Power in dB\n") -{ - int maxpwr_r = atoi(argv[0]); - struct gsm_bts_trx *trx = vty->index; - int upper_limit = 24; /* default 12.21 max power red. */ - - /* FIXME: check if our BTS type supports more than 12 */ - if (maxpwr_r < 0 || maxpwr_r > upper_limit) { - vty_out(vty, "%% Power %d dB is not in the valid range%s", - maxpwr_r, VTY_NEWLINE); - return CMD_WARNING; - } - if (maxpwr_r & 1) { - vty_out(vty, "%% Power %d dB is not an even value%s", - maxpwr_r, VTY_NEWLINE); - return CMD_WARNING; - } - - trx->max_power_red = maxpwr_r; - - /* FIXME: make sure we update this using OML */ - - return CMD_SUCCESS; -} - -DEFUN(cfg_trx_rsl_e1, - cfg_trx_rsl_e1_cmd, - "rsl e1 line E1_LINE timeslot <1-31> sub-slot (0|1|2|3|full)", - "RSL Parameters\n" - "E1/T1 interface to be used for RSL\n" - "E1/T1 interface to be used for RSL\n" - "E1/T1 Line Number to be used for RSL\n" - "E1/T1 Timeslot to be used for RSL\n" - "E1/T1 Timeslot to be used for RSL\n" - "E1/T1 Sub-slot to be used for RSL\n" - "E1/T1 Sub-slot 0 is to be used for RSL\n" - "E1/T1 Sub-slot 1 is to be used for RSL\n" - "E1/T1 Sub-slot 2 is to be used for RSL\n" - "E1/T1 Sub-slot 3 is to be used for RSL\n" - "E1/T1 full timeslot is to be used for RSL\n") -{ - struct gsm_bts_trx *trx = vty->index; - - parse_e1_link(&trx->rsl_e1_link, argv[0], argv[1], argv[2]); - - return CMD_SUCCESS; -} - -DEFUN(cfg_trx_rsl_e1_tei, - cfg_trx_rsl_e1_tei_cmd, - "rsl e1 tei <0-63>", - "RSL Parameters\n" - "Set the TEI to be used for RSL\n" - "Set the TEI to be used for RSL\n" - "TEI to be used for RSL\n") -{ - struct gsm_bts_trx *trx = vty->index; - - trx->rsl_tei = atoi(argv[0]); - - return CMD_SUCCESS; -} - -DEFUN(cfg_trx_rf_locked, - cfg_trx_rf_locked_cmd, - "rf_locked (0|1)", - "Set or unset the RF Locking (Turn off RF of the TRX)\n" - "TRX is NOT RF locked (active)\n" - "TRX is RF locked (turned off)\n") -{ - int locked = atoi(argv[0]); - struct gsm_bts_trx *trx = vty->index; - - gsm_trx_lock_rf(trx, locked); - return CMD_SUCCESS; -} - -/* per TS configuration */ -DEFUN(cfg_ts, - cfg_ts_cmd, - "timeslot <0-7>", - "Select a Timeslot to configure\n" - "Timeslot number\n") -{ - int ts_nr = atoi(argv[0]); - struct gsm_bts_trx *trx = vty->index; - struct gsm_bts_trx_ts *ts; - - if (ts_nr >= TRX_NR_TS) { - vty_out(vty, "%% A GSM TRX only has %u Timeslots per TRX%s", - TRX_NR_TS, VTY_NEWLINE); - return CMD_WARNING; - } - - ts = &trx->ts[ts_nr]; - - vty->index = ts; - vty->node = TS_NODE; - - return CMD_SUCCESS; -} - -DEFUN(cfg_ts_pchan, - cfg_ts_pchan_cmd, - "phys_chan_config PCHAN", /* dynamically generated! */ - "Physical Channel configuration (TCH/SDCCH/...)\n" "Physical Channel\n") -{ - struct gsm_bts_trx_ts *ts = vty->index; - int pchanc; - - pchanc = gsm_pchan_parse(argv[0]); - if (pchanc < 0) - return CMD_WARNING; - - ts->pchan = pchanc; - - return CMD_SUCCESS; -} - -/* used for backwards compatibility with old config files that still - * have uppercase pchan type names */ -DEFUN_HIDDEN(cfg_ts_pchan_compat, - cfg_ts_pchan_compat_cmd, - "phys_chan_config PCHAN", - "Physical Channel configuration (TCH/SDCCH/...)\n" "Physical Channel\n") -{ - struct gsm_bts_trx_ts *ts = vty->index; - int pchanc; - - pchanc = gsm_pchan_parse(argv[0]); - if (pchanc < 0) - return CMD_WARNING; - - ts->pchan = pchanc; - - return CMD_SUCCESS; -} - - - -DEFUN(cfg_ts_tsc, - cfg_ts_tsc_cmd, - "training_sequence_code <0-7>", - "Training Sequence Code of the Timeslot\n" "TSC\n") -{ - struct gsm_bts_trx_ts *ts = vty->index; - - if (!gsm_btsmodel_has_feature(ts->trx->bts->model, BTS_FEAT_MULTI_TSC)) { - vty_out(vty, "%% This BTS does not support a TSC != BCC, " - "falling back to BCC%s", VTY_NEWLINE); - ts->tsc = -1; - return CMD_WARNING; - } - - ts->tsc = atoi(argv[0]); - - return CMD_SUCCESS; -} - -#define HOPPING_STR "Configure frequency hopping\n" - -DEFUN(cfg_ts_hopping, - cfg_ts_hopping_cmd, - "hopping enabled (0|1)", - HOPPING_STR "Enable or disable frequency hopping\n" - "Disable frequency hopping\n" "Enable frequency hopping\n") -{ - struct gsm_bts_trx_ts *ts = vty->index; - int enabled = atoi(argv[0]); - - if (enabled && !gsm_btsmodel_has_feature(ts->trx->bts->model, BTS_FEAT_HOPPING)) { - vty_out(vty, "BTS model does not support hopping%s", - VTY_NEWLINE); - return CMD_WARNING; - } - - ts->hopping.enabled = enabled; - - return CMD_SUCCESS; -} - -DEFUN(cfg_ts_hsn, - cfg_ts_hsn_cmd, - "hopping sequence-number <0-63>", - HOPPING_STR - "Which hopping sequence to use for this channel\n" - "Hopping Sequence Number (HSN)\n") -{ - struct gsm_bts_trx_ts *ts = vty->index; - - ts->hopping.hsn = atoi(argv[0]); - - return CMD_SUCCESS; -} - -DEFUN(cfg_ts_maio, - cfg_ts_maio_cmd, - "hopping maio <0-63>", - HOPPING_STR - "Which hopping MAIO to use for this channel\n" - "Mobile Allocation Index Offset (MAIO)\n") -{ - struct gsm_bts_trx_ts *ts = vty->index; - - ts->hopping.maio = atoi(argv[0]); - - return CMD_SUCCESS; -} - -DEFUN(cfg_ts_arfcn_add, - cfg_ts_arfcn_add_cmd, - "hopping arfcn add <0-1023>", - HOPPING_STR "Configure hopping ARFCN list\n" - "Add an entry to the hopping ARFCN list\n" "ARFCN\n") -{ - struct gsm_bts_trx_ts *ts = vty->index; - int arfcn = atoi(argv[0]); - - bitvec_set_bit_pos(&ts->hopping.arfcns, arfcn, 1); - - return CMD_SUCCESS; -} - -DEFUN(cfg_ts_arfcn_del, - cfg_ts_arfcn_del_cmd, - "hopping arfcn del <0-1023>", - HOPPING_STR "Configure hopping ARFCN list\n" - "Delete an entry to the hopping ARFCN list\n" "ARFCN\n") -{ - struct gsm_bts_trx_ts *ts = vty->index; - int arfcn = atoi(argv[0]); - - bitvec_set_bit_pos(&ts->hopping.arfcns, arfcn, 0); - - return CMD_SUCCESS; -} - -DEFUN(cfg_ts_e1_subslot, - cfg_ts_e1_subslot_cmd, - "e1 line E1_LINE timeslot <1-31> sub-slot (0|1|2|3|full)", - "E1/T1 channel connected to this on-air timeslot\n" - "E1/T1 channel connected to this on-air timeslot\n" - "E1/T1 line connected to this on-air timeslot\n" - "E1/T1 timeslot connected to this on-air timeslot\n" - "E1/T1 timeslot connected to this on-air timeslot\n" - "E1/T1 sub-slot connected to this on-air timeslot\n" - "E1/T1 sub-slot 0 connected to this on-air timeslot\n" - "E1/T1 sub-slot 1 connected to this on-air timeslot\n" - "E1/T1 sub-slot 2 connected to this on-air timeslot\n" - "E1/T1 sub-slot 3 connected to this on-air timeslot\n" - "Full E1/T1 timeslot connected to this on-air timeslot\n") -{ - struct gsm_bts_trx_ts *ts = vty->index; - - parse_e1_link(&ts->e1_link, argv[0], argv[1], argv[2]); - - return CMD_SUCCESS; -} - -void openbsc_vty_print_statistics(struct vty *vty, struct gsm_network *net) -{ - vty_out(vty, "Channel Requests : %lu total, %lu no channel%s", - net->bsc_ctrs->ctr[BSC_CTR_CHREQ_TOTAL].current, - net->bsc_ctrs->ctr[BSC_CTR_CHREQ_NO_CHANNEL].current, - VTY_NEWLINE); - vty_out(vty, "Channel Failures : %lu rf_failures, %lu rll failures%s", - net->bsc_ctrs->ctr[BSC_CTR_CHAN_RF_FAIL].current, - net->bsc_ctrs->ctr[BSC_CTR_CHAN_RLL_ERR].current, - VTY_NEWLINE); - vty_out(vty, "Paging : %lu attempted, %lu complete, %lu expired%s", - net->bsc_ctrs->ctr[BSC_CTR_PAGING_ATTEMPTED].current, - net->bsc_ctrs->ctr[BSC_CTR_PAGING_COMPLETED].current, - net->bsc_ctrs->ctr[BSC_CTR_PAGING_EXPIRED].current, - VTY_NEWLINE); - vty_out(vty, "BTS failures : %lu OML, %lu RSL%s", - net->bsc_ctrs->ctr[BSC_CTR_BTS_OML_FAIL].current, - net->bsc_ctrs->ctr[BSC_CTR_BTS_RSL_FAIL].current, - VTY_NEWLINE); -} - -DEFUN(drop_bts, - drop_bts_cmd, - "drop bts connection <0-65535> (oml|rsl)", - "Debug/Simulation command to drop Abis/IP BTS\n" - "Debug/Simulation command to drop Abis/IP BTS\n" - "Debug/Simulation command to drop Abis/IP BTS\n" - "BTS NR\n" "Drop OML Connection\n" "Drop RSL Connection\n") -{ - struct gsm_network *gsmnet; - struct gsm_bts_trx *trx; - struct gsm_bts *bts; - unsigned int bts_nr; - - gsmnet = gsmnet_from_vty(vty); - - bts_nr = atoi(argv[0]); - if (bts_nr >= gsmnet->num_bts) { - vty_out(vty, "BTS number must be between 0 and %d. It was %d.%s", - gsmnet->num_bts, bts_nr, VTY_NEWLINE); - return CMD_WARNING; - } - - bts = gsm_bts_num(gsmnet, bts_nr); - if (!bts) { - vty_out(vty, "BTS Nr. %d could not be found.%s", bts_nr, VTY_NEWLINE); - return CMD_WARNING; - } - - if (!is_ipaccess_bts(bts)) { - vty_out(vty, "This command only works for ipaccess.%s", VTY_NEWLINE); - return CMD_WARNING; - } - - - /* close all connections */ - if (strcmp(argv[1], "oml") == 0) { - ipaccess_drop_oml(bts); - } else if (strcmp(argv[1], "rsl") == 0) { - /* close all rsl connections */ - llist_for_each_entry(trx, &bts->trx_list, list) { - ipaccess_drop_rsl(trx); - } - } else { - vty_out(vty, "Argument must be 'oml# or 'rsl'.%s", VTY_NEWLINE); - return CMD_WARNING; - } - - return CMD_SUCCESS; -} - -DEFUN(restart_bts, restart_bts_cmd, - "restart-bts <0-65535>", - "Restart ip.access nanoBTS through OML\n" - "BTS Number\n") -{ - struct gsm_network *gsmnet; - struct gsm_bts_trx *trx; - struct gsm_bts *bts; - unsigned int bts_nr; - - gsmnet = gsmnet_from_vty(vty); - - bts_nr = atoi(argv[0]); - if (bts_nr >= gsmnet->num_bts) { - vty_out(vty, "BTS number must be between 0 and %d. It was %d.%s", - gsmnet->num_bts, bts_nr, VTY_NEWLINE); - return CMD_WARNING; - } - - bts = gsm_bts_num(gsmnet, bts_nr); - if (!bts) { - vty_out(vty, "BTS Nr. %d could not be found.%s", bts_nr, VTY_NEWLINE); - return CMD_WARNING; - } - - if (!is_ipaccess_bts(bts) || is_sysmobts_v2(bts)) { - vty_out(vty, "This command only works for ipaccess nanoBTS.%s", - VTY_NEWLINE); - return CMD_WARNING; - } - - /* go from last TRX to c0 */ - llist_for_each_entry_reverse(trx, &bts->trx_list, list) - abis_nm_ipaccess_restart(trx); - - return CMD_SUCCESS; -} - -DEFUN(smscb_cmd, smscb_cmd_cmd, - "bts <0-255> smscb-command <1-4> HEXSTRING", - "BTS related commands\n" "BTS Number\n" - "SMS Cell Broadcast\n" "Last Valid Block\n" - "Hex Encoded SMSCB message (up to 88 octets)\n") -{ - struct gsm_bts *bts; - int bts_nr = atoi(argv[0]); - int last_block = atoi(argv[1]); - struct rsl_ie_cb_cmd_type cb_cmd; - uint8_t buf[88]; - int rc; - - bts = gsm_bts_num(gsmnet_from_vty(vty), bts_nr); - if (!bts) { - vty_out(vty, "%% No such BTS (%d)%s", bts_nr, VTY_NEWLINE); - return CMD_WARNING; - } - rc = osmo_hexparse(argv[2], buf, sizeof(buf)); - if (rc < 0 || rc > sizeof(buf)) { - vty_out(vty, "Error parsing HEXSTRING%s", VTY_NEWLINE); - return CMD_WARNING; - } - - cb_cmd.spare = 0; - cb_cmd.def_bcast = 0; - cb_cmd.command = RSL_CB_CMD_TYPE_NORMAL; - - switch (last_block) { - case 1: - cb_cmd.last_block = RSL_CB_CMD_LASTBLOCK_1; - break; - case 2: - cb_cmd.last_block = RSL_CB_CMD_LASTBLOCK_2; - break; - case 3: - cb_cmd.last_block = RSL_CB_CMD_LASTBLOCK_3; - break; - case 4: - cb_cmd.last_block = RSL_CB_CMD_LASTBLOCK_4; - break; - } - - rsl_sms_cb_command(bts, RSL_CHAN_SDCCH4_ACCH, cb_cmd, buf, rc); - - return CMD_SUCCESS; -} - -/* resolve a gsm_bts_trx_ts basd on the given numeric identifiers */ -static struct gsm_bts_trx_ts *vty_get_ts(struct vty *vty, const char *bts_str, const char *trx_str, - const char *ts_str) -{ - int bts_nr = atoi(bts_str); - int trx_nr = atoi(trx_str); - int ts_nr = atoi(ts_str); - struct gsm_bts *bts; - struct gsm_bts_trx *trx; - struct gsm_bts_trx_ts *ts; - - bts = gsm_bts_num(gsmnet_from_vty(vty), bts_nr); - if (!bts) { - vty_out(vty, "%% No such BTS (%d)%s", bts_nr, VTY_NEWLINE); - return NULL; - } - - trx = gsm_bts_trx_num(bts, trx_nr); - if (!trx) { - vty_out(vty, "%% No such TRX (%d)%s", trx_nr, VTY_NEWLINE); - return NULL; - } - - ts = &trx->ts[ts_nr]; - - return ts; -} - -DEFUN(pdch_act, pdch_act_cmd, - "bts <0-255> trx <0-255> timeslot <0-7> pdch (activate|deactivate)", - "BTS related commands\n" "BTS Number\n" "Transceiver\n" "Transceiver Number\n" - "TRX Timeslot\n" "Timeslot Number\n" "Packet Data Channel\n" - "Activate Dynamic PDCH/TCH (-> PDCH mode)\n" - "Deactivate Dynamic PDCH/TCH (-> TCH mode)\n") -{ - struct gsm_bts_trx_ts *ts; - int activate; - - ts = vty_get_ts(vty, argv[0], argv[1], argv[2]); - if (!ts) - return CMD_WARNING; - - if (!is_ipaccess_bts(ts->trx->bts)) { - vty_out(vty, "%% This command only works for ipaccess BTS%s", - VTY_NEWLINE); - return CMD_WARNING; - } - - if (ts->pchan != GSM_PCHAN_TCH_F_PDCH) { - vty_out(vty, "%% Timeslot %u is not in dynamic TCH_F/PDCH " - "mode%s", ts->nr, VTY_NEWLINE); - return CMD_WARNING; - } - - if (!strcmp(argv[3], "activate")) - activate = 1; - else - activate = 0; - - rsl_ipacc_pdch_activate(ts, activate); - - return CMD_SUCCESS; - -} - -/* determine the logical channel type based on the physical channel type */ -static int lchan_type_by_pchan(enum gsm_phys_chan_config pchan) -{ - switch (pchan) { - case GSM_PCHAN_TCH_F: - return GSM_LCHAN_TCH_F; - case GSM_PCHAN_TCH_H: - return GSM_LCHAN_TCH_H; - case GSM_PCHAN_SDCCH8_SACCH8C: - case GSM_PCHAN_SDCCH8_SACCH8C_CBCH: - case GSM_PCHAN_CCCH_SDCCH4: - case GSM_PCHAN_CCCH_SDCCH4_CBCH: - return GSM_LCHAN_SDCCH; - default: - return -1; - } -} - -/* configure the lchan for a single AMR mode (as specified) */ -static int lchan_set_single_amr_mode(struct gsm_lchan *lchan, uint8_t amr_mode) -{ - struct amr_multirate_conf mr; - struct gsm48_multi_rate_conf *mr_conf; - mr_conf = (struct gsm48_multi_rate_conf *) &mr.gsm48_ie; - - if (amr_mode > 7) - return -1; - - memset(&mr, 0, sizeof(mr)); - mr_conf->ver = 1; - /* bit-mask of supported modes, only one bit is set. Reflects - * Figure 10.5.2.47a where there are no thershold and only a - * single mode */ - mr.gsm48_ie[1] = 1 << amr_mode; - - mr.ms_mode[0].mode = amr_mode; - mr.bts_mode[0].mode = amr_mode; - - /* encode this configuration into the lchan for both uplink and - * downlink direction */ - gsm48_multirate_config(lchan->mr_ms_lv, &mr, mr.ms_mode); - gsm48_multirate_config(lchan->mr_bts_lv, &mr, mr.bts_mode); - - return 0; -} - -/* Debug/Measurement command to activate a given logical channel - * manually in a given mode/codec. This is useful for receiver - * performance testing (FER/RBER/...) */ -DEFUN(lchan_act, lchan_act_cmd, - "bts <0-255> trx <0-255> timeslot <0-7> sub-slot <0-7> (activate|deactivate) (hr|fr|efr|amr) [<0-7>]", - "BTS related commands\n" "BTS Number\n" "Transceiver\n" "Transceiver Number\n" - "TRX Timeslot\n" "Timeslot Number\n" "Sub-Slot Number\n" "Sub-Slot Number\n" - "Manual Channel Activation (e.g. for BER test)\n" - "Manual Channel Deactivation (e.g. for BER test)\n" - "Half-Rate v1\n" "Full-Rate\n" "Enhanced Full Rate\n" "Adaptive Multi-Rate\n" "AMR Mode\n") -{ - struct gsm_bts_trx_ts *ts; - struct gsm_lchan *lchan; - int ss_nr = atoi(argv[3]); - const char *act_str = argv[4]; - const char *codec_str = argv[5]; - int activate; - - ts = vty_get_ts(vty, argv[0], argv[1], argv[2]); - if (!ts) - return CMD_WARNING; - - lchan = &ts->lchan[ss_nr]; - - if (!strcmp(act_str, "activate")) - activate = 1; - else - activate = 0; - - if (ss_nr >= ts_subslots(ts)) { - vty_out(vty, "%% subslot %d >= permitted %d for physical channel %s%s", - ss_nr, ts_subslots(ts), gsm_pchan_name(ts->pchan), VTY_NEWLINE); - return CMD_WARNING; - } - - if (activate) { - int lchan_t; - if (lchan->state != LCHAN_S_NONE) { - vty_out(vty, "%% Cannot activate: Channel busy!%s", VTY_NEWLINE); - return CMD_WARNING; - } - lchan_t = lchan_type_by_pchan(ts->pchan); - if (lchan_t < 0) - return CMD_WARNING; - /* configure the lchan */ - lchan->type = lchan_t; - lchan->rsl_cmode = RSL_CMOD_SPD_SPEECH; - if (!strcmp(codec_str, "hr") || !strcmp(codec_str, "fr")) - lchan->tch_mode = GSM48_CMODE_SPEECH_V1; - else if (!strcmp(codec_str, "efr")) - lchan->tch_mode = GSM48_CMODE_SPEECH_EFR; - else if (!strcmp(codec_str, "amr")) { - int amr_mode; - if (argc < 7) { - vty_out(vty, "%% AMR requires specification of AMR mode%s", VTY_NEWLINE); - return CMD_WARNING; - } - amr_mode = atoi(argv[6]); - lchan->tch_mode = GSM48_CMODE_SPEECH_AMR; - lchan_set_single_amr_mode(lchan, amr_mode); - } - vty_out(vty, "%% activating lchan %s%s", gsm_lchan_name(lchan), VTY_NEWLINE); - rsl_chan_activate_lchan(lchan, RSL_ACT_TYPE_INITIAL, 0); - rsl_ipacc_crcx(lchan); - } else { - rsl_direct_rf_release(lchan); - } - - return CMD_SUCCESS; -} - -DEFUN(lchan_mdcx, lchan_mdcx_cmd, - "bts <0-255> trx <0-255> timeslot <0-7> sub-slot <0-7> mdcx A.B.C.D <0-65535>", - "BTS related commands\n" "BTS Number\n" "Transceiver\n" "Transceiver Number\n" - "TRX Timeslot\n" "Timeslot Number\n" "Sub-Slot\n" "Sub-Slot Number\n" - "Modify RTP Connection\n" "MGW IP Address\n" "MGW UDP Port\n") -{ - struct gsm_bts_trx_ts *ts; - struct gsm_lchan *lchan; - int ss_nr = atoi(argv[3]); - int port = atoi(argv[5]); - struct in_addr ia; - inet_aton(argv[4], &ia); - - ts = vty_get_ts(vty, argv[0], argv[1], argv[2]); - if (!ts) - return CMD_WARNING; - - lchan = &ts->lchan[ss_nr]; - - if (ss_nr >= ts_subslots(ts)) { - vty_out(vty, "%% subslot %d >= permitted %d for physical channel %s%s", - ss_nr, ts_subslots(ts), gsm_pchan_name(ts->pchan), VTY_NEWLINE); - return CMD_WARNING; - } - - vty_out(vty, "%% connecting RTP of %s to %s:%u%s", gsm_lchan_name(lchan), - inet_ntoa(ia), port, VTY_NEWLINE); - rsl_ipacc_mdcx(lchan, ntohl(ia.s_addr), port, 0); - return CMD_SUCCESS; -} -extern int bsc_vty_init_extra(void); - -int bsc_vty_init(struct gsm_network *network) -{ - cfg_ts_pchan_cmd.string = - vty_cmd_string_from_valstr(tall_bsc_ctx, - gsm_pchant_names, - "phys_chan_config (", "|", ")", - VTY_DO_LOWER); - cfg_ts_pchan_cmd.doc = - vty_cmd_string_from_valstr(tall_bsc_ctx, - gsm_pchant_descs, - "Physical Channel Combination\n", - "\n", "", 0); - - cfg_bts_type_cmd.string = - vty_cmd_string_from_valstr(tall_bsc_ctx, - bts_type_names, - "type (", "|", ")", - VTY_DO_LOWER); - cfg_bts_type_cmd.doc = - vty_cmd_string_from_valstr(tall_bsc_ctx, - bts_type_descs, - "BTS Vendor/Type\n", - "\n", "", 0); - - common_cs_vty_init(network, config_write_net); - - install_element_ve(&bsc_show_net_cmd); - install_element_ve(&show_bts_cmd); - install_element_ve(&show_trx_cmd); - install_element_ve(&show_ts_cmd); - install_element_ve(&show_lchan_cmd); - install_element_ve(&show_lchan_summary_cmd); - - install_element_ve(&show_paging_cmd); - install_element_ve(&show_paging_group_cmd); - - logging_vty_add_cmds(NULL); - osmo_stats_vty_add_cmds(); - - install_element(GSMNET_NODE, &cfg_net_neci_cmd); - install_element(GSMNET_NODE, &cfg_net_handover_cmd); - install_element(GSMNET_NODE, &cfg_net_ho_win_rxlev_avg_cmd); - install_element(GSMNET_NODE, &cfg_net_ho_win_rxqual_avg_cmd); - install_element(GSMNET_NODE, &cfg_net_ho_win_rxlev_avg_neigh_cmd); - install_element(GSMNET_NODE, &cfg_net_ho_pwr_interval_cmd); - install_element(GSMNET_NODE, &cfg_net_ho_pwr_hysteresis_cmd); - install_element(GSMNET_NODE, &cfg_net_ho_max_distance_cmd); - install_element(GSMNET_NODE, &cfg_net_T3101_cmd); - install_element(GSMNET_NODE, &cfg_net_T3103_cmd); - install_element(GSMNET_NODE, &cfg_net_T3105_cmd); - install_element(GSMNET_NODE, &cfg_net_T3107_cmd); - install_element(GSMNET_NODE, &cfg_net_T3109_cmd); - install_element(GSMNET_NODE, &cfg_net_T3111_cmd); - install_element(GSMNET_NODE, &cfg_net_T3113_cmd); - install_element(GSMNET_NODE, &cfg_net_T3115_cmd); - install_element(GSMNET_NODE, &cfg_net_T3117_cmd); - install_element(GSMNET_NODE, &cfg_net_T3119_cmd); - install_element(GSMNET_NODE, &cfg_net_T3122_cmd); - install_element(GSMNET_NODE, &cfg_net_T3141_cmd); - install_element(GSMNET_NODE, &cfg_net_dtx_cmd); - install_element(GSMNET_NODE, &cfg_net_pag_any_tch_cmd); - - install_element(GSMNET_NODE, &cfg_bts_cmd); - install_node(&bts_node, config_write_bts); - vty_install_default(BTS_NODE); - install_element(BTS_NODE, &cfg_bts_type_cmd); - install_element(BTS_NODE, &cfg_description_cmd); - install_element(BTS_NODE, &cfg_no_description_cmd); - install_element(BTS_NODE, &cfg_bts_band_cmd); - install_element(BTS_NODE, &cfg_bts_ci_cmd); - install_element(BTS_NODE, &cfg_bts_dtxu_cmd); - install_element(BTS_NODE, &cfg_bts_dtxd_cmd); - install_element(BTS_NODE, &cfg_bts_no_dtxu_cmd); - install_element(BTS_NODE, &cfg_bts_no_dtxd_cmd); - install_element(BTS_NODE, &cfg_bts_lac_cmd); - install_element(BTS_NODE, &cfg_bts_tsc_cmd); - install_element(BTS_NODE, &cfg_bts_bsic_cmd); - install_element(BTS_NODE, &cfg_bts_unit_id_cmd); - install_element(BTS_NODE, &cfg_bts_rsl_ip_cmd); - install_element(BTS_NODE, &cfg_bts_nokia_site_skip_reset_cmd); - install_element(BTS_NODE, &cfg_bts_nokia_site_no_loc_rel_cnf_cmd); - install_element(BTS_NODE, &cfg_bts_nokia_site_bts_reset_timer_cnf_cmd); - install_element(BTS_NODE, &cfg_bts_stream_id_cmd); - install_element(BTS_NODE, &cfg_bts_oml_e1_cmd); - install_element(BTS_NODE, &cfg_bts_oml_e1_tei_cmd); - install_element(BTS_NODE, &cfg_bts_challoc_cmd); - install_element(BTS_NODE, &cfg_bts_rach_tx_integer_cmd); - install_element(BTS_NODE, &cfg_bts_rach_max_trans_cmd); - install_element(BTS_NODE, &cfg_bts_chan_desc_att_cmd); - install_element(BTS_NODE, &cfg_bts_chan_desc_bs_pa_mfrms_cmd); - install_element(BTS_NODE, &cfg_bts_chan_desc_bs_ag_blks_res_cmd); - install_element(BTS_NODE, &cfg_bts_rach_nm_b_thresh_cmd); - install_element(BTS_NODE, &cfg_bts_rach_nm_ldavg_cmd); - install_element(BTS_NODE, &cfg_bts_cell_barred_cmd); - install_element(BTS_NODE, &cfg_bts_rach_ec_allowed_cmd); - install_element(BTS_NODE, &cfg_bts_rach_ac_class_cmd); - install_element(BTS_NODE, &cfg_bts_ms_max_power_cmd); - install_element(BTS_NODE, &cfg_bts_per_loc_upd_cmd); - install_element(BTS_NODE, &cfg_bts_no_per_loc_upd_cmd); - install_element(BTS_NODE, &cfg_bts_cell_resel_hyst_cmd); - install_element(BTS_NODE, &cfg_bts_rxlev_acc_min_cmd); - install_element(BTS_NODE, &cfg_bts_cell_bar_qualify_cmd); - install_element(BTS_NODE, &cfg_bts_cell_resel_ofs_cmd); - install_element(BTS_NODE, &cfg_bts_temp_ofs_cmd); - install_element(BTS_NODE, &cfg_bts_temp_ofs_inf_cmd); - install_element(BTS_NODE, &cfg_bts_penalty_time_cmd); - install_element(BTS_NODE, &cfg_bts_penalty_time_rsvd_cmd); - install_element(BTS_NODE, &cfg_bts_radio_link_timeout_cmd); - install_element(BTS_NODE, &cfg_bts_radio_link_timeout_inf_cmd); - install_element(BTS_NODE, &cfg_bts_gprs_mode_cmd); - install_element(BTS_NODE, &cfg_bts_gprs_11bit_rach_support_for_egprs_cmd); - install_element(BTS_NODE, &cfg_bts_gprs_ns_timer_cmd); - install_element(BTS_NODE, &cfg_bts_gprs_rac_cmd); - install_element(BTS_NODE, &cfg_bts_gprs_net_ctrl_ord_cmd); - install_element(BTS_NODE, &cfg_bts_gprs_ctrl_ack_cmd); - install_element(BTS_NODE, &cfg_no_bts_gprs_ctrl_ack_cmd); - install_element(BTS_NODE, &cfg_bts_gprs_bvci_cmd); - install_element(BTS_NODE, &cfg_bts_gprs_cell_timer_cmd); - install_element(BTS_NODE, &cfg_bts_gprs_nsei_cmd); - install_element(BTS_NODE, &cfg_bts_gprs_nsvci_cmd); - install_element(BTS_NODE, &cfg_bts_gprs_nsvc_lport_cmd); - install_element(BTS_NODE, &cfg_bts_gprs_nsvc_rport_cmd); - install_element(BTS_NODE, &cfg_bts_gprs_nsvc_rip_cmd); - install_element(BTS_NODE, &cfg_bts_pag_free_cmd); - install_element(BTS_NODE, &cfg_bts_si_mode_cmd); - install_element(BTS_NODE, &cfg_bts_si_static_cmd); - install_element(BTS_NODE, &cfg_bts_early_cm_cmd); - install_element(BTS_NODE, &cfg_bts_neigh_mode_cmd); - install_element(BTS_NODE, &cfg_bts_neigh_cmd); - install_element(BTS_NODE, &cfg_bts_si5_neigh_cmd); - install_element(BTS_NODE, &cfg_bts_si2quater_neigh_add_cmd); - install_element(BTS_NODE, &cfg_bts_si2quater_neigh_del_cmd); - install_element(BTS_NODE, &cfg_bts_si2quater_uarfcn_add_cmd); - install_element(BTS_NODE, &cfg_bts_si2quater_uarfcn_del_cmd); - install_element(BTS_NODE, &cfg_bts_excl_rf_lock_cmd); - install_element(BTS_NODE, &cfg_bts_no_excl_rf_lock_cmd); - install_element(BTS_NODE, &cfg_bts_force_comb_si_cmd); - install_element(BTS_NODE, &cfg_bts_no_force_comb_si_cmd); - install_element(BTS_NODE, &cfg_bts_codec0_cmd); - install_element(BTS_NODE, &cfg_bts_codec1_cmd); - install_element(BTS_NODE, &cfg_bts_codec2_cmd); - install_element(BTS_NODE, &cfg_bts_codec3_cmd); - install_element(BTS_NODE, &cfg_bts_codec4_cmd); - install_element(BTS_NODE, &cfg_bts_depends_on_cmd); - install_element(BTS_NODE, &cfg_bts_no_depends_on_cmd); - install_element(BTS_NODE, &cfg_bts_amr_fr_modes1_cmd); - install_element(BTS_NODE, &cfg_bts_amr_fr_modes2_cmd); - install_element(BTS_NODE, &cfg_bts_amr_fr_modes3_cmd); - install_element(BTS_NODE, &cfg_bts_amr_fr_modes4_cmd); - install_element(BTS_NODE, &cfg_bts_amr_fr_thres1_cmd); - install_element(BTS_NODE, &cfg_bts_amr_fr_thres2_cmd); - install_element(BTS_NODE, &cfg_bts_amr_fr_thres3_cmd); - install_element(BTS_NODE, &cfg_bts_amr_fr_hyst1_cmd); - install_element(BTS_NODE, &cfg_bts_amr_fr_hyst2_cmd); - install_element(BTS_NODE, &cfg_bts_amr_fr_hyst3_cmd); - install_element(BTS_NODE, &cfg_bts_amr_fr_start_mode_cmd); - install_element(BTS_NODE, &cfg_bts_amr_hr_modes1_cmd); - install_element(BTS_NODE, &cfg_bts_amr_hr_modes2_cmd); - install_element(BTS_NODE, &cfg_bts_amr_hr_modes3_cmd); - install_element(BTS_NODE, &cfg_bts_amr_hr_modes4_cmd); - install_element(BTS_NODE, &cfg_bts_amr_hr_thres1_cmd); - install_element(BTS_NODE, &cfg_bts_amr_hr_thres2_cmd); - install_element(BTS_NODE, &cfg_bts_amr_hr_thres3_cmd); - install_element(BTS_NODE, &cfg_bts_amr_hr_hyst1_cmd); - install_element(BTS_NODE, &cfg_bts_amr_hr_hyst2_cmd); - install_element(BTS_NODE, &cfg_bts_amr_hr_hyst3_cmd); - install_element(BTS_NODE, &cfg_bts_amr_hr_start_mode_cmd); - install_element(BTS_NODE, &cfg_bts_pcu_sock_cmd); - - install_element(BTS_NODE, &cfg_trx_cmd); - install_node(&trx_node, dummy_config_write); - vty_install_default(TRX_NODE); - install_element(TRX_NODE, &cfg_trx_arfcn_cmd); - install_element(TRX_NODE, &cfg_description_cmd); - install_element(TRX_NODE, &cfg_no_description_cmd); - install_element(TRX_NODE, &cfg_trx_nominal_power_cmd); - install_element(TRX_NODE, &cfg_trx_max_power_red_cmd); - install_element(TRX_NODE, &cfg_trx_rsl_e1_cmd); - install_element(TRX_NODE, &cfg_trx_rsl_e1_tei_cmd); - install_element(TRX_NODE, &cfg_trx_rf_locked_cmd); - - install_element(TRX_NODE, &cfg_ts_cmd); - install_node(&ts_node, dummy_config_write); - vty_install_default(TS_NODE); - install_element(TS_NODE, &cfg_ts_pchan_cmd); - install_element(TS_NODE, &cfg_ts_pchan_compat_cmd); - install_element(TS_NODE, &cfg_ts_tsc_cmd); - install_element(TS_NODE, &cfg_ts_hopping_cmd); - install_element(TS_NODE, &cfg_ts_hsn_cmd); - install_element(TS_NODE, &cfg_ts_maio_cmd); - install_element(TS_NODE, &cfg_ts_arfcn_add_cmd); - install_element(TS_NODE, &cfg_ts_arfcn_del_cmd); - install_element(TS_NODE, &cfg_ts_e1_subslot_cmd); - - install_element(ENABLE_NODE, &drop_bts_cmd); - install_element(ENABLE_NODE, &restart_bts_cmd); - install_element(ENABLE_NODE, &pdch_act_cmd); - install_element(ENABLE_NODE, &lchan_act_cmd); - install_element(ENABLE_NODE, &lchan_mdcx_cmd); - install_element(ENABLE_NODE, &smscb_cmd_cmd); - - abis_nm_vty_init(); - abis_om2k_vty_init(); - e1inp_vty_init(); - osmo_fsm_vty_add_cmds(); - - bsc_vty_init_extra(); - - return 0; -} diff --git a/openbsc/src/libbsc/bts_ericsson_rbs2000.c b/openbsc/src/libbsc/bts_ericsson_rbs2000.c deleted file mode 100644 index 99da4e75..00000000 --- a/openbsc/src/libbsc/bts_ericsson_rbs2000.c +++ /dev/null @@ -1,204 +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 - -static void bootstrap_om_bts(struct gsm_bts *bts) -{ - LOGP(DNM, LOGL_NOTICE, "bootstrapping OML for BTS %u\n", bts->nr); - - /* FIXME: this is global init, not bootstrapping */ - abis_om2k_bts_init(bts); - abis_om2k_trx_init(bts->c0); - - /* TODO: Should we wait for a Failure report? */ - om2k_bts_fsm_start(bts); -} - -static void bootstrap_om_trx(struct gsm_bts_trx *trx) -{ - LOGP(DNM, LOGL_NOTICE, "bootstrapping OML for TRX %u/%u\n", - trx->bts->nr, trx->nr); - /* FIXME */ -} - -static int shutdown_om(struct gsm_bts *bts) -{ - /* FIXME */ - return 0; -} - - -/* Tell LAPD to start start the SAP (send SABM requests) for all signalling - * timeslots in this line */ -static void start_sabm_in_line(struct e1inp_line *line, int start) -{ - struct e1inp_sign_link *link; - int i; - - for (i = 0; i < ARRAY_SIZE(line->ts); i++) { - struct e1inp_ts *ts = &line->ts[i]; - - if (ts->type != E1INP_TS_TYPE_SIGN) - continue; - - llist_for_each_entry(link, &ts->sign.sign_links, list) { - if (!ts->lapd) - continue; - lapd_instance_set_profile(ts->lapd, - &lapd_profile_abis_ericsson); - - if (start) - lapd_sap_start(ts->lapd, link->tei, link->sapi); - else - lapd_sap_stop(ts->lapd, link->tei, link->sapi); - } - } -} - -/* Callback function to be called every time we receive a signal from INPUT */ -static int gbl_sig_cb(unsigned int subsys, unsigned int signal, - void *handler_data, void *signal_data) -{ - struct gsm_bts *bts; - - if (subsys != SS_L_GLOBAL) - return 0; - - switch (signal) { - case S_GLOBAL_BTS_CLOSE_OM: - bts = signal_data; - if (bts->type == GSM_BTS_TYPE_RBS2000) - shutdown_om(signal_data); - break; - } - - return 0; -} - -/* Callback function to be called every time we receive a signal from INPUT */ -static int inp_sig_cb(unsigned int subsys, unsigned int signal, - void *handler_data, void *signal_data) -{ - struct input_signal_data *isd = signal_data; - struct e1inp_ts *e1i_ts; - - if (subsys != SS_L_INPUT) - return 0; - - LOGP(DNM, LOGL_DEBUG, "%s(): Input signal '%s' received\n", __func__, - get_value_string(e1inp_signal_names, signal)); - switch (signal) { - case S_L_INP_TEI_UP: - switch (isd->link_type) { - case E1INP_SIGN_OML: - if (isd->trx->bts->type != GSM_BTS_TYPE_RBS2000) - break; - if (isd->tei == isd->trx->bts->oml_tei) - bootstrap_om_bts(isd->trx->bts); - else - bootstrap_om_trx(isd->trx); - break; - } - break; - case S_L_INP_TEI_DN: - if (isd->trx->bts->type != GSM_BTS_TYPE_RBS2000) - break; - LOGP(DNM, LOGL_NOTICE, "Line-%u TS-%u TEI-%u SAPI-%u: Link " - "Lost for Ericsson RBS2000. Re-starting DL Establishment\n", - isd->line->num, isd->ts_nr, isd->tei, isd->sapi); - /* Some datalink for a given TEI/SAPI went down, try to re-start it */ - e1i_ts = &isd->line->ts[isd->ts_nr-1]; - OSMO_ASSERT(e1i_ts->type == E1INP_TS_TYPE_SIGN); - lapd_sap_start(e1i_ts->lapd, isd->tei, isd->sapi); - break; - case S_L_INP_LINE_INIT: - case S_L_INP_LINE_NOALARM: - if (strcasecmp(isd->line->driver->name, "DAHDI") - && strcasecmp(isd->line->driver->name, "MISDN_LAPD") - && strcasecmp(isd->line->driver->name, "UNIXSOCKET")) - break; - start_sabm_in_line(isd->line, 1); - break; - case S_L_INP_LINE_ALARM: - if (strcasecmp(isd->line->driver->name, "DAHDI") - && strcasecmp(isd->line->driver->name, "MISDN_LAPD") - && strcasecmp(isd->line->driver->name, "UNIXSOCKET")) - break; - start_sabm_in_line(isd->line, 0); - break; - } - - return 0; -} - -static void config_write_bts(struct vty *vty, struct gsm_bts *bts) -{ - abis_om2k_config_write_bts(vty, bts); -} - -static int bts_model_rbs2k_start(struct gsm_network *net); - -static void bts_model_rbs2k_e1line_bind_ops(struct e1inp_line *line) -{ - e1inp_line_bind_ops(line, &bts_isdn_e1inp_line_ops); -} - -static struct gsm_bts_model model_rbs2k = { - .type = GSM_BTS_TYPE_RBS2000, - .name = "rbs2000", - .start = bts_model_rbs2k_start, - .oml_rcvmsg = &abis_om2k_rcvmsg, - .config_write_bts = &config_write_bts, - .e1line_bind_ops = &bts_model_rbs2k_e1line_bind_ops, -}; - -static int bts_model_rbs2k_start(struct gsm_network *net) -{ - model_rbs2k.features.data = &model_rbs2k._features_data[0]; - model_rbs2k.features.data_len = sizeof(model_rbs2k._features_data); - - gsm_btsmodel_set_feature(&model_rbs2k, BTS_FEAT_GPRS); - gsm_btsmodel_set_feature(&model_rbs2k, BTS_FEAT_EGPRS); - gsm_btsmodel_set_feature(&model_rbs2k, BTS_FEAT_HOPPING); - gsm_btsmodel_set_feature(&model_rbs2k, BTS_FEAT_HSCSD); - gsm_btsmodel_set_feature(&model_rbs2k, BTS_FEAT_MULTI_TSC); - - osmo_signal_register_handler(SS_L_INPUT, inp_sig_cb, NULL); - osmo_signal_register_handler(SS_L_GLOBAL, gbl_sig_cb, NULL); - - return 0; -} - -int bts_model_rbs2k_init(void) -{ - return gsm_bts_model_register(&model_rbs2k); -} diff --git a/openbsc/src/libbsc/bts_init.c b/openbsc/src/libbsc/bts_init.c deleted file mode 100644 index d6b152a7..00000000 --- a/openbsc/src/libbsc/bts_init.c +++ /dev/null @@ -1,30 +0,0 @@ -/* (C) 2011 by Harald Welte - * - * All Rights Reserved - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation; either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - * - */ -#include - -int bts_init(void) -{ - bts_model_bs11_init(); - bts_model_rbs2k_init(); - bts_model_nanobts_init(); - bts_model_nokia_site_init(); - bts_model_sysmobts_init(); - /* Your new BTS here. */ - return 0; -} diff --git a/openbsc/src/libbsc/bts_ipaccess_nanobts.c b/openbsc/src/libbsc/bts_ipaccess_nanobts.c deleted file mode 100644 index a1bde778..00000000 --- a/openbsc/src/libbsc/bts_ipaccess_nanobts.c +++ /dev/null @@ -1,520 +0,0 @@ -/* ip.access nanoBTS specific code */ - -/* (C) 2009-2010 by Harald Welte - * - * All Rights Reserved - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation; either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public 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 - -extern struct gsm_network *bsc_gsmnet; - -static int bts_model_nanobts_start(struct gsm_network *net); -static void bts_model_nanobts_e1line_bind_ops(struct e1inp_line *line); - -struct gsm_bts_model bts_model_nanobts = { - .type = GSM_BTS_TYPE_NANOBTS, - .name = "nanobts", - .start = bts_model_nanobts_start, - .oml_rcvmsg = &abis_nm_rcvmsg, - .e1line_bind_ops = bts_model_nanobts_e1line_bind_ops, - .nm_att_tlvdef = { - .def = { - /* ip.access specifics */ - [NM_ATT_IPACC_DST_IP] = { TLV_TYPE_FIXED, 4 }, - [NM_ATT_IPACC_DST_IP_PORT] = { TLV_TYPE_FIXED, 2 }, - [NM_ATT_IPACC_STREAM_ID] = { TLV_TYPE_TV, }, - [NM_ATT_IPACC_SEC_OML_CFG] = { TLV_TYPE_FIXED, 6 }, - [NM_ATT_IPACC_IP_IF_CFG] = { TLV_TYPE_FIXED, 8 }, - [NM_ATT_IPACC_IP_GW_CFG] = { TLV_TYPE_FIXED, 12 }, - [NM_ATT_IPACC_IN_SERV_TIME] = { TLV_TYPE_FIXED, 4 }, - [NM_ATT_IPACC_LOCATION] = { TLV_TYPE_TL16V }, - [NM_ATT_IPACC_PAGING_CFG] = { TLV_TYPE_FIXED, 2 }, - [NM_ATT_IPACC_UNIT_ID] = { TLV_TYPE_TL16V }, - [NM_ATT_IPACC_UNIT_NAME] = { TLV_TYPE_TL16V }, - [NM_ATT_IPACC_SNMP_CFG] = { TLV_TYPE_TL16V }, - [NM_ATT_IPACC_PRIM_OML_CFG_LIST] = { TLV_TYPE_TL16V }, - [NM_ATT_IPACC_NV_FLAGS] = { TLV_TYPE_TL16V }, - [NM_ATT_IPACC_FREQ_CTRL] = { TLV_TYPE_FIXED, 2 }, - [NM_ATT_IPACC_PRIM_OML_FB_TOUT] = { TLV_TYPE_TL16V }, - [NM_ATT_IPACC_CUR_SW_CFG] = { TLV_TYPE_TL16V }, - [NM_ATT_IPACC_TIMING_BUS] = { TLV_TYPE_TL16V }, - [NM_ATT_IPACC_CGI] = { TLV_TYPE_TL16V }, - [NM_ATT_IPACC_RAC] = { TLV_TYPE_TL16V }, - [NM_ATT_IPACC_OBJ_VERSION] = { TLV_TYPE_TL16V }, - [NM_ATT_IPACC_GPRS_PAGING_CFG]= { TLV_TYPE_TL16V }, - [NM_ATT_IPACC_NSEI] = { TLV_TYPE_TL16V }, - [NM_ATT_IPACC_BVCI] = { TLV_TYPE_TL16V }, - [NM_ATT_IPACC_NSVCI] = { TLV_TYPE_TL16V }, - [NM_ATT_IPACC_NS_CFG] = { TLV_TYPE_TL16V }, - [NM_ATT_IPACC_BSSGP_CFG] = { TLV_TYPE_TL16V }, - [NM_ATT_IPACC_NS_LINK_CFG] = { TLV_TYPE_TL16V }, - [NM_ATT_IPACC_RLC_CFG] = { TLV_TYPE_TL16V }, - [NM_ATT_IPACC_ALM_THRESH_LIST]= { TLV_TYPE_TL16V }, - [NM_ATT_IPACC_MONIT_VAL_LIST] = { TLV_TYPE_TL16V }, - [NM_ATT_IPACC_TIB_CONTROL] = { TLV_TYPE_TL16V }, - [NM_ATT_IPACC_SUPP_FEATURES] = { TLV_TYPE_TL16V }, - [NM_ATT_IPACC_CODING_SCHEMES] = { TLV_TYPE_TL16V }, - [NM_ATT_IPACC_RLC_CFG_2] = { TLV_TYPE_TL16V }, - [NM_ATT_IPACC_HEARTB_TOUT] = { TLV_TYPE_TL16V }, - [NM_ATT_IPACC_UPTIME] = { TLV_TYPE_TL16V }, - [NM_ATT_IPACC_RLC_CFG_3] = { TLV_TYPE_TL16V }, - [NM_ATT_IPACC_SSL_CFG] = { TLV_TYPE_TL16V }, - [NM_ATT_IPACC_SEC_POSSIBLE] = { TLV_TYPE_TL16V }, - [NM_ATT_IPACC_IML_SSL_STATE] = { TLV_TYPE_TL16V }, - [NM_ATT_IPACC_REVOC_DATE] = { TLV_TYPE_TL16V }, - }, - }, -}; - - -/* Callback function to be called whenever we get a GSM 12.21 state change event */ -static int nm_statechg_event(int evt, struct nm_statechg_signal_data *nsd) -{ - uint8_t obj_class = nsd->obj_class; - void *obj = nsd->obj; - struct gsm_nm_state *new_state = nsd->new_state; - - struct gsm_bts *bts; - struct gsm_bts_trx *trx; - struct gsm_bts_trx_ts *ts; - struct gsm_bts_gprs_nsvc *nsvc; - - struct msgb *msgb; - - if (!is_ipaccess_bts(nsd->bts)) - return 0; - - /* This event-driven BTS setup is currently only required on nanoBTS */ - - /* S_NM_STATECHG_ADM is called after we call chg_adm_state() and would create - * endless loop */ - if (evt != S_NM_STATECHG_OPER) - return 0; - - switch (obj_class) { - case NM_OC_SITE_MANAGER: - bts = container_of(obj, struct gsm_bts, site_mgr); - if ((new_state->operational == NM_OPSTATE_ENABLED && - new_state->availability == NM_AVSTATE_OK) || - (new_state->operational == NM_OPSTATE_DISABLED && - new_state->availability == NM_AVSTATE_OFF_LINE)) - abis_nm_opstart(bts, obj_class, 0xff, 0xff, 0xff); - break; - case NM_OC_BTS: - bts = obj; - if (new_state->availability == NM_AVSTATE_DEPENDENCY) { - msgb = nanobts_attr_bts_get(bts); - abis_nm_set_bts_attr(bts, msgb->data, msgb->len); - msgb_free(msgb); - abis_nm_chg_adm_state(bts, obj_class, - bts->bts_nr, 0xff, 0xff, - NM_STATE_UNLOCKED); - abis_nm_opstart(bts, obj_class, - bts->bts_nr, 0xff, 0xff); - } - break; - case NM_OC_CHANNEL: - ts = obj; - trx = ts->trx; - if (new_state->operational == NM_OPSTATE_DISABLED && - new_state->availability == NM_AVSTATE_DEPENDENCY) { - enum abis_nm_chan_comb ccomb = - abis_nm_chcomb4pchan(ts->pchan); - if (abis_nm_set_channel_attr(ts, ccomb) == -EINVAL) { - ipaccess_drop_oml(trx->bts); - return -1; - } - abis_nm_chg_adm_state(trx->bts, obj_class, - trx->bts->bts_nr, trx->nr, ts->nr, - NM_STATE_UNLOCKED); - abis_nm_opstart(trx->bts, obj_class, - trx->bts->bts_nr, trx->nr, ts->nr); - } - if (new_state->operational == NM_OPSTATE_ENABLED - && new_state->availability == NM_AVSTATE_OK) - dyn_ts_init(ts); - break; - case NM_OC_RADIO_CARRIER: - trx = obj; - if (new_state->operational == NM_OPSTATE_DISABLED && - new_state->availability == NM_AVSTATE_OK) - abis_nm_opstart(trx->bts, obj_class, trx->bts->bts_nr, - trx->nr, 0xff); - break; - case NM_OC_GPRS_NSE: - bts = container_of(obj, struct gsm_bts, gprs.nse); - if (bts->gprs.mode == BTS_GPRS_NONE) - break; - if (new_state->availability == NM_AVSTATE_DEPENDENCY) { - msgb = nanobts_attr_nse_get(bts); - abis_nm_ipaccess_set_attr(bts, obj_class, bts->bts_nr, - 0xff, 0xff, msgb->data, - msgb->len); - msgb_free(msgb); - abis_nm_opstart(bts, obj_class, bts->bts_nr, - 0xff, 0xff); - } - break; - case NM_OC_GPRS_CELL: - bts = container_of(obj, struct gsm_bts, gprs.cell); - if (bts->gprs.mode == BTS_GPRS_NONE) - break; - if (new_state->availability == NM_AVSTATE_DEPENDENCY) { - msgb = nanobts_attr_cell_get(bts); - abis_nm_ipaccess_set_attr(bts, obj_class, bts->bts_nr, - 0, 0xff, msgb->data, - msgb->len); - msgb_free(msgb); - abis_nm_opstart(bts, obj_class, bts->bts_nr, - 0, 0xff); - abis_nm_chg_adm_state(bts, obj_class, bts->bts_nr, - 0, 0xff, NM_STATE_UNLOCKED); - abis_nm_chg_adm_state(bts, NM_OC_GPRS_NSE, bts->bts_nr, - 0xff, 0xff, NM_STATE_UNLOCKED); - } - break; - case NM_OC_GPRS_NSVC: - nsvc = obj; - bts = nsvc->bts; - if (bts->gprs.mode == BTS_GPRS_NONE) - break; - /* We skip NSVC1 since we only use NSVC0 */ - if (nsvc->id == 1) - break; - if ((new_state->availability == NM_AVSTATE_OFF_LINE) || - (new_state->availability == NM_AVSTATE_DEPENDENCY)) { - msgb = nanobts_attr_nscv_get(bts); - abis_nm_ipaccess_set_attr(bts, obj_class, bts->bts_nr, - nsvc->id, 0xff, - msgb->data, msgb->len); - msgb_free(msgb); - abis_nm_opstart(bts, obj_class, bts->bts_nr, - nsvc->id, 0xff); - abis_nm_chg_adm_state(bts, obj_class, bts->bts_nr, - nsvc->id, 0xff, - NM_STATE_UNLOCKED); - } - default: - break; - } - return 0; -} - -/* Callback function to be called every time we receive a 12.21 SW activated report */ -static int sw_activ_rep(struct msgb *mb) -{ - struct abis_om_fom_hdr *foh = msgb_l3(mb); - struct e1inp_sign_link *sign_link = mb->dst; - struct gsm_bts *bts = sign_link->trx->bts; - struct gsm_bts_trx *trx = gsm_bts_trx_num(bts, foh->obj_inst.trx_nr); - - if (!trx) - return -EINVAL; - - if (!is_ipaccess_bts(trx->bts)) - return 0; - - switch (foh->obj_class) { - case NM_OC_BASEB_TRANSC: - abis_nm_chg_adm_state(trx->bts, foh->obj_class, - trx->bts->bts_nr, trx->nr, 0xff, - NM_STATE_UNLOCKED); - abis_nm_opstart(trx->bts, foh->obj_class, - trx->bts->bts_nr, trx->nr, 0xff); - /* TRX software is active, tell it to initiate RSL Link */ - abis_nm_ipaccess_rsl_connect(trx, trx->bts->ip_access.rsl_ip, - 3003, trx->rsl_tei); - break; - case NM_OC_RADIO_CARRIER: { - /* - * Locking the radio carrier will make it go - * offline again and we would come here. The - * framework should determine that there was - * no change and avoid recursion. - * - * This code is here to make sure that on start - * a TRX remains locked. - */ - int rc_state = trx->mo.nm_state.administrative; - /* Patch ARFCN into radio attribute */ - struct msgb *msgb = nanobts_attr_radio_get(trx->bts, trx); - abis_nm_set_radio_attr(trx, msgb->data, msgb->len); - msgb_free(msgb); - abis_nm_chg_adm_state(trx->bts, foh->obj_class, - trx->bts->bts_nr, trx->nr, 0xff, - rc_state); - abis_nm_opstart(trx->bts, foh->obj_class, trx->bts->bts_nr, - trx->nr, 0xff); - break; - } - } - return 0; -} - -/* Callback function to be called every time we receive a signal from NM */ -static int bts_ipa_nm_sig_cb(unsigned int subsys, unsigned int signal, - void *handler_data, void *signal_data) -{ - if (subsys != SS_NM) - return 0; - - switch (signal) { - case S_NM_SW_ACTIV_REP: - return sw_activ_rep(signal_data); - case S_NM_STATECHG_OPER: - case S_NM_STATECHG_ADM: - return nm_statechg_event(signal, signal_data); - default: - break; - } - return 0; -} - -static int bts_model_nanobts_start(struct gsm_network *net) -{ - osmo_signal_unregister_handler(SS_NM, bts_ipa_nm_sig_cb, NULL); - osmo_signal_register_handler(SS_NM, bts_ipa_nm_sig_cb, NULL); - return 0; -} - -int bts_model_nanobts_init(void) -{ - bts_model_nanobts.features.data = &bts_model_nanobts._features_data[0]; - bts_model_nanobts.features.data_len = - sizeof(bts_model_nanobts._features_data); - - gsm_btsmodel_set_feature(&bts_model_nanobts, BTS_FEAT_GPRS); - gsm_btsmodel_set_feature(&bts_model_nanobts, BTS_FEAT_EGPRS); - gsm_btsmodel_set_feature(&bts_model_nanobts, BTS_FEAT_MULTI_TSC); - - return gsm_bts_model_register(&bts_model_nanobts); -} - -#define OML_UP 0x0001 -#define RSL_UP 0x0002 - -static struct gsm_bts * -find_bts_by_unitid(struct gsm_network *net, uint16_t site_id, uint16_t bts_id) -{ - struct gsm_bts *bts; - - llist_for_each_entry(bts, &net->bts_list, list) { - if (!is_ipaccess_bts(bts)) - continue; - - if (bts->ip_access.site_id == site_id && - bts->ip_access.bts_id == bts_id) - return bts; - } - return NULL; -} - -/* These are exported because they are used by the VTY interface. */ -void ipaccess_drop_rsl(struct gsm_bts_trx *trx) -{ - if (!trx->rsl_link) - return; - - e1inp_sign_link_destroy(trx->rsl_link); - trx->rsl_link = NULL; -} - -void ipaccess_drop_oml(struct gsm_bts *bts) -{ - struct gsm_bts *rdep_bts; - struct gsm_bts_trx *trx; - - if (!bts->oml_link) - return; - - e1inp_sign_link_destroy(bts->oml_link); - bts->oml_link = NULL; - - /* we have issues reconnecting RSL, drop everything. */ - llist_for_each_entry(trx, &bts->trx_list, list) - ipaccess_drop_rsl(trx); - - bts->ip_access.flags = 0; - - /* - * Go through the list and see if we are the depndency of a BTS - * and then drop the BTS. This can lead to some recursion but it - * should be fine in userspace. - * The oml_link is serving as recursion anchor for us and - * it is set to NULL some lines above. - */ - llist_for_each_entry(rdep_bts, &bts->network->bts_list, list) { - if (!bts_depend_is_depedency(rdep_bts, bts)) - continue; - LOGP(DLINP, LOGL_NOTICE, "Dropping BTS(%u) due BTS(%u).\n", - rdep_bts->nr, bts->nr); - ipaccess_drop_oml(rdep_bts); - } -} - -/* This function is called once the OML/RSL link becomes up. */ -static struct e1inp_sign_link * -ipaccess_sign_link_up(void *unit_data, struct e1inp_line *line, - enum e1inp_sign_type type) -{ - struct gsm_bts *bts; - struct ipaccess_unit *dev = unit_data; - struct e1inp_sign_link *sign_link = NULL; - - bts = find_bts_by_unitid(bsc_gsmnet, dev->site_id, dev->bts_id); - if (!bts) { - LOGP(DLINP, LOGL_ERROR, "Unable to find BTS configuration for " - " %u/%u/%u, disconnecting\n", dev->site_id, - dev->bts_id, dev->trx_id); - return NULL; - } - DEBUGP(DLINP, "Identified BTS %u/%u/%u\n", - dev->site_id, dev->bts_id, dev->trx_id); - - switch(type) { - case E1INP_SIGN_OML: - /* remove old OML signal link for this BTS. */ - ipaccess_drop_oml(bts); - - if (!bts_depend_check(bts)) { - LOGP(DLINP, LOGL_NOTICE, - "Dependency not full-filled for %u/%u/%u\n", - dev->site_id, dev->bts_id, dev->trx_id); - return NULL; - } - - /* create new OML link. */ - sign_link = bts->oml_link = - e1inp_sign_link_create(&line->ts[E1INP_SIGN_OML - 1], - E1INP_SIGN_OML, bts->c0, - bts->oml_tei, 0); - break; - case E1INP_SIGN_RSL: { - struct e1inp_ts *ts; - struct gsm_bts_trx *trx = gsm_bts_trx_num(bts, dev->trx_id); - - /* no OML link set yet? give up. */ - if (!bts->oml_link || !trx) - return NULL; - - /* remove old RSL link for this TRX. */ - ipaccess_drop_rsl(trx); - - /* set new RSL link for this TRX. */ - line = bts->oml_link->ts->line; - ts = &line->ts[E1INP_SIGN_RSL + dev->trx_id - 1]; - e1inp_ts_config_sign(ts, line); - sign_link = trx->rsl_link = - e1inp_sign_link_create(ts, E1INP_SIGN_RSL, - trx, trx->rsl_tei, 0); - trx->rsl_link->ts->sign.delay = 0; - break; - } - default: - break; - } - return sign_link; -} - -static void ipaccess_sign_link_down(struct e1inp_line *line) -{ - /* No matter what link went down, we close both signal links. */ - struct e1inp_ts *ts = &line->ts[E1INP_SIGN_OML-1]; - struct e1inp_sign_link *link; - - llist_for_each_entry(link, &ts->sign.sign_links, list) { - struct gsm_bts *bts = link->trx->bts; - - ipaccess_drop_oml(bts); - /* Yes, we only use the first element of the list. */ - break; - } -} - -/* This function is called if we receive one OML/RSL message. */ -static int ipaccess_sign_link(struct msgb *msg) -{ - int ret = 0; - struct e1inp_sign_link *link = msg->dst; - struct e1inp_ts *e1i_ts = link->ts; - - switch (link->type) { - case E1INP_SIGN_RSL: - if (!(link->trx->bts->ip_access.flags & - (RSL_UP << link->trx->nr))) { - e1inp_event(e1i_ts, S_L_INP_TEI_UP, - link->tei, link->sapi); - link->trx->bts->ip_access.flags |= - (RSL_UP << link->trx->nr); - } - ret = abis_rsl_rcvmsg(msg); - break; - case E1INP_SIGN_OML: - if (!(link->trx->bts->ip_access.flags & OML_UP)) { - e1inp_event(e1i_ts, S_L_INP_TEI_UP, - link->tei, link->sapi); - link->trx->bts->ip_access.flags |= OML_UP; - } - ret = abis_nm_rcvmsg(msg); - break; - default: - LOGP(DLINP, LOGL_ERROR, "Unknown signal link type %d\n", - link->type); - msgb_free(msg); - break; - } - return ret; -} - -/* not static, ipaccess-config needs it. */ -struct e1inp_line_ops ipaccess_e1inp_line_ops = { - .cfg = { - .ipa = { - .addr = "0.0.0.0", - .role = E1INP_LINE_R_BSC, - }, - }, - .sign_link_up = ipaccess_sign_link_up, - .sign_link_down = ipaccess_sign_link_down, - .sign_link = ipaccess_sign_link, -}; - -static void bts_model_nanobts_e1line_bind_ops(struct e1inp_line *line) -{ - e1inp_line_bind_ops(line, &ipaccess_e1inp_line_ops); -} diff --git a/openbsc/src/libbsc/bts_ipaccess_nanobts_omlattr.c b/openbsc/src/libbsc/bts_ipaccess_nanobts_omlattr.c deleted file mode 100644 index 473e1cae..00000000 --- a/openbsc/src/libbsc/bts_ipaccess_nanobts_omlattr.c +++ /dev/null @@ -1,240 +0,0 @@ -/* ip.access nanoBTS specific code, OML attribute table generator */ - -/* (C) 2016 by sysmocom s.f.m.c. GmbH - * All Rights Reserved - * - * Author: Philipp Maier - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation; either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - */ - -#include -#include -#include -#include - -static void patch_16(uint8_t *data, const uint16_t val) -{ - memcpy(data, &val, sizeof(val)); -} - -static void patch_32(uint8_t *data, const uint32_t val) -{ - memcpy(data, &val, sizeof(val)); -} - -struct msgb *nanobts_attr_bts_get(struct gsm_bts *bts) -{ - struct msgb *msgb; - uint8_t buf[256]; - int rlt; - msgb = msgb_alloc(1024, "nanobts_attr_bts"); - - memcpy(buf, "\x55\x5b\x61\x67\x6d\x73", 6); - msgb_tv_fixed_put(msgb, NM_ATT_INTERF_BOUND, 6, buf); - - /* interference avg. period in numbers of SACCH multifr */ - msgb_tv_put(msgb, NM_ATT_INTAVE_PARAM, 0x06); - - rlt = gsm_bts_get_radio_link_timeout(bts); - if (rlt == -1) { - /* Osmocom extension: Use infinite radio link timeout */ - buf[0] = 0xFF; - buf[1] = 0x00; - } else { - /* conn fail based on SACCH error rate */ - buf[0] = 0x01; - buf[1] = rlt; - } - msgb_tl16v_put(msgb, NM_ATT_CONN_FAIL_CRIT, 2, buf); - - memcpy(buf, "\x1e\x24\x24\xa8\x34\x21\xa8", 7); - msgb_tv_fixed_put(msgb, NM_ATT_T200, 7, buf); - - msgb_tv_put(msgb, NM_ATT_MAX_TA, 0x3f); - - /* seconds */ - memcpy(buf, "\x00\x01\x0a", 3); - msgb_tv_fixed_put(msgb, NM_ATT_OVERL_PERIOD, 3, buf); - - /* percent */ - msgb_tv_put(msgb, NM_ATT_CCCH_L_T, 10); - - /* seconds */ - msgb_tv_put(msgb, NM_ATT_CCCH_L_I_P, 1); - - /* busy threshold in - dBm */ - buf[0] = 10; - if (bts->rach_b_thresh != -1) - buf[0] = bts->rach_b_thresh & 0xff; - msgb_tv_put(msgb, NM_ATT_RACH_B_THRESH, buf[0]); - - /* rach load averaging 1000 slots */ - buf[0] = 0x03; - buf[1] = 0xe8; - if (bts->rach_ldavg_slots != -1) { - buf[0] = (bts->rach_ldavg_slots >> 8) & 0x0f; - buf[1] = bts->rach_ldavg_slots & 0xff; - } - msgb_tv_fixed_put(msgb, NM_ATT_LDAVG_SLOTS, 2, buf); - - /* miliseconds */ - msgb_tv_put(msgb, NM_ATT_BTS_AIR_TIMER, 128); - - /* 10 retransmissions of physical config */ - msgb_tv_put(msgb, NM_ATT_NY1, 10); - - buf[0] = (bts->c0->arfcn >> 8) & 0x0f; - buf[1] = bts->c0->arfcn & 0xff; - msgb_tv_fixed_put(msgb, NM_ATT_BCCH_ARFCN, 2, buf); - - msgb_tv_put(msgb, NM_ATT_BSIC, bts->bsic); - - abis_nm_ipaccess_cgi(buf, bts); - msgb_tl16v_put(msgb, NM_ATT_IPACC_CGI, 7, buf); - - return msgb; -} - -struct msgb *nanobts_attr_nse_get(struct gsm_bts *bts) -{ - struct msgb *msgb; - uint8_t buf[256]; - msgb = msgb_alloc(1024, "nanobts_attr_bts"); - - /* NSEI 925 */ - buf[0] = bts->gprs.nse.nsei >> 8; - buf[1] = bts->gprs.nse.nsei & 0xff; - msgb_tl16v_put(msgb, NM_ATT_IPACC_NSEI, 2, buf); - - /* all timers in seconds */ - OSMO_ASSERT(ARRAY_SIZE(bts->gprs.nse.timer) < sizeof(buf)); - memcpy(buf, bts->gprs.nse.timer, ARRAY_SIZE(bts->gprs.nse.timer)); - msgb_tl16v_put(msgb, NM_ATT_IPACC_NS_CFG, 7, buf); - - /* all timers in seconds */ - buf[0] = 3; /* blockimg timer (T1) */ - buf[1] = 3; /* blocking retries */ - buf[2] = 3; /* unblocking retries */ - buf[3] = 3; /* reset timer (T2) */ - buf[4] = 3; /* reset retries */ - buf[5] = 10; /* suspend timer (T3) in 100ms */ - buf[6] = 3; /* suspend retries */ - buf[7] = 10; /* resume timer (T4) in 100ms */ - buf[8] = 3; /* resume retries */ - buf[9] = 10; /* capability update timer (T5) */ - buf[10] = 3; /* capability update retries */ - - OSMO_ASSERT(ARRAY_SIZE(bts->gprs.cell.timer) < sizeof(buf)); - memcpy(buf, bts->gprs.cell.timer, ARRAY_SIZE(bts->gprs.cell.timer)); - msgb_tl16v_put(msgb, NM_ATT_IPACC_BSSGP_CFG, 11, buf); - - return msgb; -} - -struct msgb *nanobts_attr_cell_get(struct gsm_bts *bts) -{ - struct msgb *msgb; - uint8_t buf[256]; - msgb = msgb_alloc(1024, "nanobts_attr_bts"); - - /* routing area code */ - buf[0] = bts->gprs.rac; - msgb_tl16v_put(msgb, NM_ATT_IPACC_RAC, 1, buf); - - buf[0] = 5; /* repeat time (50ms) */ - buf[1] = 3; /* repeat count */ - msgb_tl16v_put(msgb, NM_ATT_IPACC_GPRS_PAGING_CFG, 2, buf); - - /* BVCI 925 */ - buf[0] = bts->gprs.cell.bvci >> 8; - buf[1] = bts->gprs.cell.bvci & 0xff; - msgb_tl16v_put(msgb, NM_ATT_IPACC_BVCI, 2, buf); - - /* all timers in seconds, unless otherwise stated */ - buf[0] = 20; /* T3142 */ - buf[1] = 5; /* T3169 */ - buf[2] = 5; /* T3191 */ - buf[3] = 160; /* T3193 (units of 10ms) */ - buf[4] = 5; /* T3195 */ - buf[5] = 10; /* N3101 */ - buf[6] = 4; /* N3103 */ - buf[7] = 8; /* N3105 */ - buf[8] = 15; /* RLC CV countdown */ - msgb_tl16v_put(msgb, NM_ATT_IPACC_RLC_CFG, 9, buf); - - if (bts->gprs.mode == BTS_GPRS_EGPRS) { - buf[0] = 0x8f; - buf[1] = 0xff; - } else { - buf[0] = 0x0f; - buf[1] = 0x00; - } - msgb_tl16v_put(msgb, NM_ATT_IPACC_CODING_SCHEMES, 2, buf); - - buf[0] = 0; /* T downlink TBF extension (0..500, high byte) */ - buf[1] = 250; /* T downlink TBF extension (0..500, low byte) */ - buf[2] = 0; /* T uplink TBF extension (0..500, high byte) */ - buf[3] = 250; /* T uplink TBF extension (0..500, low byte) */ - buf[4] = 2; /* CS2 */ - msgb_tl16v_put(msgb, NM_ATT_IPACC_RLC_CFG_2, 5, buf); - -#if 0 - /* EDGE model only, breaks older models. - * Should inquire the BTS capabilities */ - buf[0] = 2; /* MCS2 */ - msgb_tl16v_put(msgb, NM_ATT_IPACC_RLC_CFG_3, 1, buf); -#endif - - return msgb; -} - -struct msgb *nanobts_attr_nscv_get(struct gsm_bts *bts) -{ - struct msgb *msgb; - uint8_t buf[256]; - msgb = msgb_alloc(1024, "nanobts_attr_bts"); - - /* 925 */ - buf[0] = bts->gprs.nsvc[0].nsvci >> 8; - buf[1] = bts->gprs.nsvc[0].nsvci & 0xff; - msgb_tl16v_put(msgb, NM_ATT_IPACC_NSVCI, 2, buf); - - /* remote udp port */ - patch_16(&buf[0], htons(bts->gprs.nsvc[0].remote_port)); - /* remote ip address */ - patch_32(&buf[2], htonl(bts->gprs.nsvc[0].remote_ip)); - /* local udp port */ - patch_16(&buf[6], htons(bts->gprs.nsvc[0].local_port)); - msgb_tl16v_put(msgb, NM_ATT_IPACC_NS_LINK_CFG, 8, buf); - - return msgb; -} - -struct msgb *nanobts_attr_radio_get(struct gsm_bts *bts, - struct gsm_bts_trx *trx) -{ - struct msgb *msgb; - uint8_t buf[256]; - msgb = msgb_alloc(1024, "nanobts_attr_bts"); - - /* number of -2dB reduction steps / Pn */ - msgb_tv_put(msgb, NM_ATT_RF_MAXPOWR_R, trx->max_power_red / 2); - - buf[0] = trx->arfcn >> 8; - buf[1] = trx->arfcn & 0xff; - msgb_tl16v_put(msgb, NM_ATT_ARFCN_LIST, 2, buf); - - return msgb; -} diff --git a/openbsc/src/libbsc/bts_nokia_site.c b/openbsc/src/libbsc/bts_nokia_site.c deleted file mode 100644 index 3ca76c01..00000000 --- a/openbsc/src/libbsc/bts_nokia_site.c +++ /dev/null @@ -1,1739 +0,0 @@ -/* Nokia XXXsite family specific code */ - -/* (C) 2011 by Dieter Spaar - * - * All Rights Reserved - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation; either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - * - */ - -/* - TODO: Attention: There are some static variables used for states during - configuration. Those variables have to be moved to a BTS specific context, - otherwise there will most certainly be problems if more than one Nokia BTS - is used. -*/ - -#include - -#include - -#include -#include -#include -#include -#include - -#include - -#include - -/* TODO: put in a separate file ? */ - -extern int abis_nm_sendmsg(struct gsm_bts *bts, struct msgb *msg); -/* was static in system_information.c */ -extern int generate_cell_chan_list(uint8_t * chan_list, struct gsm_bts *bts); - -static void nokia_abis_nm_queue_send_next(struct gsm_bts *bts); -static void reset_timer_cb(void *_bts); -static int abis_nm_reset(struct gsm_bts *bts, uint16_t ref); -static int dump_elements(uint8_t * data, int len) __attribute__((unused)); - -static void bootstrap_om_bts(struct gsm_bts *bts) -{ - LOGP(DNM, LOGL_NOTICE, "bootstrapping OML for BTS %u\n", bts->nr); - - if (!bts->nokia.skip_reset) { - if (!bts->nokia.did_reset) - abis_nm_reset(bts, 1); - } else - bts->nokia.did_reset = 1; -} - -static void bootstrap_om_trx(struct gsm_bts_trx *trx) -{ - LOGP(DNM, LOGL_NOTICE, "bootstrapping OML for TRX %u/%u\n", - trx->bts->nr, trx->nr); -} - -static int shutdown_om(struct gsm_bts *bts) -{ - /* TODO !? */ - return 0; -} - -#define SAPI_OML 62 -#define SAPI_RSL 0 - -/* - - Tell LAPD to start start the SAP (send SABM requests) for all signalling - timeslots in this line - - Attention: this has to be adapted for mISDN -*/ - -static void start_sabm_in_line(struct e1inp_line *line, int start, int sapi) -{ - struct e1inp_sign_link *link; - int i; - - for (i = 0; i < ARRAY_SIZE(line->ts); i++) { - struct e1inp_ts *ts = &line->ts[i]; - - if (ts->type != E1INP_TS_TYPE_SIGN) - continue; - - llist_for_each_entry(link, &ts->sign.sign_links, list) { - if (sapi != -1 && link->sapi != sapi) - continue; - -#if 0 /* debugging */ - printf("sap start/stop (%d): %d tei=%d sapi=%d\n", - start, i + 1, link->tei, link->sapi); -#endif - - if (start) { - ts->lapd->profile.t200_sec = 1; - ts->lapd->profile.t200_usec = 0; - lapd_sap_start(ts->lapd, link->tei, - link->sapi); - } else - lapd_sap_stop(ts->lapd, link->tei, - link->sapi); - } - } -} - -/* Callback function to be called every time we receive a signal from INPUT */ -static int gbl_sig_cb(unsigned int subsys, unsigned int signal, - void *handler_data, void *signal_data) -{ - struct gsm_bts *bts; - - if (subsys != SS_L_GLOBAL) - return 0; - - switch (signal) { - case S_GLOBAL_BTS_CLOSE_OM: - bts = signal_data; - if (bts->type == GSM_BTS_TYPE_NOKIA_SITE) - shutdown_om(signal_data); - break; - } - - return 0; -} - -/* Callback function to be called every time we receive a signal from INPUT */ -static int inp_sig_cb(unsigned int subsys, unsigned int signal, - void *handler_data, void *signal_data) -{ - struct input_signal_data *isd = signal_data; - - if (subsys != SS_L_INPUT) - return 0; - - switch (signal) { - case S_L_INP_LINE_INIT: - start_sabm_in_line(isd->line, 1, SAPI_OML); /* start only OML */ - break; - case S_L_INP_TEI_DN: - break; - case S_L_INP_TEI_UP: - switch (isd->link_type) { - case E1INP_SIGN_OML: - if (isd->trx->bts->type != GSM_BTS_TYPE_NOKIA_SITE) - break; - - if (isd->tei == isd->trx->bts->oml_tei) - bootstrap_om_bts(isd->trx->bts); - else - bootstrap_om_trx(isd->trx); - break; - } - break; - case S_L_INP_TEI_UNKNOWN: - /* We are receiving LAPD frames with one TEI that we do not - * seem to know, likely that we (the BSC) stopped working - * and lost our local states. However, the BTS is already - * configured, we try to take over the RSL links. */ - start_sabm_in_line(isd->line, 1, SAPI_RSL); - break; - } - - return 0; -} - -static void nm_statechg_evt(unsigned int signal, - struct nm_statechg_signal_data *nsd) -{ - if (nsd->bts->type != GSM_BTS_TYPE_NOKIA_SITE) - return; -} - -static int nm_sig_cb(unsigned int subsys, unsigned int signal, - void *handler_data, void *signal_data) -{ - if (subsys != SS_NM) - return 0; - - switch (signal) { - case S_NM_STATECHG_OPER: - case S_NM_STATECHG_ADM: - nm_statechg_evt(signal, signal_data); - break; - default: - break; - } - - return 0; -} - -/* TODO: put in a separate file ? */ - -static const struct value_string nokia_msgt_name[] = { - { 0x80, "NOKIA_BTS_CONF_DATA" }, - { 0x81, "NOKIA_BTS_ACK" }, - { 0x82, "NOKIA_BTS_OMU_STARTED" }, - { 0x83, "NOKIA_BTS_START_DOWNLOAD_REQ" }, - { 0x84, "NOKIA_BTS_MF_REQ" }, - { 0x85, "NOKIA_BTS_AF_REQ" }, - { 0x86, "NOKIA_BTS_RESET_REQ" }, - { 0x87, "NOKIA_reserved" }, - { 0x88, "NOKIA_BTS_CONF_REQ" }, - { 0x89, "NOKIA_BTS_TEST_REQ" }, - { 0x8A, "NOKIA_BTS_TEST_REPORT" }, - { 0x8B, "NOKIA_reserved" }, - { 0x8C, "NOKIA_reserved" }, - { 0x8D, "NOKIA_reserved" }, - { 0x8E, "NOKIA_BTS_CONF_COMPL" }, - { 0x8F, "NOKIA_reserved" }, - { 0x90, "NOKIA_BTS_STM_TEST_REQ" }, - { 0x91, "NOKIA_BTS_STM_TEST_REPORT" }, - { 0x92, "NOKIA_BTS_TRANSMISSION_COMMAND" }, - { 0x93, "NOKIA_BTS_TRANSMISSION_ANSWER" }, - { 0x94, "NOKIA_BTS_HW_DB_UPLOAD_REQ" }, - { 0x95, "NOKIA_BTS_START_HW_DB_DOWNLOAD_REQ" }, - { 0x96, "NOKIA_BTS_HW_DB_SAVE_REQ" }, - { 0x97, "NOKIA_BTS_FLASH_ERASURE_REQ" }, - { 0x98, "NOKIA_BTS_HW_DB_DOWNLOAD_REQ" }, - { 0x99, "NOKIA_BTS_PWR_SUPPLY_CONTROL" }, - { 0x9A, "NOKIA_BTS_ATTRIBUTE_REQ" }, - { 0x9B, "NOKIA_BTS_ATTRIBUTE_REPORT" }, - { 0x9C, "NOKIA_BTS_HW_REQ" }, - { 0x9D, "NOKIA_BTS_HW_REPORT" }, - { 0x9E, "NOKIA_BTS_RTE_TEST_REQ" }, - { 0x9F, "NOKIA_BTS_RTE_TEST_REPORT" }, - { 0xA0, "NOKIA_BTS_HW_DB_VERIFICATION_REQ" }, - { 0xA1, "NOKIA_BTS_CLOCK_REQ" }, - { 0xA2, "NOKIA_AC_CIRCUIT_REQ_NACK" }, - { 0xA3, "NOKIA_AC_INTERRUPTED" }, - { 0xA4, "NOKIA_BTS_NEW_TRE_INFO" }, - { 0xA5, "NOKIA_AC_BSC_CIRCUITS_ALLOCATED" }, - { 0xA6, "NOKIA_BTS_TRE_POLL_LIST" }, - { 0xA7, "NOKIA_AC_CIRCUIT_REQ" }, - { 0xA8, "NOKIA_BTS_BLOCK_CTRL_REQ" }, - { 0xA9, "NOKIA_BTS_GSM_TIME_REQ" }, - { 0xAA, "NOKIA_BTS_GSM_TIME" }, - { 0xAB, "NOKIA_BTS_OUTPUT_CONTROL" }, - { 0xAC, "NOKIA_BTS_STATE_CHANGED" }, - { 0xAD, "NOKIA_BTS_SW_SAVE_REQ" }, - { 0xAE, "NOKIA_BTS_ALARM" }, - { 0xAF, "NOKIA_BTS_CHA_ADM_STATE" }, - { 0xB0, "NOKIA_AC_POOL_SIZE_REPORT" }, - { 0xB1, "NOKIA_AC_POOL_SIZE_INQUIRY" }, - { 0xB2, "NOKIA_BTS_COMMISS_TEST_COMPLETED" }, - { 0xB3, "NOKIA_BTS_COMMISS_TEST_REQ" }, - { 0xB4, "NOKIA_BTS_TRANSP_BTS_TO_BSC" }, - { 0xB5, "NOKIA_BTS_TRANSP_BSC_TO_BTS" }, - { 0xB6, "NOKIA_BTS_LCS_COMMAND" }, - { 0xB7, "NOKIA_BTS_LCS_ANSWER" }, - { 0xB8, "NOKIA_BTS_LMU_FN_OFFSET_COMMAND" }, - { 0xB9, "NOKIA_BTS_LMU_FN_OFFSET_ANSWER" }, - { 0, NULL } -}; - -static const char *get_msg_type_name_string(uint8_t msg_type) -{ - return get_value_string(nokia_msgt_name, msg_type); -} - -static const struct value_string nokia_element_name[] = { - { 0x01, "Ny1" }, - { 0x02, "T3105_F" }, - { 0x03, "Interference band limits" }, - { 0x04, "Interference report timer in secs" }, - { 0x05, "Channel configuration per TS" }, - { 0x06, "BSIC" }, - { 0x07, "RACH report timer in secs" }, - { 0x08, "Hardware database status" }, - { 0x09, "BTS RX level" }, - { 0x0A, "ARFN" }, - { 0x0B, "STM antenna attenuation" }, - { 0x0C, "Cell allocation bitmap" }, - { 0x0D, "Radio definition per TS" }, - { 0x0E, "Frame number" }, - { 0x0F, "Antenna diversity" }, - { 0x10, "T3105_D" }, - { 0x11, "File format" }, - { 0x12, "Last File" }, - { 0x13, "BTS type" }, - { 0x14, "Erasure mode" }, - { 0x15, "Hopping mode" }, - { 0x16, "Floating TRX" }, - { 0x17, "Power supplies" }, - { 0x18, "Reset type" }, - { 0x19, "Averaging period" }, - { 0x1A, "RBER2" }, - { 0x1B, "LAC" }, - { 0x1C, "CI" }, - { 0x1D, "Failure parameters" }, - { 0x1E, "(RF max power reduction)" }, - { 0x1F, "Measured RX_SENS" }, - { 0x20, "Extended cell radius" }, - { 0x21, "reserved" }, - { 0x22, "Success-Failure" }, - { 0x23, "Ack-Nack" }, - { 0x24, "OMU test results" }, - { 0x25, "File identity" }, - { 0x26, "Generation and version code" }, - { 0x27, "SW description" }, - { 0x28, "BCCH LEV" }, - { 0x29, "Test type" }, - { 0x2A, "Subscriber number" }, - { 0x2B, "reserved" }, - { 0x2C, "HSN" }, - { 0x2D, "reserved" }, - { 0x2E, "MS RXLEV" }, - { 0x2F, "MS TXLEV" }, - { 0x30, "RXQUAL" }, - { 0x31, "RX SENS" }, - { 0x32, "Alarm block" }, - { 0x33, "Neighbouring BCCH levels" }, - { 0x34, "STM report type" }, - { 0x35, "MA" }, - { 0x36, "MAIO" }, - { 0x37, "H_FLAG" }, - { 0x38, "TCH_ARFN" }, - { 0x39, "Clock output" }, - { 0x3A, "Transmitted power" }, - { 0x3B, "Clock sync" }, - { 0x3C, "TMS protocol discriminator" }, - { 0x3D, "TMS protocol data" }, - { 0x3E, "FER" }, - { 0x3F, "SWR result" }, - { 0x40, "Object identity" }, - { 0x41, "STM RX Antenna Test" }, - { 0x42, "reserved" }, - { 0x43, "reserved" }, - { 0x44, "Object current state" }, - { 0x45, "reserved" }, - { 0x46, "FU channel configuration" }, - { 0x47, "reserved" }, - { 0x48, "ARFN of a CU" }, - { 0x49, "FU radio definition" }, - { 0x4A, "reserved" }, - { 0x4B, "Severity" }, - { 0x4C, "Diversity selection" }, - { 0x4D, "RX antenna test" }, - { 0x4E, "RX antenna supervision period" }, - { 0x4F, "RX antenna state" }, - { 0x50, "Sector configuration" }, - { 0x51, "Additional info" }, - { 0x52, "SWR parameters" }, - { 0x53, "HW inquiry mode" }, - { 0x54, "reserved" }, - { 0x55, "Availability status" }, - { 0x56, "reserved" }, - { 0x57, "EAC inputs" }, - { 0x58, "EAC outputs" }, - { 0x59, "reserved" }, - { 0x5A, "Position" }, - { 0x5B, "HW unit identity" }, - { 0x5C, "RF test signal attenuation" }, - { 0x5D, "Operational state" }, - { 0x5E, "Logical object identity" }, - { 0x5F, "reserved" }, - { 0x60, "BS_TXPWR_OM" }, - { 0x61, "Loop_Duration" }, - { 0x62, "LNA_Path_Selection" }, - { 0x63, "Serial number" }, - { 0x64, "HW version" }, - { 0x65, "Obj. identity and obj. state" }, - { 0x66, "reserved" }, - { 0x67, "EAC input definition" }, - { 0x68, "EAC id and text" }, - { 0x69, "HW unit status" }, - { 0x6A, "SW release version" }, - { 0x6B, "FW version" }, - { 0x6C, "Bit_Error_Ratio" }, - { 0x6D, "RXLEV_with_Attenuation" }, - { 0x6E, "RXLEV_without_Attenuation" }, - { 0x6F, "reserved" }, - { 0x70, "CU_Results" }, - { 0x71, "reserved" }, - { 0x72, "LNA_Path_Results" }, - { 0x73, "RTE Results" }, - { 0x74, "Real Time" }, - { 0x75, "RX diversity selection" }, - { 0x76, "EAC input config" }, - { 0x77, "Feature support" }, - { 0x78, "File version" }, - { 0x79, "Outputs" }, - { 0x7A, "FU parameters" }, - { 0x7B, "Diagnostic info" }, - { 0x7C, "FU BSIC" }, - { 0x7D, "TRX Configuration" }, - { 0x7E, "Download status" }, - { 0x7F, "RX difference limit" }, - { 0x80, "TRX HW capability" }, - { 0x81, "Common HW config" }, - { 0x82, "Autoconfiguration pool size" }, - { 0x83, "TRE diagnostic info" }, - { 0x84, "TRE object identity" }, - { 0x85, "New TRE Info" }, - { 0x86, "Acknowledgement period" }, - { 0x87, "Synchronization mode" }, - { 0x88, "reserved" }, - { 0x89, "Block Control Data" }, - { 0x8A, "SW load mode" }, - { 0x8B, "Recommended recovery action" }, - { 0x8C, "BSC BCF id" }, - { 0x8D, "Q1 baud rate" }, - { 0x8E, "Allocation status" }, - { 0x8F, "Functional entity number" }, - { 0x90, "Transmission delay" }, - { 0x91, "Loop Duration ms" }, - { 0x92, "Logical channel" }, - { 0x93, "Q1 address" }, - { 0x94, "Alarm detail" }, - { 0x95, "Cabinet type" }, - { 0x96, "HW unit existence" }, - { 0x97, "RF power parameters" }, - { 0x98, "Message scenario" }, - { 0x99, "HW unit max amount" }, - { 0x9A, "Master TRX" }, - { 0x9B, "Transparent data" }, - { 0x9C, "BSC topology info" }, - { 0x9D, "Air i/f modulation" }, - { 0x9E, "LCS Q1 command data" }, - { 0x9F, "Frame number offset" }, - { 0xA0, "Abis TSL" }, - { 0xA1, "Dynamic pool info" }, - { 0xA2, "LCS LLP data" }, - { 0xA3, "LCS Q1 answer data" }, - { 0xA4, "DFCA FU Radio Definition" }, - { 0xA5, "Antenna hopping" }, - { 0xA6, "Field record sequence number" }, - { 0xA7, "Timeslot offslot" }, - { 0xA8, "EPCR capability" }, - { 0xA9, "Connectsite optional element" }, - { 0xAA, "TSC" }, - { 0xAB, "Special TX Power Setting" }, - { 0xAC, "Optional sync settings" }, - { 0xFA, "Abis If parameters" }, - { 0, NULL } -}; - -static const char *get_element_name_string(uint16_t element) -{ - return get_value_string(nokia_element_name, element); -} - -static const struct value_string nokia_bts_types[] = { - { 0x0a, "MetroSite GSM 900" }, - { 0x0b, "MetroSite GSM 1800" }, - { 0x0c, "MetroSite GSM 1900 (PCS)" }, - { 0x0d, "MetroSite GSM 900 & 1800" }, - { 0x0e, "InSite GSM 900" }, - { 0x0f, "InSite GSM 1800" }, - { 0x10, "InSite GSM 1900" }, - { 0x11, "UltraSite GSM 900" }, - { 0x12, "UltraSite GSM 1800" }, - { 0x13, "UltraSite GSM/US-TDMA 1900" }, - { 0x14, "UltraSite GSM 900 & 1800" }, - { 0x16, "UltraSite GSM/US-TDMA 850" }, - { 0x18, "MetroSite GSM/US-TDMA 850" }, - { 0x19, "UltraSite GSM 800/1900" }, - { 0, NULL } -}; - -static const char *get_bts_type_string(uint8_t type) -{ - return get_value_string(nokia_bts_types, type); -} - -static const struct value_string nokia_severity[] = { - { 0, "indeterminate" }, - { 1, "critical" }, - { 2, "major" }, - { 3, "minor" }, - { 4, "warning" }, - { 0, NULL } -}; - -static const char *get_severity_string(uint8_t severity) -{ - return get_value_string(nokia_severity, severity); -} - -/* TODO: put in a separate file ? */ - -/* some message IDs */ - -#define NOKIA_MSG_CONF_DATA 128 -#define NOKIA_MSG_ACK 129 -#define NOKIA_MSG_OMU_STARTED 130 -#define NOKIA_MSG_START_DOWNLOAD_REQ 131 -#define NOKIA_MSG_MF_REQ 132 -#define NOKIA_MSG_RESET_REQ 134 -#define NOKIA_MSG_CONF_REQ 136 -#define NOKIA_MSG_CONF_COMPLETE 142 -#define NOKIA_MSG_BLOCK_CTRL_REQ 168 -#define NOKIA_MSG_STATE_CHANGED 172 -#define NOKIA_MSG_ALARM 174 - -/* some element IDs */ - -#define NOKIA_EI_BTS_TYPE 0x13 -#define NOKIA_EI_ACK 0x23 -#define NOKIA_EI_ADD_INFO 0x51 -#define NOKIA_EI_SEVERITY 0x4B -#define NOKIA_EI_ALARM_DETAIL 0x94 - -#define OM_ALLOC_SIZE 1024 -#define OM_HEADROOM_SIZE 128 - -static uint8_t fu_config_template[] = { - 0x7F, 0x7A, 0x39, - /* ID = 0x7A (FU parameters) ## constructed ## */ - /* length = 57 */ - /* [3] */ - - 0x5F, 0x40, 0x04, - /* ID = 0x40 (Object identity) */ - /* length = 4 */ - /* [6] */ - 0x00, 0x07, 0x01, 0xFF, - - 0x41, 0x02, - /* ID = 0x01 (Ny1) */ - /* length = 2 */ - /* [12] */ - 0x00, 0x05, - - 0x42, 0x02, - /* ID = 0x02 (T3105_F) */ - /* length = 2 */ - /* [16] */ - 0x00, 0x28, /* FIXME: use net->T3105 */ - - 0x50, 0x02, - /* ID = 0x10 (T3105_D) */ - /* length = 2 */ - /* [20] */ - 0x00, 0x28, /* FIXME: use net->T3105 */ - - 0x43, 0x05, - /* ID = 0x03 (Interference band limits) */ - /* length = 5 */ - /* [24] */ - 0x0F, 0x1B, 0x27, 0x33, 0x3F, - - 0x44, 0x02, - /* ID = 0x04 (Interference report timer in secs) */ - /* length = 2 */ - /* [31] */ - 0x00, 0x10, - - 0x47, 0x01, - /* ID = 0x07 (RACH report timer in secs) */ - /* length = 1 */ - /* [35] */ - 0x1E, - - 0x4C, 0x10, - /* ID = 0x0C (Cell allocation bitmap) ####### */ - /* length = 16 */ - /* [38] */ - 0x8F, 0xB1, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - - 0x59, 0x01, - /* ID = 0x19 (Averaging period) */ - /* length = 1 */ - /* [56] */ - 0x01, - - 0x5E, 0x01, - /* ID = 0x1E ((RF max power reduction)) */ - /* length = 1 */ - /* [59] */ - 0x00, - - 0x7F, 0x46, 0x11, - /* ID = 0x46 (FU channel configuration) ## constructed ## */ - /* length = 17 */ - /* [63] */ - - 0x5F, 0x40, 0x04, - /* ID = 0x40 (Object identity) */ - /* length = 4 */ - /* [66] */ - 0x00, 0x07, 0x01, 0xFF, - - 0x45, 0x08, - /* ID = 0x05 (Channel configuration per TS) */ - /* length = 8 */ - /* [72] */ - 0x01, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, - - 0x7F, 0x65, 0x0B, - /* ID = 0x65 (Obj. identity and obj. state) ## constructed ## */ - /* length = 11 */ - /* [83] */ - - 0x5F, 0x40, 0x04, - /* ID = 0x40 (Object identity) */ - /* length = 4 */ - /* [86] */ - 0x00, 0x04, 0x01, 0xFF, - - 0x5F, 0x44, 0x01, - /* ID = 0x44 (Object current state) */ - /* length = 1 */ - /* [93] */ - 0x03, - - 0x7F, 0x7C, 0x0A, - /* ID = 0x7C (FU BSIC) ## constructed ## */ - /* length = 10 */ - /* [97] */ - - 0x5F, 0x40, 0x04, - /* ID = 0x40 (Object identity) */ - /* length = 4 */ - /* [100] */ - 0x00, 0x07, 0x01, 0xFF, - - 0x46, 0x01, - /* ID = 0x06 (BSIC) */ - /* length = 1 */ - /* [106] */ - 0x00, - - 0x7F, 0x48, 0x0B, - /* ID = 0x48 (ARFN of a CU) ## constructed ## */ - /* length = 11 */ - /* [110] */ - - 0x5F, 0x40, 0x04, - /* ID = 0x40 (Object identity) */ - /* length = 4 */ - /* [113] */ - 0x00, 0x08, 0x01, 0xFF, - - 0x4A, 0x02, - /* ID = 0x0A (ARFN) ####### */ - /* length = 2 */ - /* [119] */ - 0x03, 0x62, - - 0x7F, 0x49, 0x59, - /* ID = 0x49 (FU radio definition) ## constructed ## */ - /* length = 89 */ - /* [124] */ - - 0x5F, 0x40, 0x04, - /* ID = 0x40 (Object identity) */ - /* length = 4 */ - /* [127] */ - 0x00, 0x07, 0x01, 0xFF, - - 0x4D, 0x50, - /* ID = 0x0D (Radio definition per TS) ####### */ - /* length = 80 */ - /* [133] */ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* MA */ - 0x03, 0x62, /* HSN, MAIO or ARFCN if no hopping */ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x03, 0x62, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x03, 0x62, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x03, 0x62, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x03, 0x62, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x03, 0x62, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x03, 0x62, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x03, 0x62, -}; - -/* TODO: put in a separate file ? */ - -/* - build the configuration for each TRX -*/ - -static int make_fu_config(struct gsm_bts_trx *trx, uint8_t id, - uint8_t * fu_config, int *hopping) -{ - int i; - - *hopping = 0; - - memcpy(fu_config, fu_config_template, sizeof(fu_config_template)); - - /* set ID */ - - fu_config[6 + 2] = id; - fu_config[66 + 2] = id; - fu_config[86 + 2] = id; - fu_config[100 + 2] = id; - fu_config[113 + 2] = id; - fu_config[127 + 2] = id; - - /* set ARFCN */ - - uint16_t arfcn = trx->arfcn; - - fu_config[119] = arfcn >> 8; - fu_config[119 + 1] = arfcn & 0xFF; - - for (i = 0; i < ARRAY_SIZE(trx->ts); i++) { - struct gsm_bts_trx_ts *ts = &trx->ts[i]; - - if (ts->hopping.enabled) { - /* reverse order */ - int j; - for (j = 0; j < ts->hopping.ma_len; j++) - fu_config[133 + (i * 10) + (7 - j)] = - ts->hopping.ma_data[j]; - fu_config[133 + 8 + (i * 10)] = ts->hopping.hsn; - fu_config[133 + 8 + 1 + (i * 10)] = ts->hopping.maio; - *hopping = 1; - } else { - fu_config[133 + 8 + (i * 10)] = arfcn >> 8; - fu_config[133 + 8 + 1 + (i * 10)] = arfcn & 0xFF; - } - } - - /* set BSIC */ - - /* - Attention: all TRX except the first one seem to get the TSC - from the CHANNEL ACTIVATION command (in CHANNEL IDENTIFICATION, - GSM 04.08 CHANNEL DESCRIPTION). - There was a bug in rsl_chan_activate_lchan() setting this parameter. - */ - - uint8_t bsic = trx->bts->bsic; - - fu_config[106] = bsic; - - /* set CA */ - - if (generate_cell_chan_list(&fu_config[38], trx->bts) != 0) { - fprintf(stderr, "generate_cell_chan_list failed\n"); - return 0; - } - - /* set channel configuration */ - - for (i = 0; i < ARRAY_SIZE(trx->ts); i++) { - struct gsm_bts_trx_ts *ts = &trx->ts[i]; - uint8_t chan_config; - - /* - 0 = FCCH + SCH + BCCH + CCCH - 1 = FCCH + SCH + BCCH + CCCH + SDCCH/4 + SACCH/4 - 2 = BCCH + CCCH (This combination is not used in any BTS) - 3 = FCCH + SCH + BCCH + CCCH + SDCCH/4 with SDCCH2 used as CBCH - 4 = SDCCH/8 + SACCH/8 - 5 = SDCCH/8 with SDCCH2 used as CBCH - 6 = TCH/F + FACCH/F + SACCH/F - 7 = E-RACH (Talk family) - 9 = Dual rate (capability for TCH/F and TCH/H) - 10 = reserved for BTS internal use - 11 = PBCCH + PCCCH + PDTCH + PACCH + PTCCH (can be used in GPRS release 2). - 0xFF = spare TS - */ - - if (ts->pchan == GSM_PCHAN_NONE) - chan_config = 0xFF; - else if (ts->pchan == GSM_PCHAN_CCCH) - chan_config = 0; - else if (ts->pchan == GSM_PCHAN_CCCH_SDCCH4) - chan_config = 1; - else if (ts->pchan == GSM_PCHAN_TCH_F) - chan_config = 6; /* 9 should work too */ - else if (ts->pchan == GSM_PCHAN_TCH_H) - chan_config = 9; - else if (ts->pchan == GSM_PCHAN_SDCCH8_SACCH8C) - chan_config = 4; - else if (ts->pchan == GSM_PCHAN_PDCH) - chan_config = 11; - else { - fprintf(stderr, - "unsupported channel config %d for timeslot %d\n", - ts->pchan, i); - return 0; - } - - fu_config[72 + i] = chan_config; - } - return sizeof(fu_config_template); -} - -/* TODO: put in a separate file ? */ - -static uint8_t bts_config_1[] = { - 0x4E, 0x02, - /* ID = 0x0E (Frame number) */ - /* length = 2 */ - /* [2] */ - 0xFF, 0xFF, - - 0x5F, 0x4E, 0x02, - /* ID = 0x4E (RX antenna supervision period) */ - /* length = 2 */ - /* [7] */ - 0xFF, 0xFF, - - 0x5F, 0x50, 0x02, - /* ID = 0x50 (Sector configuration) */ - /* length = 2 */ - /* [12] */ - 0x01, 0x01, -}; - -static uint8_t bts_config_2[] = { - 0x55, 0x02, - /* ID = 0x15 (Hopping mode) */ - /* length = 2 */ - /* [2] */ - 0x01, 0x00, - - 0x5F, 0x75, 0x02, - /* ID = 0x75 (RX diversity selection) */ - /* length = 2 */ - /* [7] */ - 0x01, 0x01, -}; - -static uint8_t bts_config_3[] = { - 0x5F, 0x20, 0x02, - /* ID = 0x20 (Extended cell radius) */ - /* length = 2 */ - /* [3] */ - 0x01, 0x00, -}; - -static uint8_t bts_config_4[] = { - 0x5F, 0x74, 0x09, - /* ID = 0x74 (Real Time) */ - /* length = 9 */ - /* [3] year-high, year-low, month, day, hour, minute, second, msec-high, msec-low */ - 0x07, 0xDB, 0x06, 0x02, 0x0B, 0x20, 0x0C, 0x00, - 0x00, - - 0x5F, 0x76, 0x03, - /* ID = 0x76 (EAC input config) */ - /* length = 3 */ - /* [15] */ - 0x01, 0x01, 0x00, - - 0x5F, 0x76, 0x03, - /* ID = 0x76 (EAC input config) */ - /* length = 3 */ - /* [21] */ - 0x02, 0x01, 0x00, - - 0x5F, 0x76, 0x03, - /* ID = 0x76 (EAC input config) */ - /* length = 3 */ - /* [27] */ - 0x03, 0x01, 0x00, - - 0x5F, 0x76, 0x03, - /* ID = 0x76 (EAC input config) */ - /* length = 3 */ - /* [33] */ - 0x04, 0x01, 0x00, - - 0x5F, 0x76, 0x03, - /* ID = 0x76 (EAC input config) */ - /* length = 3 */ - /* [39] */ - 0x05, 0x01, 0x00, - - 0x5F, 0x76, 0x03, - /* ID = 0x76 (EAC input config) */ - /* length = 3 */ - /* [45] */ - 0x06, 0x01, 0x00, - - 0x5F, 0x76, 0x03, - /* ID = 0x76 (EAC input config) */ - /* length = 3 */ - /* [51] */ - 0x07, 0x01, 0x00, - - 0x5F, 0x76, 0x03, - /* ID = 0x76 (EAC input config) */ - /* length = 3 */ - /* [57] */ - 0x08, 0x01, 0x00, - - 0x5F, 0x76, 0x03, - /* ID = 0x76 (EAC input config) */ - /* length = 3 */ - /* [63] */ - 0x09, 0x01, 0x00, - - 0x5F, 0x76, 0x03, - /* ID = 0x76 (EAC input config) */ - /* length = 3 */ - /* [69] */ - 0x0A, 0x01, 0x00, -}; - -static uint8_t bts_config_insite[] = { - 0x4E, 0x02, - /* ID = 0x0E (Frame number) */ - /* length = 2 */ - /* [2] */ - 0xFF, 0xFF, - - 0x5F, 0x4E, 0x02, - /* ID = 0x4E (RX antenna supervision period) */ - /* length = 2 */ - /* [7] */ - 0xFF, 0xFF, - - 0x5F, 0x50, 0x02, - /* ID = 0x50 (Sector configuration) */ - /* length = 2 */ - /* [12] */ - 0x01, 0x01, - - 0x55, 0x02, - /* ID = 0x15 (Hopping mode) */ - /* length = 2 */ - /* [16] */ - 0x01, 0x00, - - 0x5F, 0x20, 0x02, - /* ID = 0x20 (Extended cell radius) */ - /* length = 2 */ - /* [21] */ - 0x01, 0x00, - - 0x5F, 0x74, 0x09, - /* ID = 0x74 (Real Time) */ - /* length = 9 */ - /* [26] */ - 0x07, 0xDB, 0x07, 0x0A, 0x0F, 0x09, 0x0B, 0x00, - 0x00, -}; - -void set_real_time(uint8_t * real_time) -{ - time_t t; - struct tm *tm; - - t = time(NULL); - tm = localtime(&t); - - /* year-high, year-low, month, day, hour, minute, second, msec-high, msec-low */ - - real_time[0] = (1900 + tm->tm_year) >> 8; - real_time[1] = (1900 + tm->tm_year) & 0xFF; - real_time[2] = tm->tm_mon + 1; - real_time[3] = tm->tm_mday; - real_time[4] = tm->tm_hour; - real_time[5] = tm->tm_min; - real_time[6] = tm->tm_sec; - real_time[7] = 0; - real_time[8] = 0; -} - -/* TODO: put in a separate file ? */ - -/* - build the configuration data -*/ - -static int make_bts_config(uint8_t bts_type, int n_trx, uint8_t * fu_config, - int need_hopping) -{ - /* is it an InSite BTS ? */ - if (bts_type == 0x0E || bts_type == 0x0F || bts_type == 0x10) { /* TODO */ - if (n_trx != 1) { - fprintf(stderr, "InSite has only one TRX\n"); - return 0; - } - if (need_hopping != 0) { - fprintf(stderr, "InSite does not support hopping\n"); - return 0; - } - memcpy(fu_config, bts_config_insite, sizeof(bts_config_insite)); - set_real_time(&fu_config[26]); - return sizeof(bts_config_insite); - } - - int len = 0; - int i; - - memcpy(fu_config + len, bts_config_1, sizeof(bts_config_1)); - - /* set sector configuration */ - fu_config[len + 12 - 1] = 1 + n_trx; /* len */ - for (i = 0; i < n_trx; i++) - fu_config[len + 12 + 1 + i] = ((i + 1) & 0xFF); - - len += (sizeof(bts_config_1) + (n_trx - 1)); - - memcpy(fu_config + len, bts_config_2, sizeof(bts_config_2)); - /* set hopping mode (Baseband and RF hopping work for the MetroSite) */ - if (need_hopping) - fu_config[len + 2 + 1] = 1; /* 0: no hopping, 1: Baseband hopping, 2: RF hopping */ - len += sizeof(bts_config_2); - - /* set extended cell radius for each TRX */ - for (i = 0; i < n_trx; i++) { - memcpy(fu_config + len, bts_config_3, sizeof(bts_config_3)); - fu_config[len + 3] = ((i + 1) & 0xFF); - len += sizeof(bts_config_3); - } - - memcpy(fu_config + len, bts_config_4, sizeof(bts_config_4)); - set_real_time(&fu_config[len + 3]); - len += sizeof(bts_config_4); - - return len; -} - -/* TODO: put in a separate file ? */ - -static struct msgb *nm_msgb_alloc(void) -{ - return msgb_alloc_headroom(OM_ALLOC_SIZE, OM_HEADROOM_SIZE, "OML"); -} - -/* TODO: put in a separate file ? */ - -struct abis_om_nokia_hdr { - uint8_t msg_type; - uint8_t spare; - uint16_t reference; - uint8_t data[0]; -} __attribute__ ((packed)); - -#define ABIS_OM_NOKIA_HDR_SIZE (sizeof(struct abis_om_hdr) + sizeof(struct abis_om_nokia_hdr)) - -static int abis_nm_send(struct gsm_bts *bts, uint8_t msg_type, uint16_t ref, - uint8_t * data, int len_data) -{ - struct abis_om_hdr *oh; - struct abis_om_nokia_hdr *noh; - struct msgb *msg = nm_msgb_alloc(); - - oh = (struct abis_om_hdr *)msgb_put(msg, - ABIS_OM_NOKIA_HDR_SIZE + len_data); - - oh->mdisc = ABIS_OM_MDISC_FOM; - oh->placement = ABIS_OM_PLACEMENT_ONLY; - oh->sequence = 0; - oh->length = sizeof(struct abis_om_nokia_hdr) + len_data; - - noh = (struct abis_om_nokia_hdr *)oh->data; - - noh->msg_type = msg_type; - noh->spare = 0; - noh->reference = htons(ref); - memcpy(noh->data, data, len_data); - - DEBUGPC(DNM, "Sending %s\n", get_msg_type_name_string(msg_type)); - - return abis_nm_sendmsg(bts, msg); -} - -/* TODO: put in a separate file ? */ - -static uint8_t download_req[] = { - 0x5F, 0x25, 0x0B, - /* ID = 0x25 (File identity) */ - /* length = 11 */ - /* [3] */ - 0x2A, 0x2A, 0x2A, 0x2A, 0x2A, 0x2A, 0x2A, 0x2A, - 0x2A, 0x2A, 0x2A, - - 0x5F, 0x78, 0x03, - /* ID = 0x78 (File version) */ - /* length = 3 */ - /* [17] */ - 0x2A, 0x2A, 0x2A, - - 0x5F, 0x81, 0x0A, 0x01, - /* ID = 0x8A (SW load mode) */ - /* length = 1 */ - /* [24] */ - 0x01, - - 0x5F, 0x81, 0x06, 0x01, - /* ID = 0x86 (Acknowledgement period) */ - /* length = 1 */ - /* [29] */ - 0x01, -}; - -static int abis_nm_download_req(struct gsm_bts *bts, uint16_t ref) -{ - uint8_t *data = download_req; - int len_data = sizeof(download_req); - - return abis_nm_send(bts, NOKIA_MSG_START_DOWNLOAD_REQ, ref, data, - len_data); -} - -/* TODO: put in a separate file ? */ - -static uint8_t ack[] = { - 0x5F, 0x23, 0x01, - /* ID = 0x23 (Ack-Nack) */ - /* length = 1 */ - /* [3] */ - 0x01, -}; - -static int abis_nm_ack(struct gsm_bts *bts, uint16_t ref) -{ - uint8_t *data = ack; - int len_data = sizeof(ack); - - return abis_nm_send(bts, NOKIA_MSG_ACK, ref, data, len_data); -} - -/* TODO: put in a separate file ? */ - -static uint8_t reset[] = { - 0x5F, 0x40, 0x04, - /* ID = 0x40 (Object identity) */ - /* length = 4 */ - /* [3] */ - 0x00, 0x01, 0xFF, 0xFF, -}; - -static int abis_nm_reset(struct gsm_bts *bts, uint16_t ref) -{ - uint8_t *data = reset; - int len_data = sizeof(reset); - LOGP(DLINP, LOGL_INFO, "Nokia BTS reset timer: %d\n", bts->nokia.bts_reset_timer_cnf); - return abis_nm_send(bts, NOKIA_MSG_RESET_REQ, ref, data, len_data); -} - -/* TODO: put in a separate file ? */ - -static int abis_nm_send_multi_segments(struct gsm_bts *bts, uint8_t msg_type, - uint16_t ref, uint8_t * data, int len) -{ - int len_remain, len_to_send, max_send; - int seq = 0; - int ret; - - len_remain = len; - - while (len_remain) { - struct abis_om_hdr *oh; - struct abis_om_nokia_hdr *noh; - struct msgb *msg = nm_msgb_alloc(); - - if (seq == 0) - max_send = 256 - sizeof(struct abis_om_nokia_hdr); - else - max_send = 256; - - if (len_remain > max_send) { - len_to_send = max_send; - - if (seq == 0) { - /* first segment */ - oh = (struct abis_om_hdr *)msgb_put(msg, - ABIS_OM_NOKIA_HDR_SIZE - + - len_to_send); - - oh->mdisc = ABIS_OM_MDISC_FOM; - oh->placement = ABIS_OM_PLACEMENT_FIRST; /* first segment of multi-segment message */ - oh->sequence = seq; - oh->length = 0; /* 256 bytes */ - - noh = (struct abis_om_nokia_hdr *)oh->data; - - noh->msg_type = msg_type; - noh->spare = 0; - noh->reference = htons(ref); - memcpy(noh->data, data, len_to_send); - } else { - /* segment in between */ - oh = (struct abis_om_hdr *)msgb_put(msg, - sizeof - (struct - abis_om_hdr) - + - len_to_send); - - oh->mdisc = ABIS_OM_MDISC_FOM; - oh->placement = ABIS_OM_PLACEMENT_MIDDLE; /* segment of multi-segment message */ - oh->sequence = seq; - oh->length = 0; /* 256 bytes */ - - memcpy(oh->data, data, len_to_send); - } - } else { - - len_to_send = len_remain; - - /* check if message fits in a single segment */ - - if (seq == 0) - return abis_nm_send(bts, msg_type, ref, data, - len_to_send); - - /* last segment */ - - oh = (struct abis_om_hdr *)msgb_put(msg, - sizeof(struct - abis_om_hdr) - + len_to_send); - - oh->mdisc = ABIS_OM_MDISC_FOM; - oh->placement = ABIS_OM_PLACEMENT_LAST; /* last segment of multi-segment message */ - oh->sequence = seq; - oh->length = len_to_send; - - memcpy(oh->data, data, len_to_send); - } - - DEBUGPC(DNM, "Sending multi-segment %d\n", seq); - - ret = abis_nm_sendmsg(bts, msg); - if (ret < 0) - return ret; - - nokia_abis_nm_queue_send_next(bts); - - /* next segment */ - len_remain -= len_to_send; - data += len_to_send; - seq++; - } - return ret; -} - -/* TODO: put in a separate file ? */ - -static int abis_nm_send_config(struct gsm_bts *bts, uint8_t bts_type) -{ - struct gsm_bts_trx *trx; - uint8_t config[2048]; /* TODO: might be too small if lots of TRX are used */ - int len = 0; - int idx = 0; - int ret; - int hopping = 0; - int need_hopping = 0; - - memset(config, 0, sizeof(config)); - - llist_for_each_entry(trx, &bts->trx_list, list) { -#if 0 /* debugging */ - printf("TRX\n"); - printf(" arfcn: %d\n", trx->arfcn); - printf(" bsic: %d\n", trx->bts->bsic); - uint8_t ca[20]; - memset(ca, 0xFF, sizeof(ca)); - ret = generate_cell_chan_list(ca, trx->bts); - printf(" ca (%d): %s\n", ret, osmo_hexdump(ca, sizeof(ca))); - int i; - for (i = 0; i < ARRAY_SIZE(trx->ts); i++) { - struct gsm_bts_trx_ts *ts = &trx->ts[i]; - - printf(" pchan %d: %d\n", i, ts->pchan); - } -#endif - ret = make_fu_config(trx, idx + 1, config + len, &hopping); - need_hopping |= hopping; - len += ret; - - idx++; - } - - ret = make_bts_config(bts_type, idx, config + len, need_hopping); - len += ret; - -#if 0 /* debugging */ - dump_elements(config, len); -#endif - - return abis_nm_send_multi_segments(bts, NOKIA_MSG_CONF_DATA, 1, config, - len); -} - -#define GET_NEXT_BYTE if(idx >= len) return 0; \ - ub = data[idx++]; - -static int find_element(uint8_t * data, int len, uint16_t id, uint8_t * value, - int max_value) -{ - uint8_t ub; - int idx = 0; - int found = 0; - int constructed __attribute__((unused)); - uint16_t id_value; - - for (;;) { - - GET_NEXT_BYTE; - - /* encoding bit, construced means that other elements are contained */ - constructed = ((ub & 0x20) ? 1 : 0); - - if ((ub & 0x1F) == 0x1F) { - /* fixed pattern, ID follows */ - GET_NEXT_BYTE; /* ID */ - id_value = ub & 0x7F; - if (ub & 0x80) { - /* extension bit */ - GET_NEXT_BYTE; /* ID low part */ - id_value = (id_value << 7) | (ub & 0x7F); - } - if (id_value == id) - found = 1; - } else { - id_value = (ub & 0x3F); - if (id_value == id) - found = 1; - } - - GET_NEXT_BYTE; /* length */ - - if (found) { - /* get data */ - uint8_t n = ub; - uint8_t i; - for (i = 0; i < n; i++) { - GET_NEXT_BYTE; - if (max_value <= 0) - return -1; /* buffer too small */ - *value = ub; - value++; - max_value--; - } - return n; /* length */ - } else { - /* skip data */ - uint8_t n = ub; - uint8_t i; - for (i = 0; i < n; i++) { - GET_NEXT_BYTE; - } - } - } - return 0; /* not found */ -} - -static int dump_elements(uint8_t * data, int len) -{ - uint8_t ub; - int idx = 0; - int constructed; - uint16_t id_value; - static char indent[100] = ""; /* TODO: move static to BTS context */ - - for (;;) { - - GET_NEXT_BYTE; - - /* encoding bit, construced means that other elements are contained */ - constructed = ((ub & 0x20) ? 1 : 0); - - if ((ub & 0x1F) == 0x1F) { - /* fixed pattern, ID follows */ - GET_NEXT_BYTE; /* ID */ - id_value = ub & 0x7F; - if (ub & 0x80) { - /* extension bit */ - GET_NEXT_BYTE; /* ID low part */ - id_value = (id_value << 7) | (ub & 0x7F); - } - - } else { - id_value = (ub & 0x3F); - } - - GET_NEXT_BYTE; /* length */ - - printf("%s--ID = 0x%02X (%s) %s\n", indent, id_value, - get_element_name_string(id_value), - constructed ? "** constructed **" : ""); - printf("%s length = %d\n", indent, ub); - printf("%s %s\n", indent, osmo_hexdump(data + idx, ub)); - - if (constructed) { - int indent_len = strlen(indent); - strcat(indent, " "); - - dump_elements(data + idx, ub); - - indent[indent_len] = 0; - } - /* skip data */ - uint8_t n = ub; - uint8_t i; - for (i = 0; i < n; i++) { - GET_NEXT_BYTE; - } - } - return 0; -} - -/* TODO: put in a separate file ? */ - -/* taken from abis_nm.c */ - -static void nokia_abis_nm_queue_send_next(struct gsm_bts *bts) -{ - int wait = 0; - struct msgb *msg; - /* the queue is empty */ - while (!llist_empty(&bts->abis_queue)) { - msg = msgb_dequeue(&bts->abis_queue); - wait = OBSC_NM_W_ACK_CB(msg); - abis_sendmsg(msg); - - if (wait) - break; - } - - bts->abis_nm_pend = wait; -} - -/* TODO: put in a separate file ? */ - -/* timer for restarting OML after BTS reset */ - -static void reset_timer_cb(void *_bts) -{ - struct gsm_bts *bts = _bts; - struct gsm_e1_subslot *e1_link = &bts->oml_e1_link; - struct e1inp_line *line; - - bts->nokia.wait_reset = 0; - - /* OML link */ - line = e1inp_line_find(e1_link->e1_nr); - if (!line) { - LOGP(DLINP, LOGL_ERROR, "BTS %u OML link referring to " - "non-existing E1 line %u\n", bts->nr, e1_link->e1_nr); - return; - } - - start_sabm_in_line(line, 0, -1); /* stop all first */ - start_sabm_in_line(line, 1, SAPI_OML); /* start only OML */ -} - -/* TODO: put in a separate file ? */ - -/* - This is how the configuration is done: - - start OML link - - reset BTS - - receive ACK, wait some time and restart OML link - - receive OMU STARTED message, send START DOWNLOAD REQ - - receive CNF REQ message, send CONF DATA - - receive ACK, start RSL link(s) - ACK some other messages received from the BTS. - - Probably its also possible to configure the BTS without a reset, this - has not been tested yet. -*/ - -static int abis_nm_rcvmsg_fom(struct msgb *mb) -{ - struct e1inp_sign_link *sign_link = (struct e1inp_sign_link *)mb->dst; - struct gsm_bts *bts = sign_link->trx->bts; - struct abis_om_hdr *oh = msgb_l2(mb); - struct abis_om_nokia_hdr *noh = msgb_l3(mb); - uint8_t mt = noh->msg_type; - int ret = 0; - uint16_t ref = ntohs(noh->reference); - uint8_t info[256]; - uint8_t ack = 0xFF; - uint8_t severity = 0xFF; - int str_len; - int len_data; - - if (bts->nokia.wait_reset) { - LOGP(DNM, LOGL_INFO, - "Ignore message while waiting for reset\n"); - return ret; - } - - if (oh->length < sizeof(struct abis_om_nokia_hdr)) { - LOGP(DNM, LOGL_ERROR, "Message too short\n"); - return -EINVAL; - } - - len_data = oh->length - sizeof(struct abis_om_nokia_hdr); - LOGP(DNM, LOGL_INFO, "(0x%02X) %s\n", mt, get_msg_type_name_string(mt)); -#if 0 /* debugging */ - dump_elements(noh->data, len_data); -#endif - - switch (mt) { - case NOKIA_MSG_OMU_STARTED: - if (find_element(noh->data, len_data, - NOKIA_EI_BTS_TYPE, &bts->nokia.bts_type, - sizeof(uint8_t)) == sizeof(uint8_t)) - LOGP(DNM, LOGL_INFO, "BTS type = %d (%s)\n", - bts->nokia.bts_type, - get_bts_type_string(bts->nokia.bts_type)); - else - LOGP(DNM, LOGL_ERROR, "BTS type not found\n"); - /* send START_DOWNLOAD_REQ */ - abis_nm_download_req(bts, ref); - break; - case NOKIA_MSG_MF_REQ: - break; - case NOKIA_MSG_CONF_REQ: - /* send ACK */ - abis_nm_ack(bts, ref); - nokia_abis_nm_queue_send_next(bts); - /* send CONF_DATA */ - abis_nm_send_config(bts, bts->nokia.bts_type); - bts->nokia.configured = 1; - break; - case NOKIA_MSG_ACK: - if (find_element - (noh->data, len_data, NOKIA_EI_ACK, &ack, - sizeof(uint8_t)) == sizeof(uint8_t)) { - LOGP(DNM, LOGL_INFO, "ACK = %d\n", ack); - if (ack != 1) { - LOGP(DNM, LOGL_ERROR, "No ACK received (%d)\n", - ack); - /* TODO: properly handle failures (NACK) */ - } - } else - LOGP(DNM, LOGL_ERROR, "ACK not found\n"); - - /* TODO: the assumption for the following is that no NACK was received */ - - /* ACK for reset message ? */ - if (!bts->nokia.did_reset) { - bts->nokia.did_reset = 1; - - /* - TODO: For the InSite processing the received data is - blocked in the driver during reset. - Otherwise the LAPD module might assert because the InSite - sends garbage on the E1 line during reset. - This is done by looking at "wait_reset" in the driver - (function handle_ts1_read()) and ignoring the received data. - It seems to be necessary for the MetroSite too. - */ - bts->nokia.wait_reset = 1; - - osmo_timer_setup(&bts->nokia.reset_timer, - reset_timer_cb, bts); - osmo_timer_schedule(&bts->nokia.reset_timer, bts->nokia.bts_reset_timer_cnf, 0); - - struct gsm_e1_subslot *e1_link = &bts->oml_e1_link; - struct e1inp_line *line; - /* OML link */ - line = e1inp_line_find(e1_link->e1_nr); - if (!line) { - LOGP(DLINP, LOGL_ERROR, - "BTS %u OML link referring to " - "non-existing E1 line %u\n", bts->nr, - e1_link->e1_nr); - return -ENOMEM; - } - - start_sabm_in_line(line, 0, -1); /* stop all first */ - } - - /* ACK for CONF DATA message ? */ - if (bts->nokia.configured != 0) { - /* start TRX (RSL link) */ - - struct gsm_e1_subslot *e1_link = - &sign_link->trx->rsl_e1_link; - struct e1inp_line *line; - - bts->nokia.configured = 0; - - /* RSL Link */ - line = e1inp_line_find(e1_link->e1_nr); - if (!line) { - LOGP(DLINP, LOGL_ERROR, - "TRX (%u/%u) RSL link referring " - "to non-existing E1 line %u\n", - sign_link->trx->bts->nr, sign_link->trx->nr, - e1_link->e1_nr); - return -ENOMEM; - } - /* start TRX */ - start_sabm_in_line(line, 1, SAPI_RSL); /* start only RSL */ - } - break; - case NOKIA_MSG_STATE_CHANGED: - /* send ACK */ - abis_nm_ack(bts, ref); - break; - case NOKIA_MSG_CONF_COMPLETE: - /* send ACK */ - abis_nm_ack(bts, ref); - break; - case NOKIA_MSG_BLOCK_CTRL_REQ: /* seems to be send when something goes wrong !? */ - /* send ACK (do we have to send an ACK ?) */ - abis_nm_ack(bts, ref); - break; - case NOKIA_MSG_ALARM: - find_element(noh->data, len_data, NOKIA_EI_SEVERITY, &severity, - sizeof(severity)); - /* TODO: there might be alarms with both elements set */ - str_len = - find_element(noh->data, len_data, NOKIA_EI_ADD_INFO, info, - sizeof(info)); - if (str_len > 0) { - info[str_len] = 0; - LOGP(DNM, LOGL_INFO, "ALARM Severity %s (%d) : %s\n", - get_severity_string(severity), severity, info); - } else { /* nothing found, try details */ - str_len = - find_element(noh->data, len_data, - NOKIA_EI_ALARM_DETAIL, info, - sizeof(info)); - if (str_len > 0) { - uint16_t code; - info[str_len] = 0; - code = (info[0] << 8) + info[1]; - LOGP(DNM, LOGL_INFO, - "ALARM Severity %s (%d), code 0x%X : %s\n", - get_severity_string(severity), severity, - code, info + 2); - } - } - /* send ACK */ - abis_nm_ack(bts, ref); - break; - } - - nokia_abis_nm_queue_send_next(bts); - - return ret; -} - -/* TODO: put in a separate file ? */ - -int abis_nokia_rcvmsg(struct msgb *msg) -{ - struct abis_om_hdr *oh = msgb_l2(msg); - int rc = 0; - - /* Various consistency checks */ - if (oh->placement != ABIS_OM_PLACEMENT_ONLY) { - LOGP(DNM, LOGL_ERROR, "ABIS OML placement 0x%x not supported\n", - oh->placement); - if (oh->placement != ABIS_OM_PLACEMENT_FIRST) - return -EINVAL; - } - if (oh->sequence != 0) { - LOGP(DNM, LOGL_ERROR, "ABIS OML sequence 0x%x != 0x00\n", - oh->sequence); - return -EINVAL; - } - msg->l3h = (unsigned char *)oh + sizeof(*oh); - - switch (oh->mdisc) { - case ABIS_OM_MDISC_FOM: - LOGP(DNM, LOGL_INFO, "ABIS_OM_MDISC_FOM\n"); - rc = abis_nm_rcvmsg_fom(msg); - break; - case ABIS_OM_MDISC_MANUF: - LOGP(DNM, LOGL_INFO, "ABIS_OM_MDISC_MANUF\n"); - break; - case ABIS_OM_MDISC_MMI: - case ABIS_OM_MDISC_TRAU: - LOGP(DNM, LOGL_ERROR, - "unimplemented ABIS OML message discriminator 0x%x\n", - oh->mdisc); - break; - default: - LOGP(DNM, LOGL_ERROR, - "unknown ABIS OML message discriminator 0x%x\n", - oh->mdisc); - return -EINVAL; - } - - msgb_free(msg); - return rc; -} - -static int bts_model_nokia_site_start(struct gsm_network *net); - -static void bts_model_nokia_site_e1line_bind_ops(struct e1inp_line *line) -{ - e1inp_line_bind_ops(line, &bts_isdn_e1inp_line_ops); -} - -static struct gsm_bts_model model_nokia_site = { - .type = GSM_BTS_TYPE_NOKIA_SITE, - .name = "nokia_site", - .start = bts_model_nokia_site_start, - .oml_rcvmsg = &abis_nokia_rcvmsg, - .e1line_bind_ops = &bts_model_nokia_site_e1line_bind_ops, -}; - -static struct gsm_network *my_net; - -static int bts_model_nokia_site_start(struct gsm_network *net) -{ - model_nokia_site.features.data = &model_nokia_site._features_data[0]; - model_nokia_site.features.data_len = - sizeof(model_nokia_site._features_data); - - gsm_btsmodel_set_feature(&model_nokia_site, BTS_FEAT_HOPPING); - gsm_btsmodel_set_feature(&model_nokia_site, BTS_FEAT_HSCSD); - gsm_btsmodel_set_feature(&model_nokia_site, BTS_FEAT_MULTI_TSC); - - osmo_signal_register_handler(SS_L_INPUT, inp_sig_cb, NULL); - osmo_signal_register_handler(SS_L_GLOBAL, gbl_sig_cb, NULL); - osmo_signal_register_handler(SS_NM, nm_sig_cb, NULL); - - my_net = net; - - return 0; -} - -int bts_model_nokia_site_init(void) -{ - return gsm_bts_model_register(&model_nokia_site); -} diff --git a/openbsc/src/libbsc/bts_siemens_bs11.c b/openbsc/src/libbsc/bts_siemens_bs11.c deleted file mode 100644 index c083b1e0..00000000 --- a/openbsc/src/libbsc/bts_siemens_bs11.c +++ /dev/null @@ -1,602 +0,0 @@ -/* Siemens BS-11 specific code */ - -/* (C) 2009-2010 by Harald Welte - * - * All Rights Reserved - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation; either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - * - */ - - -#include - -#include -#include -#include -#include -#include - -static int bts_model_bs11_start(struct gsm_network *net); - -static void bts_model_bs11_e1line_bind_ops(struct e1inp_line *line) -{ - e1inp_line_bind_ops(line, &bts_isdn_e1inp_line_ops); -} - -static struct gsm_bts_model model_bs11 = { - .type = GSM_BTS_TYPE_BS11, - .name = "bs11", - .start = bts_model_bs11_start, - .oml_rcvmsg = &abis_nm_rcvmsg, - .e1line_bind_ops = bts_model_bs11_e1line_bind_ops, - .nm_att_tlvdef = { - .def = { - [NM_ATT_AVAIL_STATUS] = { TLV_TYPE_TLV }, - /* BS11 specifics */ - [NM_ATT_BS11_ESN_FW_CODE_NO] = { TLV_TYPE_TLV }, - [NM_ATT_BS11_ESN_HW_CODE_NO] = { TLV_TYPE_TLV }, - [NM_ATT_BS11_ESN_PCB_SERIAL] = { TLV_TYPE_TLV }, - [NM_ATT_BS11_BOOT_SW_VERS] = { TLV_TYPE_TLV }, - [0xd5] = { TLV_TYPE_TLV }, - [0xa8] = { TLV_TYPE_TLV }, - [NM_ATT_BS11_PASSWORD] = { TLV_TYPE_TLV }, - [NM_ATT_BS11_TXPWR] = { TLV_TYPE_TLV }, - [NM_ATT_BS11_RSSI_OFFS] = { TLV_TYPE_TLV }, - [NM_ATT_BS11_LINE_CFG] = { TLV_TYPE_TV }, - [NM_ATT_BS11_L1_PROT_TYPE] = { TLV_TYPE_TV }, - [NM_ATT_BS11_BIT_ERR_THESH] = { TLV_TYPE_FIXED, 2 }, - [NM_ATT_BS11_DIVERSITY] = { TLV_TYPE_TLV }, - [NM_ATT_BS11_LMT_LOGON_SESSION]={ TLV_TYPE_TLV }, - [NM_ATT_BS11_LMT_LOGIN_TIME] = { TLV_TYPE_TLV }, - [NM_ATT_BS11_LMT_USER_ACC_LEV] ={ TLV_TYPE_TLV }, - [NM_ATT_BS11_LMT_USER_NAME] = { TLV_TYPE_TLV }, - [NM_ATT_BS11_BTS_STATE] = { TLV_TYPE_TLV }, - [NM_ATT_BS11_E1_STATE] = { TLV_TYPE_TLV }, - [NM_ATT_BS11_PLL_MODE] = { TLV_TYPE_TLV }, - [NM_ATT_BS11_PLL] = { TLV_TYPE_TLV }, - [NM_ATT_BS11_CCLK_ACCURACY] = { TLV_TYPE_TV }, - [NM_ATT_BS11_CCLK_TYPE] = { TLV_TYPE_TV }, - [0x95] = { TLV_TYPE_FIXED, 2 }, - }, - }, -}; - -/* The following definitions are for OM and NM packets that we cannot yet - * generate by code but we just pass on */ - -// BTS Site Manager, SET ATTRIBUTES - -/* - Object Class: BTS Site Manager - Instance 1: FF - Instance 2: FF - Instance 3: FF -SET ATTRIBUTES - sAbisExternalTime: 2007/09/08 14:36:11 - omLAPDRelTimer: 30sec - shortLAPDIntTimer: 5sec - emergencyTimer1: 10 minutes - emergencyTimer2: 0 minutes -*/ - -unsigned char msg_1[] = -{ - NM_MT_BS11_SET_ATTR, NM_OC_SITE_MANAGER, 0xFF, 0xFF, 0xFF, - NM_ATT_BS11_ABIS_EXT_TIME, 0x07, - 0xD7, 0x09, 0x08, 0x0E, 0x24, 0x0B, 0xCE, - 0x02, - 0x00, 0x1E, - NM_ATT_BS11_SH_LAPD_INT_TIMER, - 0x01, 0x05, - 0x42, 0x02, 0x00, 0x0A, - 0x44, 0x02, 0x00, 0x00 -}; - -// BTS, SET BTS ATTRIBUTES - -/* - Object Class: BTS - BTS relat. Number: 0 - Instance 2: FF - Instance 3: FF -SET BTS ATTRIBUTES - bsIdentityCode / BSIC: - PLMN_colour_code: 7h - BS_colour_code: 7h - BTS Air Timer T3105: 4 ,unit 10 ms - btsIsHopping: FALSE - periodCCCHLoadIndication: 1sec - thresholdCCCHLoadIndication: 0% - cellAllocationNumber: 00h = GSM 900 - enableInterferenceClass: 00h = Disabled - fACCHQual: 6 (FACCH stealing flags minus 1) - intaveParameter: 31 SACCH multiframes - interferenceLevelBoundaries: - Interference Boundary 1: 0Ah - Interference Boundary 2: 0Fh - Interference Boundary 3: 14h - Interference Boundary 4: 19h - Interference Boundary 5: 1Eh - mSTxPwrMax: 11 - GSM range: 2=39dBm, 15=13dBm, stepsize 2 dBm - DCS1800 range: 0=30dBm, 15=0dBm, stepsize 2 dBm - PCS1900 range: 0=30dBm, 15=0dBm, stepsize 2 dBm - 30=33dBm, 31=32dBm - ny1: - Maximum number of repetitions for PHYSICAL INFORMATION message (GSM 04.08): 20 - powerOutputThresholds: - Out Power Fault Threshold: -10 dB - Red Out Power Threshold: - 6 dB - Excessive Out Power Threshold: 5 dB - rACHBusyThreshold: -127 dBm - rACHLoadAveragingSlots: 250 ,number of RACH burst periods - rfResourceIndicationPeriod: 125 SACCH multiframes - T200: - SDCCH: 044 in 5 ms - FACCH/Full rate: 031 in 5 ms - FACCH/Half rate: 041 in 5 ms - SACCH with TCH SAPI0: 090 in 10 ms - SACCH with SDCCH: 090 in 10 ms - SDCCH with SAPI3: 090 in 5 ms - SACCH with TCH SAPI3: 135 in 10 ms - tSync: 9000 units of 10 msec - tTrau: 9000 units of 10 msec - enableUmLoopTest: 00h = disabled - enableExcessiveDistance: 00h = Disabled - excessiveDistance: 64km - hoppingMode: 00h = baseband hopping - cellType: 00h = Standard Cell - BCCH ARFCN / bCCHFrequency: 1 -*/ - -static unsigned char bs11_attr_bts[] = -{ - NM_ATT_BSIC, HARDCODED_BSIC, - NM_ATT_BTS_AIR_TIMER, 0x04, - NM_ATT_BS11_BTSLS_HOPPING, 0x00, - NM_ATT_CCCH_L_I_P, 0x01, - NM_ATT_CCCH_L_T, 0x00, - NM_ATT_BS11_CELL_ALLOC_NR, NM_BS11_CANR_GSM, - NM_ATT_BS11_ENA_INTERF_CLASS, 0x01, - NM_ATT_BS11_FACCH_QUAL, 0x06, - /* interference avg. period in numbers of SACCH multifr */ - NM_ATT_INTAVE_PARAM, 0x1F, - NM_ATT_INTERF_BOUND, 0x0A, 0x0F, 0x14, 0x19, 0x1E, 0x7B, - NM_ATT_CCCH_L_T, 0x23, - NM_ATT_GSM_TIME, 0x28, 0x00, - NM_ATT_ADM_STATE, 0x03, - NM_ATT_RACH_B_THRESH, 0x7F, - NM_ATT_LDAVG_SLOTS, 0x00, 0xFA, - NM_ATT_BS11_RF_RES_IND_PER, 0x7D, - NM_ATT_T200, 0x2C, 0x1F, 0x29, 0x5A, 0x5A, 0x5A, 0x87, - NM_ATT_BS11_TSYNC, 0x23, 0x28, - NM_ATT_BS11_TTRAU, 0x23, 0x28, - NM_ATT_TEST_DUR, 0x01, 0x00, - NM_ATT_OUTST_ALARM, 0x01, 0x00, - NM_ATT_BS11_EXCESSIVE_DISTANCE, 0x01, 0x40, - NM_ATT_BS11_HOPPING_MODE, 0x01, 0x00, - NM_ATT_BS11_PLL, 0x01, 0x00, - NM_ATT_BCCH_ARFCN, 0x00, HARDCODED_ARFCN/*0x01*/, -}; - -// Handover Recognition, SET ATTRIBUTES - -/* -Illegal Contents GSM Formatted O&M Msg - Object Class: Handover Recognition - BTS relat. Number: 0 - Instance 2: FF - Instance 3: FF -SET ATTRIBUTES - enableDelayPowerBudgetHO: 00h = Disabled - enableDistanceHO: 00h = Disabled - enableInternalInterCellHandover: 00h = Disabled - enableInternalIntraCellHandover: 00h = Disabled - enablePowerBudgetHO: 00h = Disabled - enableRXLEVHO: 00h = Disabled - enableRXQUALHO: 00h = Disabled - hoAveragingDistance: 8 SACCH multiframes - hoAveragingLev: - A_LEV_HO: 8 SACCH multiframes - W_LEV_HO: 1 SACCH multiframes - hoAveragingPowerBudget: 16 SACCH multiframes - hoAveragingQual: - A_QUAL_HO: 8 SACCH multiframes - W_QUAL_HO: 2 SACCH multiframes - hoLowerThresholdLevDL: (10 - 110) dBm - hoLowerThresholdLevUL: (5 - 110) dBm - hoLowerThresholdQualDL: 06h = 6.4% < BER < 12.8% - hoLowerThresholdQualUL: 06h = 6.4% < BER < 12.8% - hoThresholdLevDLintra : (20 - 110) dBm - hoThresholdLevULintra: (20 - 110) dBm - hoThresholdMsRangeMax: 20 km - nCell: 06h - timerHORequest: 3 ,unit 2 SACCH multiframes -*/ - -unsigned char msg_3[] = -{ - NM_MT_BS11_SET_ATTR, NM_OC_BS11_HANDOVER, 0x00, 0xFF, 0xFF, - 0xD0, 0x00, /* enableDelayPowerBudgetHO */ - 0x64, 0x00, /* enableDistanceHO */ - 0x67, 0x00, /* enableInternalInterCellHandover */ - 0x68, 0x00, /* enableInternalInterCellHandover */ - 0x6A, 0x00, /* enablePowerBudgetHO */ - 0x6C, 0x00, /* enableRXLEVHO */ - 0x6D, 0x00, /* enableRXQUALHO */ - 0x6F, 0x08, /* hoAveragingDistance */ - 0x70, 0x08, 0x01, /* hoAveragingLev */ - 0x71, 0x10, 0x10, 0x10, - 0x72, 0x08, 0x02, /* hoAveragingQual */ - 0x73, 0x0A, /* hoLowerThresholdLevDL */ - 0x74, 0x05, /* hoLowerThresholdLevUL */ - 0x75, 0x06, /* hoLowerThresholdQualDL */ - 0x76, 0x06, /* hoLowerThresholdQualUL */ - 0x78, 0x14, /* hoThresholdLevDLintra */ - 0x79, 0x14, /* hoThresholdLevULintra */ - 0x7A, 0x14, /* hoThresholdMsRangeMax */ - 0x7D, 0x06, /* nCell */ - NM_ATT_BS11_TIMER_HO_REQUEST, 0x03, - 0x20, 0x01, 0x00, - 0x45, 0x01, 0x00, - 0x48, 0x01, 0x00, - 0x5A, 0x01, 0x00, - 0x5B, 0x01, 0x05, - 0x5E, 0x01, 0x1A, - 0x5F, 0x01, 0x20, - 0x9D, 0x01, 0x00, - 0x47, 0x01, 0x00, - 0x5C, 0x01, 0x64, - 0x5D, 0x01, 0x1E, - 0x97, 0x01, 0x20, - 0xF7, 0x01, 0x3C, -}; - -// Power Control, SET ATTRIBUTES - -/* - Object Class: Power Control - BTS relat. Number: 0 - Instance 2: FF - Instance 3: FF -SET ATTRIBUTES - enableMsPowerControl: 00h = Disabled - enablePowerControlRLFW: 00h = Disabled - pcAveragingLev: - A_LEV_PC: 4 SACCH multiframes - W_LEV_PC: 1 SACCH multiframes - pcAveragingQual: - A_QUAL_PC: 4 SACCH multiframes - W_QUAL_PC: 2 SACCH multiframes - pcLowerThresholdLevDL: 0Fh - pcLowerThresholdLevUL: 0Ah - pcLowerThresholdQualDL: 05h = 3.2% < BER < 6.4% - pcLowerThresholdQualUL: 05h = 3.2% < BER < 6.4% - pcRLFThreshold: 0Ch - pcUpperThresholdLevDL: 14h - pcUpperThresholdLevUL: 0Fh - pcUpperThresholdQualDL: 04h = 1.6% < BER < 3.2% - pcUpperThresholdQualUL: 04h = 1.6% < BER < 3.2% - powerConfirm: 2 ,unit 2 SACCH multiframes - powerControlInterval: 2 ,unit 2 SACCH multiframes - powerIncrStepSize: 02h = 4 dB - powerRedStepSize: 01h = 2 dB - radioLinkTimeoutBs: 64 SACCH multiframes - enableBSPowerControl: 00h = disabled -*/ - -unsigned char msg_4[] = -{ - NM_MT_BS11_SET_ATTR, NM_OC_BS11_PWR_CTRL, 0x00, 0xFF, 0xFF, - NM_ATT_BS11_ENA_MS_PWR_CTRL, 0x00, - NM_ATT_BS11_ENA_PWR_CTRL_RLFW, 0x00, - 0x7E, 0x04, 0x01, /* pcAveragingLev */ - 0x7F, 0x04, 0x02, /* pcAveragingQual */ - 0x80, 0x0F, /* pcLowerThresholdLevDL */ - 0x81, 0x0A, /* pcLowerThresholdLevUL */ - 0x82, 0x05, /* pcLowerThresholdQualDL */ - 0x83, 0x05, /* pcLowerThresholdQualUL */ - 0x84, 0x0C, /* pcRLFThreshold */ - 0x85, 0x14, /* pcUpperThresholdLevDL */ - 0x86, 0x0F, /* pcUpperThresholdLevUL */ - 0x87, 0x04, /* pcUpperThresholdQualDL */ - 0x88, 0x04, /* pcUpperThresholdQualUL */ - 0x89, 0x02, /* powerConfirm */ - 0x8A, 0x02, /* powerConfirmInterval */ - 0x8B, 0x02, /* powerIncrStepSize */ - 0x8C, 0x01, /* powerRedStepSize */ - 0x8D, 0x40, /* radioLinkTimeoutBs */ - 0x65, 0x01, 0x00 // set to 0x01 to enable BSPowerControl -}; - - -// Transceiver, SET TRX ATTRIBUTES (TRX 0) - -/* - Object Class: Transceiver - BTS relat. Number: 0 - Tranceiver number: 0 - Instance 3: FF -SET TRX ATTRIBUTES - aRFCNList (HEX): 0001 - txPwrMaxReduction: 00h = 30dB - radioMeasGran: 254 SACCH multiframes - radioMeasRep: 01h = enabled - memberOfEmergencyConfig: 01h = TRUE - trxArea: 00h = TRX doesn't belong to a concentric cell -*/ - -static unsigned char bs11_attr_radio[] = -{ - NM_ATT_ARFCN_LIST, 0x01, 0x00, HARDCODED_ARFCN /*0x01*/, - NM_ATT_RF_MAXPOWR_R, 0x00, - NM_ATT_BS11_RADIO_MEAS_GRAN, 0x01, 0x05, - NM_ATT_BS11_RADIO_MEAS_REP, 0x01, 0x01, - NM_ATT_BS11_EMRG_CFG_MEMBER, 0x01, 0x01, - NM_ATT_BS11_TRX_AREA, 0x01, 0x00, -}; - -/* - * Patch the various SYSTEM INFORMATION tables to update - * the LAI - */ -static void patch_nm_tables(struct gsm_bts *bts) -{ - uint8_t arfcn_low = bts->c0->arfcn & 0xff; - uint8_t arfcn_high = (bts->c0->arfcn >> 8) & 0x0f; - - /* T3105 attribute in units of 10ms */ - bs11_attr_bts[2] = bts->network->T3105 / 10; - - /* patch ARFCN into BTS Attributes */ - bs11_attr_bts[69] &= 0xf0; - bs11_attr_bts[69] |= arfcn_high; - bs11_attr_bts[70] = arfcn_low; - - /* patch ARFCN into TRX Attributes */ - bs11_attr_radio[2] &= 0xf0; - bs11_attr_radio[2] |= arfcn_high; - bs11_attr_radio[3] = arfcn_low; - - /* patch the RACH attributes */ - if (bts->rach_b_thresh != -1) - bs11_attr_bts[33] = bts->rach_b_thresh & 0xff; - - if (bts->rach_ldavg_slots != -1) { - uint8_t avg_high = bts->rach_ldavg_slots & 0xff; - uint8_t avg_low = (bts->rach_ldavg_slots >> 8) & 0x0f; - - bs11_attr_bts[35] = avg_high; - bs11_attr_bts[36] = avg_low; - } - - /* patch BSIC */ - bs11_attr_bts[1] = bts->bsic; - - /* patch the power reduction */ - bs11_attr_radio[5] = bts->c0->max_power_red / 2; -} - - -static void nm_reconfig_ts(struct gsm_bts_trx_ts *ts) -{ - enum abis_nm_chan_comb ccomb = abis_nm_chcomb4pchan(ts->pchan); - struct gsm_e1_subslot *e1l = &ts->e1_link; - - abis_nm_set_channel_attr(ts, ccomb); - - if (is_ipaccess_bts(ts->trx->bts)) - return; - - if (ts_is_tch(ts)) - abis_nm_conn_terr_traf(ts, e1l->e1_nr, e1l->e1_ts, - e1l->e1_ts_ss); -} - -static void nm_reconfig_trx(struct gsm_bts_trx *trx) -{ - struct gsm_e1_subslot *e1l = &trx->rsl_e1_link; - int i; - - patch_nm_tables(trx->bts); - - switch (trx->bts->type) { - case GSM_BTS_TYPE_BS11: - /* FIXME: discover this by fetching an attribute */ -#if 0 - trx->nominal_power = 15; /* 15dBm == 30mW PA configuration */ -#else - trx->nominal_power = 24; /* 24dBm == 250mW PA configuration */ -#endif - abis_nm_conn_terr_sign(trx, e1l->e1_nr, e1l->e1_ts, - e1l->e1_ts_ss); - abis_nm_establish_tei(trx->bts, trx->nr, e1l->e1_nr, - e1l->e1_ts, e1l->e1_ts_ss, trx->rsl_tei); - - /* Set Radio Attributes */ - if (trx == trx->bts->c0) - abis_nm_set_radio_attr(trx, bs11_attr_radio, - sizeof(bs11_attr_radio)); - else { - uint8_t trx1_attr_radio[sizeof(bs11_attr_radio)]; - uint8_t arfcn_low = trx->arfcn & 0xff; - uint8_t arfcn_high = (trx->arfcn >> 8) & 0x0f; - memcpy(trx1_attr_radio, bs11_attr_radio, - sizeof(trx1_attr_radio)); - - /* patch ARFCN into TRX Attributes */ - trx1_attr_radio[2] &= 0xf0; - trx1_attr_radio[2] |= arfcn_high; - trx1_attr_radio[3] = arfcn_low; - - abis_nm_set_radio_attr(trx, trx1_attr_radio, - sizeof(trx1_attr_radio)); - } - break; - case GSM_BTS_TYPE_NANOBTS: - switch (trx->bts->band) { - case GSM_BAND_850: - case GSM_BAND_900: - trx->nominal_power = 20; - break; - case GSM_BAND_1800: - case GSM_BAND_1900: - trx->nominal_power = 23; - break; - default: - LOGP(DNM, LOGL_ERROR, "Unsupported nanoBTS GSM band %s\n", - gsm_band_name(trx->bts->band)); - break; - } - break; - default: - break; - } - - for (i = 0; i < TRX_NR_TS; i++) - nm_reconfig_ts(&trx->ts[i]); -} - -static void nm_reconfig_bts(struct gsm_bts *bts) -{ - struct gsm_bts_trx *trx; - - switch (bts->type) { - case GSM_BTS_TYPE_BS11: - patch_nm_tables(bts); - abis_nm_raw_msg(bts, sizeof(msg_1), msg_1); /* set BTS SiteMgr attr*/ - abis_nm_set_bts_attr(bts, bs11_attr_bts, sizeof(bs11_attr_bts)); - abis_nm_raw_msg(bts, sizeof(msg_3), msg_3); /* set BTS handover attr */ - abis_nm_raw_msg(bts, sizeof(msg_4), msg_4); /* set BTS power control attr */ - break; - default: - break; - } - - llist_for_each_entry(trx, &bts->trx_list, list) - nm_reconfig_trx(trx); -} - - -static void bootstrap_om_bs11(struct gsm_bts *bts) -{ - LOGP(DNM, LOGL_NOTICE, "bootstrapping OML for BTS %u\n", bts->nr); - - /* stop sending event reports */ - abis_nm_event_reports(bts, 0); - - /* begin DB transmission */ - abis_nm_bs11_db_transmission(bts, 1); - - /* end DB transmission */ - abis_nm_bs11_db_transmission(bts, 0); - - /* Reset BTS Site manager resource */ - abis_nm_bs11_reset_resource(bts); - - /* begin DB transmission */ - abis_nm_bs11_db_transmission(bts, 1); - - /* reconfigure BTS with all TRX and all TS */ - nm_reconfig_bts(bts); - - /* end DB transmission */ - abis_nm_bs11_db_transmission(bts, 0); - - /* Reset BTS Site manager resource */ - abis_nm_bs11_reset_resource(bts); - - /* restart sending event reports */ - abis_nm_event_reports(bts, 1); -} - -static int shutdown_om(struct gsm_bts *bts) -{ - /* stop sending event reports */ - abis_nm_event_reports(bts, 0); - - /* begin DB transmission */ - abis_nm_bs11_db_transmission(bts, 1); - - /* end DB transmission */ - abis_nm_bs11_db_transmission(bts, 0); - - /* Reset BTS Site manager resource */ - abis_nm_bs11_reset_resource(bts); - - return 0; -} - -/* Callback function to be called every time we receive a signal from INPUT */ -static int gbl_sig_cb(unsigned int subsys, unsigned int signal, - void *handler_data, void *signal_data) -{ - struct gsm_bts *bts; - - if (subsys != SS_L_GLOBAL) - return 0; - - switch (signal) { - case S_GLOBAL_BTS_CLOSE_OM: - bts = signal_data; - if (bts->type == GSM_BTS_TYPE_BS11) - shutdown_om(signal_data); - break; - } - - return 0; -} - -/* Callback function to be called every time we receive a signal from INPUT */ -static int inp_sig_cb(unsigned int subsys, unsigned int signal, - void *handler_data, void *signal_data) -{ - struct input_signal_data *isd = signal_data; - - if (subsys != SS_L_INPUT) - return 0; - - switch (signal) { - case S_L_INP_TEI_UP: - switch (isd->link_type) { - case E1INP_SIGN_OML: - if (isd->trx->bts->type == GSM_BTS_TYPE_BS11) - bootstrap_om_bs11(isd->trx->bts); - break; - } - } - - return 0; -} - -static int bts_model_bs11_start(struct gsm_network *net) -{ - model_bs11.features.data = &model_bs11._features_data[0]; - model_bs11.features.data_len = sizeof(model_bs11._features_data); - - gsm_btsmodel_set_feature(&model_bs11, BTS_FEAT_HOPPING); - gsm_btsmodel_set_feature(&model_bs11, BTS_FEAT_HSCSD); - gsm_btsmodel_set_feature(&model_bs11, BTS_FEAT_MULTI_TSC); - - osmo_signal_register_handler(SS_L_INPUT, inp_sig_cb, NULL); - osmo_signal_register_handler(SS_L_GLOBAL, gbl_sig_cb, NULL); - - return 0; -} - -int bts_model_bs11_init(void) -{ - return gsm_bts_model_register(&model_bs11); -} diff --git a/openbsc/src/libbsc/bts_sysmobts.c b/openbsc/src/libbsc/bts_sysmobts.c deleted file mode 100644 index e4b6cdc7..00000000 --- a/openbsc/src/libbsc/bts_sysmobts.c +++ /dev/null @@ -1,60 +0,0 @@ -/* sysmocom sysmoBTS specific code */ - -/* (C) 2010-2012 by Harald Welte - * - * All Rights Reserved - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation; either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - * - */ - -#include - -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -extern struct gsm_bts_model bts_model_nanobts; - -static struct gsm_bts_model model_sysmobts; - -int bts_model_sysmobts_init(void) -{ - model_sysmobts = bts_model_nanobts; - model_sysmobts.name = "sysmobts"; - model_sysmobts.type = GSM_BTS_TYPE_OSMOBTS; - - model_sysmobts.features.data = &model_sysmobts._features_data[0]; - model_sysmobts.features.data_len = - sizeof(model_sysmobts._features_data); - memset(model_sysmobts.features.data, 0, sizeof(model_sysmobts.features.data_len)); - - gsm_btsmodel_set_feature(&model_sysmobts, BTS_FEAT_GPRS); - gsm_btsmodel_set_feature(&model_sysmobts, BTS_FEAT_EGPRS); - - return gsm_bts_model_register(&model_sysmobts); -} diff --git a/openbsc/src/libbsc/bts_unknown.c b/openbsc/src/libbsc/bts_unknown.c deleted file mode 100644 index f1135294..00000000 --- a/openbsc/src/libbsc/bts_unknown.c +++ /dev/null @@ -1,40 +0,0 @@ -/* Generic BTS - VTY code tries to allocate this BTS before type is known */ - -/* (C) 2010 by Daniel Willmann - * - * All Rights Reserved - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation; either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - * - */ - - -#include -#include -#include - -static struct gsm_bts_model model_unknown = { - .type = GSM_BTS_TYPE_UNKNOWN, - .name = "unknown", - .oml_rcvmsg = &abis_nm_rcvmsg, - .nm_att_tlvdef = { - .def = { - }, - }, -}; - -int bts_model_unknown_init(void) -{ - return gsm_bts_model_register(&model_unknown); -} diff --git a/openbsc/src/libbsc/chan_alloc.c b/openbsc/src/libbsc/chan_alloc.c deleted file mode 100644 index 33b79a0b..00000000 --- a/openbsc/src/libbsc/chan_alloc.c +++ /dev/null @@ -1,543 +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 - -#include - -static int ts_is_usable(struct gsm_bts_trx_ts *ts) -{ - /* FIXME: How does this behave for BS-11 ? */ - if (is_ipaccess_bts(ts->trx->bts)) { - if (!nm_is_running(&ts->mo.nm_state)) - return 0; - } - - /* If a TCH/F_PDCH TS is busy changing, it is already taken or not - * yet available. */ - if (ts->pchan == GSM_PCHAN_TCH_F_PDCH) { - if (ts->flags & TS_F_PDCH_PENDING_MASK) - return 0; - } - - /* If a dynamic channel is busy changing, it is already taken or not - * yet available. */ - if (ts->pchan == GSM_PCHAN_TCH_F_TCH_H_PDCH) { - if (ts->dyn.pchan_is != ts->dyn.pchan_want) - return 0; - } - - return 1; -} - -int trx_is_usable(struct gsm_bts_trx *trx) -{ - /* FIXME: How does this behave for BS-11 ? */ - if (is_ipaccess_bts(trx->bts)) { - if (!nm_is_running(&trx->mo.nm_state) || - !nm_is_running(&trx->bb_transc.mo.nm_state)) - return 0; - } - - return 1; -} - -static struct gsm_lchan * -_lc_find_trx(struct gsm_bts_trx *trx, enum gsm_phys_chan_config pchan, - enum gsm_phys_chan_config dyn_as_pchan) -{ - struct gsm_bts_trx_ts *ts; - int j, start, stop, dir, ss; - int check_subslots; - - if (!trx_is_usable(trx)) - return NULL; - - if (trx->bts->chan_alloc_reverse) { - /* check TS 7..0 */ - start = 7; - stop = -1; - dir = -1; - } else { - /* check TS 0..7 */ - start = 0; - stop = 8; - dir = 1; - } - - for (j = start; j != stop; j += dir) { - ts = &trx->ts[j]; - if (!ts_is_usable(ts)) - continue; - if (ts->pchan != pchan) - continue; - - /* - * Allocation for fully dynamic timeslots - * (does not apply for ip.access style GSM_PCHAN_TCH_F_PDCH) - * - * Note the special nature of a dynamic timeslot in PDCH mode: - * in PDCH mode, typically, lchan->type is GSM_LCHAN_NONE and - * lchan->state is LCHAN_S_NONE -- an otherwise unused slot - * becomes PDCH implicitly. In the same sense, this channel - * allocator will never be asked to find an available PDCH - * slot; only TCH/F or TCH/H will be requested, and PDCH mode - * means that it is available for switchover. - * - * A dynamic timeslot in PDCH mode may be switched to TCH/F or - * TCH/H. If a dyn TS is already in TCH/F or TCH/H mode, it - * means that it is in use and its mode can't be switched. - * - * The logic concerning channels for TCH/F is trivial: there is - * only one channel, so a dynamic TS in TCH/F mode is already - * taken and not available for allocation. For TCH/H, we need - * to check whether a dynamic timeslot is already in TCH/H mode - * and whether one of the two channels is still available. - */ - switch (pchan) { - case GSM_PCHAN_TCH_F_TCH_H_PDCH: - if (ts->dyn.pchan_is != ts->dyn.pchan_want) { - /* The TS's mode is being switched. Not - * available anymore/yet. */ - DEBUGP(DRLL, "%s already in switchover\n", - gsm_ts_and_pchan_name(ts)); - continue; - } - if (ts->dyn.pchan_is == GSM_PCHAN_PDCH) { - /* This slot is available. Still check for - * error states to be sure; in all cases the - * first lchan will be used. */ - if (ts->lchan->state != LCHAN_S_NONE - && ts->lchan->state != LCHAN_S_ACTIVE) - continue; - return ts->lchan; - } - if (ts->dyn.pchan_is != dyn_as_pchan) - /* not applicable. */ - continue; - /* The requested type matches the dynamic timeslot's - * current mode. A channel may still be available - * (think TCH/H). */ - check_subslots = ts_subslots(ts); - break; - - case GSM_PCHAN_TCH_F_PDCH: - /* Available for voice when in PDCH mode */ - if (ts_pchan(ts) != GSM_PCHAN_PDCH) - continue; - /* Subslots of a PDCH ts don't need to be checked. */ - return ts->lchan; - - default: - /* Not a dynamic channel, there is only one pchan kind: */ - check_subslots = ts_subslots(ts); - break; - } - - /* Is a sub-slot still available? */ - for (ss = 0; ss < check_subslots; ss++) { - struct gsm_lchan *lc = &ts->lchan[ss]; - if (lc->type == GSM_LCHAN_NONE && - lc->state == LCHAN_S_NONE) - return lc; - } - } - - return NULL; -} - -static struct gsm_lchan * -_lc_dyn_find_bts(struct gsm_bts *bts, enum gsm_phys_chan_config pchan, - enum gsm_phys_chan_config dyn_as_pchan) -{ - struct gsm_bts_trx *trx; - struct gsm_lchan *lc; - - if (bts->chan_alloc_reverse) { - llist_for_each_entry_reverse(trx, &bts->trx_list, list) { - lc = _lc_find_trx(trx, pchan, dyn_as_pchan); - if (lc) - return lc; - } - } else { - llist_for_each_entry(trx, &bts->trx_list, list) { - lc = _lc_find_trx(trx, pchan, dyn_as_pchan); - if (lc) - return lc; - } - } - - return NULL; -} - -static struct gsm_lchan * -_lc_find_bts(struct gsm_bts *bts, enum gsm_phys_chan_config pchan) -{ - return _lc_dyn_find_bts(bts, pchan, GSM_PCHAN_NONE); -} - -/* Allocate a logical channel. - * - * Dynamic channel types: we always prefer a dedicated TS, and only pick + - * switch a dynamic TS if no pure TS of the requested PCHAN is available. - * - * TCH_F/PDCH: if we pick a PDCH ACT style dynamic TS as TCH/F channel, PDCH - * will be disabled in rsl_chan_activate_lchan(); there is no need to check - * whether PDCH mode is currently active, here. - */ -struct gsm_lchan *lchan_alloc(struct gsm_bts *bts, enum gsm_chan_t type, - int allow_bigger) -{ - struct gsm_lchan *lchan = NULL; - enum gsm_phys_chan_config first, first_cbch, second, second_cbch; - - switch (type) { - case GSM_LCHAN_SDCCH: - if (bts->chan_alloc_reverse) { - first = GSM_PCHAN_SDCCH8_SACCH8C; - first_cbch = GSM_PCHAN_SDCCH8_SACCH8C_CBCH; - second = GSM_PCHAN_CCCH_SDCCH4; - second_cbch = GSM_PCHAN_CCCH_SDCCH4_CBCH; - } else { - first = GSM_PCHAN_CCCH_SDCCH4; - first_cbch = GSM_PCHAN_CCCH_SDCCH4_CBCH; - second = GSM_PCHAN_SDCCH8_SACCH8C; - second_cbch = GSM_PCHAN_SDCCH8_SACCH8C_CBCH; - } - - lchan = _lc_find_bts(bts, first); - if (lchan == NULL) - lchan = _lc_find_bts(bts, first_cbch); - if (lchan == NULL) - lchan = _lc_find_bts(bts, second); - if (lchan == NULL) - lchan = _lc_find_bts(bts, second_cbch); - - /* allow to assign bigger channels */ - if (allow_bigger) { - if (lchan == NULL) { - lchan = _lc_find_bts(bts, GSM_PCHAN_TCH_H); - if (lchan) - type = GSM_LCHAN_TCH_H; - } - - if (lchan == NULL) { - lchan = _lc_find_bts(bts, GSM_PCHAN_TCH_F); - if (lchan) - type = GSM_LCHAN_TCH_F; - } - - /* try dynamic TCH/F_PDCH */ - if (lchan == NULL) { - lchan = _lc_find_bts(bts, GSM_PCHAN_TCH_F_PDCH); - /* TCH/F_PDCH will be used as TCH/F */ - if (lchan) - type = GSM_LCHAN_TCH_F; - } - - /* try fully dynamic TCH/F_TCH/H_PDCH */ - if (lchan == NULL) { - lchan = _lc_dyn_find_bts(bts, GSM_PCHAN_TCH_F_TCH_H_PDCH, - GSM_PCHAN_TCH_H); - if (lchan) - type = GSM_LCHAN_TCH_H; - } - /* - * No need to check fully dynamic channels for TCH/F: - * if no TCH/H was available, neither will be TCH/F. - */ - } - break; - case GSM_LCHAN_TCH_F: - lchan = _lc_find_bts(bts, GSM_PCHAN_TCH_F); - /* If we don't have TCH/F available, fall-back to TCH/H */ - if (!lchan) { - lchan = _lc_find_bts(bts, GSM_PCHAN_TCH_H); - if (lchan) - type = GSM_LCHAN_TCH_H; - } - /* If we don't have TCH/H either, try dynamic TCH/F_PDCH */ - if (!lchan) { - lchan = _lc_find_bts(bts, GSM_PCHAN_TCH_F_PDCH); - /* TCH/F_PDCH used as TCH/F -- here, type is already - * set to GSM_LCHAN_TCH_F, but for clarity's sake... */ - if (lchan) - type = GSM_LCHAN_TCH_F; - } - - /* Try fully dynamic TCH/F_TCH/H_PDCH as TCH/F... */ - if (!lchan && bts->network->dyn_ts_allow_tch_f) { - lchan = _lc_dyn_find_bts(bts, - GSM_PCHAN_TCH_F_TCH_H_PDCH, - GSM_PCHAN_TCH_F); - if (lchan) - type = GSM_LCHAN_TCH_F; - } - /* ...and as TCH/H. */ - if (!lchan) { - lchan = _lc_dyn_find_bts(bts, - GSM_PCHAN_TCH_F_TCH_H_PDCH, - GSM_PCHAN_TCH_H); - if (lchan) - type = GSM_LCHAN_TCH_H; - } - break; - case GSM_LCHAN_TCH_H: - lchan = _lc_find_bts(bts, GSM_PCHAN_TCH_H); - /* If we don't have TCH/H available, fall-back to TCH/F */ - if (!lchan) { - lchan = _lc_find_bts(bts, GSM_PCHAN_TCH_F); - if (lchan) - type = GSM_LCHAN_TCH_F; - } - /* No dedicated TCH/x available -- try fully dynamic - * TCH/F_TCH/H_PDCH */ - if (!lchan) { - lchan = _lc_dyn_find_bts(bts, - GSM_PCHAN_TCH_F_TCH_H_PDCH, - GSM_PCHAN_TCH_H); - if (lchan) - type = GSM_LCHAN_TCH_H; - } - /* - * No need to check TCH/F_TCH/H_PDCH channels for TCH/F: - * if no TCH/H was available, neither will be TCH/F. - */ - /* If we don't have TCH/F either, try dynamic TCH/F_PDCH */ - if (!lchan) { - lchan = _lc_find_bts(bts, GSM_PCHAN_TCH_F_PDCH); - if (lchan) - type = GSM_LCHAN_TCH_F; - } - break; - default: - LOGP(DRLL, LOGL_ERROR, "Unknown gsm_chan_t %u\n", type); - } - - if (lchan) { - lchan->type = type; - - LOGP(DRLL, LOGL_INFO, "%s Allocating lchan=%u as %s\n", - gsm_ts_and_pchan_name(lchan->ts), - lchan->nr, gsm_lchant_name(lchan->type)); - - /* clear sapis */ - memset(lchan->sapis, 0, ARRAY_SIZE(lchan->sapis)); - - /* clear multi rate config */ - memset(&lchan->mr_ms_lv, 0, sizeof(lchan->mr_ms_lv)); - memset(&lchan->mr_bts_lv, 0, sizeof(lchan->mr_bts_lv)); - lchan->broken_reason = ""; - } else { - struct challoc_signal_data sig; - - LOGP(DRLL, LOGL_ERROR, "Failed to allocate %s channel\n", - gsm_lchant_name(type)); - - sig.bts = bts; - sig.type = type; - osmo_signal_dispatch(SS_CHALLOC, S_CHALLOC_ALLOC_FAIL, &sig); - } - - return lchan; -} - -/* Free a logical channel */ -void lchan_free(struct gsm_lchan *lchan) -{ - struct challoc_signal_data sig; - int i; - - sig.type = lchan->type; - lchan->type = GSM_LCHAN_NONE; - - - if (lchan->conn) { - struct lchan_signal_data sig; - - /* We might kill an active channel... */ - sig.lchan = lchan; - sig.mr = NULL; - osmo_signal_dispatch(SS_LCHAN, S_LCHAN_UNEXPECTED_RELEASE, &sig); - } - - if (lchan->abis_ip.rtp_socket) { - LOGP(DRLL, LOGL_ERROR, "%s RTP Proxy Socket remained open.\n", - gsm_lchan_name(lchan)); - rtp_socket_free(lchan->abis_ip.rtp_socket); - lchan->abis_ip.rtp_socket = NULL; - } - - /* stop the timer */ - osmo_timer_del(&lchan->T3101); - - /* clear cached measuement reports */ - lchan->meas_rep_idx = 0; - for (i = 0; i < ARRAY_SIZE(lchan->meas_rep); i++) { - lchan->meas_rep[i].flags = 0; - lchan->meas_rep[i].nr = 0; - } - for (i = 0; i < ARRAY_SIZE(lchan->neigh_meas); i++) - lchan->neigh_meas[i].arfcn = 0; - - if (lchan->rqd_ref) { - talloc_free(lchan->rqd_ref); - lchan->rqd_ref = NULL; - lchan->rqd_ta = 0; - } - - sig.lchan = lchan; - sig.bts = lchan->ts->trx->bts; - osmo_signal_dispatch(SS_CHALLOC, S_CHALLOC_FREED, &sig); - - if (lchan->conn) { - LOGP(DRLL, LOGL_ERROR, "the subscriber connection should be gone.\n"); - lchan->conn = NULL; - } - - /* FIXME: ts_free() the timeslot, if we're the last logical - * channel using it */ -} - -/* - * There was an error with the TRX and we need to forget - * any state so that a lchan can be allocated again after - * the trx is fully usable. - * - * This should be called after lchan_free to force a channel - * be available for allocation again. This means that this - * method will stop the "delay after error"-timer and set the - * state to LCHAN_S_NONE. - */ -void lchan_reset(struct gsm_lchan *lchan) -{ - osmo_timer_del(&lchan->T3101); - osmo_timer_del(&lchan->T3109); - osmo_timer_del(&lchan->T3111); - osmo_timer_del(&lchan->error_timer); - - lchan->type = GSM_LCHAN_NONE; - lchan->state = LCHAN_S_NONE; - - if (lchan->abis_ip.rtp_socket) { - rtp_socket_free(lchan->abis_ip.rtp_socket); - lchan->abis_ip.rtp_socket = NULL; - } -} - -/* Drive the release process of the lchan */ -static void _lchan_handle_release(struct gsm_lchan *lchan, - int sacch_deact, int mode) -{ - /* Release all SAPIs on the local end and continue */ - rsl_release_sapis_from(lchan, 1, RSL_REL_LOCAL_END); - - /* - * Shall we send a RR Release, start T3109 and wait for the - * release indication from the BTS or just take it down (e.g. - * on assignment requests) - */ - if (sacch_deact) { - gsm48_send_rr_release(lchan); - - /* Deactivate the SACCH on the BTS side */ - rsl_deact_sacch(lchan); - rsl_start_t3109(lchan); - } else if (lchan->sapis[0] == LCHAN_SAPI_UNUSED) { - rsl_direct_rf_release(lchan); - } else { - rsl_release_request(lchan, 0, mode); - } -} - -/* Consider releasing the channel now */ -int lchan_release(struct gsm_lchan *lchan, int sacch_deact, enum rsl_rel_mode mode) -{ - DEBUGP(DRLL, "%s starting release sequence\n", gsm_lchan_name(lchan)); - rsl_lchan_set_state(lchan, LCHAN_S_REL_REQ); - - lchan->conn = NULL; - _lchan_handle_release(lchan, sacch_deact, mode); - return 1; -} - -void bts_chan_load(struct pchan_load *cl, const struct gsm_bts *bts) -{ - struct gsm_bts_trx *trx; - - llist_for_each_entry(trx, &bts->trx_list, list) { - int i; - - /* skip administratively deactivated tranxsceivers */ - if (!nm_is_running(&trx->mo.nm_state) || - !nm_is_running(&trx->bb_transc.mo.nm_state)) - continue; - - for (i = 0; i < ARRAY_SIZE(trx->ts); i++) { - struct gsm_bts_trx_ts *ts = &trx->ts[i]; - struct load_counter *pl = &cl->pchan[ts->pchan]; - int j; - int subslots; - - /* skip administratively deactivated timeslots */ - if (!nm_is_running(&ts->mo.nm_state)) - continue; - - subslots = ts_subslots(ts); - for (j = 0; j < subslots; j++) { - struct gsm_lchan *lchan = &ts->lchan[j]; - - pl->total++; - - switch (lchan->state) { - case LCHAN_S_NONE: - break; - default: - pl->used++; - break; - } - } - } - } -} - -void network_chan_load(struct pchan_load *pl, struct gsm_network *net) -{ - struct gsm_bts *bts; - - memset(pl, 0, sizeof(*pl)); - - llist_for_each_entry(bts, &net->bts_list, list) - bts_chan_load(pl, bts); -} - diff --git a/openbsc/src/libbsc/e1_config.c b/openbsc/src/libbsc/e1_config.c deleted file mode 100644 index d57dec57..00000000 --- a/openbsc/src/libbsc/e1_config.c +++ /dev/null @@ -1,297 +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 -#include - -#define SAPI_L2ML 0 -#define SAPI_OML 62 -#define SAPI_RSL 0 /* 63 ? */ - -/* The e1_reconfig_*() functions below take the configuration present in the - * bts/trx/ts data structures and ensure the E1 configuration reflects the - * timeslot/subslot/TEI configuration */ - -int e1_reconfig_ts(struct gsm_bts_trx_ts *ts) -{ - struct gsm_e1_subslot *e1_link = &ts->e1_link; - struct e1inp_line *line; - struct e1inp_ts *e1_ts; - - DEBUGP(DLMI, "e1_reconfig_ts(%u,%u,%u)\n", ts->trx->bts->nr, ts->trx->nr, ts->nr); - - if (!e1_link->e1_ts) { - LOGP(DLINP, LOGL_ERROR, "TS (%u/%u/%u) without E1 timeslot?\n", - ts->nr, ts->trx->nr, ts->trx->bts->nr); - return 0; - } - - line = e1inp_line_find(e1_link->e1_nr); - if (!line) { - LOGP(DLINP, LOGL_ERROR, "TS (%u/%u/%u) referring to " - "non-existing E1 line %u\n", ts->nr, ts->trx->nr, - ts->trx->bts->nr, e1_link->e1_nr); - return -ENOMEM; - } - - if (ts_is_tch(ts)) { - e1_ts = &line->ts[e1_link->e1_ts-1]; - e1inp_ts_config_trau(e1_ts, line, subch_cb); - subch_demux_activate(&e1_ts->trau.demux, e1_link->e1_ts_ss); - } - - return 0; -} - -int e1_reconfig_trx(struct gsm_bts_trx *trx) -{ - struct gsm_e1_subslot *e1_link = &trx->rsl_e1_link; - struct e1inp_ts *sign_ts; - struct e1inp_line *line; - struct e1inp_sign_link *rsl_link; - int i; - - if (!e1_link->e1_ts) { - LOGP(DLINP, LOGL_ERROR, "TRX (%u/%u) RSL link without " - "timeslot?\n", trx->bts->nr, trx->nr); - return -EINVAL; - } - - /* RSL Link */ - line = e1inp_line_find(e1_link->e1_nr); - if (!line) { - LOGP(DLINP, LOGL_ERROR, "TRX (%u/%u) RSL link referring " - "to non-existing E1 line %u\n", trx->bts->nr, - trx->nr, e1_link->e1_nr); - return -ENOMEM; - } - sign_ts = &line->ts[e1_link->e1_ts-1]; - e1inp_ts_config_sign(sign_ts, line); - /* Ericsson RBS have a per-TRX OML link in parallel to RSL */ - if (trx->bts->type == GSM_BTS_TYPE_RBS2000) { - struct e1inp_sign_link *oml_link; - oml_link = e1inp_sign_link_create(sign_ts, E1INP_SIGN_OML, trx, - trx->rsl_tei, SAPI_OML); - if (!oml_link) { - LOGP(DLINP, LOGL_ERROR, "TRX (%u/%u) OML link creation " - "failed\n", trx->bts->nr, trx->nr); - return -ENOMEM; - } - if (trx->oml_link) - e1inp_sign_link_destroy(trx->oml_link); - trx->oml_link = oml_link; - } - rsl_link = e1inp_sign_link_create(sign_ts, E1INP_SIGN_RSL, - trx, trx->rsl_tei, SAPI_RSL); - if (!rsl_link) { - LOGP(DLINP, LOGL_ERROR, "TRX (%u/%u) RSL link creation " - "failed\n", trx->bts->nr, trx->nr); - return -ENOMEM; - } - if (trx->rsl_link) - e1inp_sign_link_destroy(trx->rsl_link); - trx->rsl_link = rsl_link; - - for (i = 0; i < TRX_NR_TS; i++) - e1_reconfig_ts(&trx->ts[i]); - - return 0; -} - -/* this is the generic callback for all ISDN-based BTS. */ -static int bts_isdn_sign_link(struct msgb *msg) -{ - int ret = -EINVAL; - struct e1inp_sign_link *link = msg->dst; - struct gsm_bts *bts; - - switch (link->type) { - case E1INP_SIGN_OML: - bts = link->trx->bts; - ret = bts->model->oml_rcvmsg(msg); - break; - case E1INP_SIGN_RSL: - ret = abis_rsl_rcvmsg(msg); - break; - default: - LOGP(DLMI, LOGL_ERROR, "unknown link type %u\n", link->type); - break; - } - return ret; -} - -struct e1inp_line_ops bts_isdn_e1inp_line_ops = { - .sign_link = bts_isdn_sign_link, -}; - -int e1_reconfig_bts(struct gsm_bts *bts) -{ - struct gsm_e1_subslot *e1_link = &bts->oml_e1_link; - struct e1inp_ts *sign_ts; - struct e1inp_line *line; - struct e1inp_sign_link *oml_link; - struct gsm_bts_trx *trx; - - DEBUGP(DLMI, "e1_reconfig_bts(%u)\n", bts->nr); - - line = e1inp_line_find(e1_link->e1_nr); - if (!line) { - LOGP(DLINP, LOGL_ERROR, "BTS %u OML link referring to " - "non-existing E1 line %u\n", bts->nr, e1_link->e1_nr); - return -ENOMEM; - } - - if (!bts->model->e1line_bind_ops) { - LOGP(DLINP, LOGL_ERROR, "no callback to bind E1 line operations\n"); - return -EINVAL; - } - if (!line->ops) - bts->model->e1line_bind_ops(line); - - /* skip signal link initialization, this is done later for these BTS. */ - if (bts->type == GSM_BTS_TYPE_NANOBTS || - bts->type == GSM_BTS_TYPE_OSMOBTS) - return e1inp_line_update(line); - - /* OML link */ - if (!e1_link->e1_ts) { - LOGP(DLINP, LOGL_ERROR, "BTS %u OML link without timeslot?\n", - bts->nr); - return -EINVAL; - } - - sign_ts = &line->ts[e1_link->e1_ts-1]; - e1inp_ts_config_sign(sign_ts, line); - oml_link = e1inp_sign_link_create(sign_ts, E1INP_SIGN_OML, - bts->c0, bts->oml_tei, SAPI_OML); - if (!oml_link) { - LOGP(DLINP, LOGL_ERROR, "BTS %u OML link creation failed\n", - bts->nr); - return -ENOMEM; - } - if (bts->oml_link) - e1inp_sign_link_destroy(bts->oml_link); - bts->oml_link = oml_link; - - llist_for_each_entry(trx, &bts->trx_list, list) - e1_reconfig_trx(trx); - - /* notify E1 input something has changed */ - return e1inp_line_update(line); -} - -#if 0 -/* do some compiled-in configuration for our BTS/E1 setup */ -int e1_config(struct gsm_bts *bts, int cardnr, int release_l2) -{ - struct e1inp_line *line; - struct e1inp_ts *sign_ts; - struct e1inp_sign_link *oml_link, *rsl_link; - struct gsm_bts_trx *trx = bts->c0; - int base_ts; - - switch (bts->nr) { - case 0: - /* First BTS uses E1 TS 01,02,03,04,05 */ - base_ts = HARDCODED_BTS0_TS - 1; - break; - case 1: - /* Second BTS uses E1 TS 06,07,08,09,10 */ - base_ts = HARDCODED_BTS1_TS - 1; - break; - case 2: - /* Third BTS uses E1 TS 11,12,13,14,15 */ - base_ts = HARDCODED_BTS2_TS - 1; - default: - return -EINVAL; - } - - line = talloc_zero(tall_bsc_ctx, struct e1inp_line); - if (!line) - return -ENOMEM; - - /* create E1 timeslots for signalling and TRAU frames */ - e1inp_ts_config(&line->ts[base_ts+1-1], line, E1INP_TS_TYPE_SIGN); - e1inp_ts_config(&line->ts[base_ts+2-1], line, E1INP_TS_TYPE_TRAU); - e1inp_ts_config(&line->ts[base_ts+3-1], line, E1INP_TS_TYPE_TRAU); - - /* create signalling links for TS1 */ - sign_ts = &line->ts[base_ts+1-1]; - oml_link = e1inp_sign_link_create(sign_ts, E1INP_SIGN_OML, - trx, TEI_OML, SAPI_OML); - rsl_link = e1inp_sign_link_create(sign_ts, E1INP_SIGN_RSL, - trx, TEI_RSL, SAPI_RSL); - - /* create back-links from bts/trx */ - bts->oml_link = oml_link; - trx->rsl_link = rsl_link; - - /* enable subchannel demuxer on TS2 */ - subch_demux_activate(&line->ts[base_ts+2-1].trau.demux, 1); - subch_demux_activate(&line->ts[base_ts+2-1].trau.demux, 2); - subch_demux_activate(&line->ts[base_ts+2-1].trau.demux, 3); - - /* enable subchannel demuxer on TS3 */ - subch_demux_activate(&line->ts[base_ts+3-1].trau.demux, 0); - subch_demux_activate(&line->ts[base_ts+3-1].trau.demux, 1); - subch_demux_activate(&line->ts[base_ts+3-1].trau.demux, 2); - subch_demux_activate(&line->ts[base_ts+3-1].trau.demux, 3); - - trx = gsm_bts_trx_num(bts, 1); - if (trx) { - /* create E1 timeslots for TRAU frames of TRX1 */ - e1inp_ts_config(&line->ts[base_ts+4-1], line, E1INP_TS_TYPE_TRAU); - e1inp_ts_config(&line->ts[base_ts+5-1], line, E1INP_TS_TYPE_TRAU); - - /* create RSL signalling link for TRX1 */ - sign_ts = &line->ts[base_ts+1-1]; - rsl_link = e1inp_sign_link_create(sign_ts, E1INP_SIGN_RSL, - trx, TEI_RSL+1, SAPI_RSL); - /* create back-links from trx */ - trx->rsl_link = rsl_link; - - /* enable subchannel demuxer on TS2 */ - subch_demux_activate(&line->ts[base_ts+4-1].trau.demux, 0); - subch_demux_activate(&line->ts[base_ts+4-1].trau.demux, 1); - subch_demux_activate(&line->ts[base_ts+4-1].trau.demux, 2); - subch_demux_activate(&line->ts[base_ts+4-1].trau.demux, 3); - - /* enable subchannel demuxer on TS3 */ - subch_demux_activate(&line->ts[base_ts+5-1].trau.demux, 0); - subch_demux_activate(&line->ts[base_ts+5-1].trau.demux, 1); - subch_demux_activate(&line->ts[base_ts+5-1].trau.demux, 2); - subch_demux_activate(&line->ts[base_ts+5-1].trau.demux, 3); - } - - return mi_setup(cardnr, line, release_l2); -} -#endif diff --git a/openbsc/src/libbsc/gsm_04_08_utils.c b/openbsc/src/libbsc/gsm_04_08_utils.c deleted file mode 100644 index 3447d27c..00000000 --- a/openbsc/src/libbsc/gsm_04_08_utils.c +++ /dev/null @@ -1,687 +0,0 @@ -/* GSM Mobile Radio Interface Layer 3 messages on the A-bis interface - * 3GPP TS 04.08 version 7.21.0 Release 1998 / ETSI TS 100 940 V7.21.0 - * utility functions - */ - -/* (C) 2008-2009 by Harald Welte - * (C) 2008, 2009 by Holger Hans Peter Freyther - * - * All Rights Reserved - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation; either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - * - */ -#include -#include -#include -#include - -#include -#include - -#include -#include -#include -#include -#include -#include -#include - -/* should ip.access BTS use direct RTP streams between each other (1), - * or should OpenBSC always act as RTP relay/proxy in between (0) ? */ -int ipacc_rtp_direct = 1; - -static int gsm48_sendmsg(struct msgb *msg) -{ - if (msg->lchan) - msg->dst = msg->lchan->ts->trx->rsl_link; - - msg->l3h = msg->data; - return rsl_data_request(msg, 0); -} - -/* Section 9.1.8 / Table 9.9 */ -struct chreq { - uint8_t val; - uint8_t mask; - enum chreq_type type; -}; - -/* If SYSTEM INFORMATION TYPE 4 NECI bit == 1 */ -static const struct chreq chreq_type_neci1[] = { - { 0xa0, 0xe0, CHREQ_T_EMERG_CALL }, - { 0xc0, 0xe0, CHREQ_T_CALL_REEST_TCH_F }, - { 0x68, 0xfc, CHREQ_T_CALL_REEST_TCH_H }, - { 0x6c, 0xfc, CHREQ_T_CALL_REEST_TCH_H_DBL }, - { 0xe0, 0xe0, CHREQ_T_TCH_F }, - { 0x40, 0xf0, CHREQ_T_VOICE_CALL_TCH_H }, - { 0x50, 0xf0, CHREQ_T_DATA_CALL_TCH_H }, - { 0x00, 0xf0, CHREQ_T_LOCATION_UPD }, - { 0x10, 0xf0, CHREQ_T_SDCCH }, - { 0x80, 0xe0, CHREQ_T_PAG_R_ANY_NECI1 }, - { 0x20, 0xf0, CHREQ_T_PAG_R_TCH_F }, - { 0x30, 0xf0, CHREQ_T_PAG_R_TCH_FH }, - { 0x67, 0xff, CHREQ_T_LMU }, - { 0x60, 0xf9, CHREQ_T_RESERVED_SDCCH }, - { 0x61, 0xfb, CHREQ_T_RESERVED_SDCCH }, - { 0x63, 0xff, CHREQ_T_RESERVED_SDCCH }, - { 0x70, 0xf8, CHREQ_T_PDCH_TWO_PHASE }, - { 0x78, 0xfc, CHREQ_T_PDCH_ONE_PHASE }, - { 0x78, 0xfa, CHREQ_T_PDCH_ONE_PHASE }, - { 0x78, 0xf9, CHREQ_T_PDCH_ONE_PHASE }, - { 0x7f, 0xff, CHREQ_T_RESERVED_IGNORE }, -}; - -/* If SYSTEM INFORMATION TYPE 4 NECI bit == 0 */ -static const struct chreq chreq_type_neci0[] = { - { 0xa0, 0xe0, CHREQ_T_EMERG_CALL }, - { 0xc0, 0xe0, CHREQ_T_CALL_REEST_TCH_H }, - { 0xe0, 0xe0, CHREQ_T_TCH_F }, - { 0x50, 0xf0, CHREQ_T_DATA_CALL_TCH_H }, - { 0x00, 0xe0, CHREQ_T_LOCATION_UPD }, - { 0x80, 0xe0, CHREQ_T_PAG_R_ANY_NECI0 }, - { 0x20, 0xf0, CHREQ_T_PAG_R_TCH_F }, - { 0x30, 0xf0, CHREQ_T_PAG_R_TCH_FH }, - { 0x67, 0xff, CHREQ_T_LMU }, - { 0x60, 0xf9, CHREQ_T_RESERVED_SDCCH }, - { 0x61, 0xfb, CHREQ_T_RESERVED_SDCCH }, - { 0x63, 0xff, CHREQ_T_RESERVED_SDCCH }, - { 0x70, 0xf8, CHREQ_T_PDCH_TWO_PHASE }, - { 0x78, 0xfc, CHREQ_T_PDCH_ONE_PHASE }, - { 0x78, 0xfa, CHREQ_T_PDCH_ONE_PHASE }, - { 0x78, 0xf9, CHREQ_T_PDCH_ONE_PHASE }, - { 0x7f, 0xff, CHREQ_T_RESERVED_IGNORE }, -}; - -static const enum gsm_chan_t ctype_by_chreq[] = { - [CHREQ_T_EMERG_CALL] = GSM_LCHAN_TCH_F, - [CHREQ_T_CALL_REEST_TCH_F] = GSM_LCHAN_TCH_F, - [CHREQ_T_CALL_REEST_TCH_H] = GSM_LCHAN_TCH_H, - [CHREQ_T_CALL_REEST_TCH_H_DBL] = GSM_LCHAN_TCH_H, - [CHREQ_T_SDCCH] = GSM_LCHAN_SDCCH, - [CHREQ_T_TCH_F] = GSM_LCHAN_TCH_F, - [CHREQ_T_VOICE_CALL_TCH_H] = GSM_LCHAN_TCH_H, - [CHREQ_T_DATA_CALL_TCH_H] = GSM_LCHAN_TCH_H, - [CHREQ_T_LOCATION_UPD] = GSM_LCHAN_SDCCH, - [CHREQ_T_PAG_R_ANY_NECI1] = GSM_LCHAN_SDCCH, - [CHREQ_T_PAG_R_ANY_NECI0] = GSM_LCHAN_SDCCH, - [CHREQ_T_PAG_R_TCH_F] = GSM_LCHAN_TCH_F, - [CHREQ_T_PAG_R_TCH_FH] = GSM_LCHAN_TCH_F, - [CHREQ_T_LMU] = GSM_LCHAN_SDCCH, - [CHREQ_T_RESERVED_SDCCH] = GSM_LCHAN_SDCCH, - [CHREQ_T_PDCH_ONE_PHASE] = GSM_LCHAN_PDTCH, - [CHREQ_T_PDCH_TWO_PHASE] = GSM_LCHAN_PDTCH, - [CHREQ_T_RESERVED_IGNORE] = GSM_LCHAN_UNKNOWN, -}; - -static const enum gsm_chreq_reason_t reason_by_chreq[] = { - [CHREQ_T_EMERG_CALL] = GSM_CHREQ_REASON_EMERG, - [CHREQ_T_CALL_REEST_TCH_F] = GSM_CHREQ_REASON_CALL, - [CHREQ_T_CALL_REEST_TCH_H] = GSM_CHREQ_REASON_CALL, - [CHREQ_T_CALL_REEST_TCH_H_DBL] = GSM_CHREQ_REASON_CALL, - [CHREQ_T_SDCCH] = GSM_CHREQ_REASON_OTHER, - [CHREQ_T_TCH_F] = GSM_CHREQ_REASON_OTHER, - [CHREQ_T_VOICE_CALL_TCH_H] = GSM_CHREQ_REASON_CALL, - [CHREQ_T_DATA_CALL_TCH_H] = GSM_CHREQ_REASON_OTHER, - [CHREQ_T_LOCATION_UPD] = GSM_CHREQ_REASON_LOCATION_UPD, - [CHREQ_T_PAG_R_ANY_NECI1] = GSM_CHREQ_REASON_PAG, - [CHREQ_T_PAG_R_ANY_NECI0] = GSM_CHREQ_REASON_PAG, - [CHREQ_T_PAG_R_TCH_F] = GSM_CHREQ_REASON_PAG, - [CHREQ_T_PAG_R_TCH_FH] = GSM_CHREQ_REASON_PAG, - [CHREQ_T_LMU] = GSM_CHREQ_REASON_OTHER, - [CHREQ_T_PDCH_ONE_PHASE] = GSM_CHREQ_REASON_PDCH, - [CHREQ_T_PDCH_TWO_PHASE] = GSM_CHREQ_REASON_PDCH, - [CHREQ_T_RESERVED_SDCCH] = GSM_CHREQ_REASON_OTHER, - [CHREQ_T_RESERVED_IGNORE] = GSM_CHREQ_REASON_OTHER, -}; - -/* verify that the two tables match */ -osmo_static_assert(sizeof(ctype_by_chreq) == - sizeof(((struct gsm_network *) NULL)->ctype_by_chreq), assert_size); - -/* - * Update channel types for request based on policy. E.g. in the - * case of a TCH/H network/bsc use TCH/H for the emergency calls, - * for early assignment assign a SDCCH and some other options. - */ -void gsm_net_update_ctype(struct gsm_network *network) -{ - /* copy over the data */ - memcpy(network->ctype_by_chreq, ctype_by_chreq, sizeof(ctype_by_chreq)); - - /* - * Use TCH/H for emergency calls when this cell allows TCH/H. Maybe it - * is better to iterate over the BTS/TRX and check if no TCH/F is available - * and then set it to TCH/H. - */ - if (network->neci) - network->ctype_by_chreq[CHREQ_T_EMERG_CALL] = GSM_LCHAN_TCH_H; - - if (network->pag_any_tch) { - if (network->neci) { - network->ctype_by_chreq[CHREQ_T_PAG_R_ANY_NECI0] = GSM_LCHAN_TCH_H; - network->ctype_by_chreq[CHREQ_T_PAG_R_ANY_NECI1] = GSM_LCHAN_TCH_H; - } else { - network->ctype_by_chreq[CHREQ_T_PAG_R_ANY_NECI0] = GSM_LCHAN_TCH_F; - network->ctype_by_chreq[CHREQ_T_PAG_R_ANY_NECI1] = GSM_LCHAN_TCH_F; - } - } -} - -enum gsm_chan_t get_ctype_by_chreq(struct gsm_network *network, uint8_t ra) -{ - int i; - int length; - const struct chreq *chreq; - - if (network->neci) { - chreq = chreq_type_neci1; - length = ARRAY_SIZE(chreq_type_neci1); - } else { - chreq = chreq_type_neci0; - length = ARRAY_SIZE(chreq_type_neci0); - } - - - for (i = 0; i < length; i++) { - const struct chreq *chr = &chreq[i]; - if ((ra & chr->mask) == chr->val) - return network->ctype_by_chreq[chr->type]; - } - LOGP(DRR, LOGL_ERROR, "Unknown CHANNEL REQUEST RQD 0x%02x\n", ra); - return GSM_LCHAN_SDCCH; -} - -int get_reason_by_chreq(uint8_t ra, int neci) -{ - int i; - int length; - const struct chreq *chreq; - - if (neci) { - chreq = chreq_type_neci1; - length = ARRAY_SIZE(chreq_type_neci1); - } else { - chreq = chreq_type_neci0; - length = ARRAY_SIZE(chreq_type_neci0); - } - - for (i = 0; i < length; i++) { - const struct chreq *chr = &chreq[i]; - if ((ra & chr->mask) == chr->val) - return reason_by_chreq[chr->type]; - } - LOGP(DRR, LOGL_ERROR, "Unknown CHANNEL REQUEST REASON 0x%02x\n", ra); - return GSM_CHREQ_REASON_OTHER; -} - -static void mr_config_for_ms(struct gsm_lchan *lchan, struct msgb *msg) -{ - if (lchan->tch_mode == GSM48_CMODE_SPEECH_AMR) - msgb_tlv_put(msg, GSM48_IE_MUL_RATE_CFG, lchan->mr_ms_lv[0], - lchan->mr_ms_lv + 1); -} - -/* 7.1.7 and 9.1.7: RR CHANnel RELease */ -int gsm48_send_rr_release(struct gsm_lchan *lchan) -{ - struct msgb *msg = gsm48_msgb_alloc_name("GSM 04.08 RR REL"); - struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh)); - uint8_t *cause; - - msg->lchan = lchan; - gh->proto_discr = GSM48_PDISC_RR; - gh->msg_type = GSM48_MT_RR_CHAN_REL; - - cause = msgb_put(msg, 1); - cause[0] = GSM48_RR_CAUSE_NORMAL; - - DEBUGP(DRR, "Sending Channel Release: Chan: Number: %d Type: %d\n", - lchan->nr, lchan->type); - - /* Send actual release request to MS */ - return gsm48_sendmsg(msg); -} - -int send_siemens_mrpci(struct gsm_lchan *lchan, - uint8_t *classmark2_lv) -{ - struct rsl_mrpci mrpci; - - if (classmark2_lv[0] < 2) - return -EINVAL; - - mrpci.power_class = classmark2_lv[1] & 0x7; - mrpci.vgcs_capable = classmark2_lv[2] & (1 << 1); - mrpci.vbs_capable = classmark2_lv[2] & (1 <<2); - mrpci.gsm_phase = (classmark2_lv[1]) >> 5 & 0x3; - - return rsl_siemens_mrpci(lchan, &mrpci); -} - -int gsm48_extract_mi(uint8_t *classmark2_lv, int length, char *mi_string, uint8_t *mi_type) -{ - /* Check the size for the classmark */ - if (length < 1 + *classmark2_lv) - return -1; - - uint8_t *mi_lv = classmark2_lv + *classmark2_lv + 1; - if (length < 2 + *classmark2_lv + mi_lv[0]) - return -2; - - *mi_type = mi_lv[1] & GSM_MI_TYPE_MASK; - return gsm48_mi_to_string(mi_string, GSM48_MI_SIZE, mi_lv+1, *mi_lv); -} - -int gsm48_paging_extract_mi(struct gsm48_pag_resp *resp, int length, - char *mi_string, uint8_t *mi_type) -{ - static const uint32_t classmark_offset = - offsetof(struct gsm48_pag_resp, classmark2); - uint8_t *classmark2_lv = (uint8_t *) &resp->classmark2; - return gsm48_extract_mi(classmark2_lv, length - classmark_offset, - mi_string, mi_type); -} - -int gsm48_handle_paging_resp(struct gsm_subscriber_connection *conn, - struct msgb *msg, struct bsc_subscr *bsub) -{ - struct gsm_bts *bts = msg->lchan->ts->trx->bts; - struct gsm48_hdr *gh = msgb_l3(msg); - uint8_t *classmark2_lv = gh->data + 1; - - if (is_siemens_bts(bts)) - send_siemens_mrpci(msg->lchan, classmark2_lv); - - if (!conn->bsub) { - conn->bsub = bsub; - } else if (conn->bsub != bsub) { - LOGP(DRR, LOGL_ERROR, - "<- Channel already owned by someone else?\n"); - bsc_subscr_put(bsub); - return -EINVAL; - } else { - DEBUGP(DRR, "<- Channel already owned by us\n"); - bsc_subscr_put(bsub); - bsub = conn->bsub; - } - - rate_ctr_inc(&bts->network->bsc_ctrs->ctr[BSC_CTR_PAGING_COMPLETED]); - - /* Stop paging on the bts we received the paging response */ - paging_request_stop(&bts->network->bts_list, conn->bts, bsub, conn, - msg); - return 0; -} - -/* Chapter 9.1.9: Ciphering Mode Command */ -int gsm48_send_rr_ciph_mode(struct gsm_lchan *lchan, int want_imeisv) -{ - struct msgb *msg = gsm48_msgb_alloc_name("GSM 04.08 CIPH"); - struct gsm48_hdr *gh; - uint8_t ciph_mod_set; - - msg->lchan = lchan; - - DEBUGP(DRR, "TX CIPHERING MODE CMD\n"); - - if (lchan->encr.alg_id <= RSL_ENC_ALG_A5(0)) - ciph_mod_set = 0; - else - ciph_mod_set = (lchan->encr.alg_id-2)<<1 | 1; - - gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh) + 1); - gh->proto_discr = GSM48_PDISC_RR; - gh->msg_type = GSM48_MT_RR_CIPH_M_CMD; - gh->data[0] = (want_imeisv & 0x1) << 4 | (ciph_mod_set & 0xf); - - return rsl_encryption_cmd(msg); -} - -static void gsm48_cell_desc(struct gsm48_cell_desc *cd, - const struct gsm_bts *bts) -{ - cd->ncc = (bts->bsic >> 3 & 0x7); - cd->bcc = (bts->bsic & 0x7); - cd->arfcn_hi = bts->c0->arfcn >> 8; - cd->arfcn_lo = bts->c0->arfcn & 0xff; -} - -void gsm48_lchan2chan_desc(struct gsm48_chan_desc *cd, - const struct gsm_lchan *lchan) -{ - uint16_t arfcn = lchan->ts->trx->arfcn & 0x3ff; - - cd->chan_nr = gsm_lchan2chan_nr(lchan); - if (!lchan->ts->hopping.enabled) { - cd->h0.tsc = gsm_ts_tsc(lchan->ts); - cd->h0.h = 0; - cd->h0.arfcn_high = arfcn >> 8; - cd->h0.arfcn_low = arfcn & 0xff; - } else { - cd->h1.tsc = gsm_ts_tsc(lchan->ts); - cd->h1.h = 1; - cd->h1.maio_high = lchan->ts->hopping.maio >> 2; - cd->h1.maio_low = lchan->ts->hopping.maio & 0x03; - cd->h1.hsn = lchan->ts->hopping.hsn; - } -} - -/*! \brief Encode a TS 04.08 multirate config LV according to 10.5.2.21aa - * \param[out] lv caller-allocated buffer of 7 bytes. First octet is IS length - * \param[in] mr multi-rate configuration to encode - * \param[in] modes array describing the AMR modes - * \returns 0 on success */ -int gsm48_multirate_config(uint8_t *lv, const struct amr_multirate_conf *mr, const struct amr_mode *modes) -{ - int num = 0, i; - - for (i = 0; i < 8; i++) { - if (((mr->gsm48_ie[1] >> i) & 1)) - num++; - } - if (num > 4) { - LOGP(DRR, LOGL_ERROR, "BUG: Using multirate codec with too " - "many modes in config.\n"); - num = 4; - } - if (num < 1) { - LOGP(DRR, LOGL_ERROR, "BUG: Using multirate codec with no " - "mode in config.\n"); - num = 1; - } - - lv[0] = (num == 1) ? 2 : (num + 2); - memcpy(lv + 1, mr->gsm48_ie, 2); - if (num == 1) - return 0; - - lv[3] = modes[0].threshold & 0x3f; - lv[4] = modes[0].hysteresis << 4; - if (num == 2) - return 0; - lv[4] |= (modes[1].threshold & 0x3f) >> 2; - lv[5] = modes[1].threshold << 6; - lv[5] |= (modes[1].hysteresis & 0x0f) << 2; - if (num == 3) - return 0; - lv[5] |= (modes[2].threshold & 0x3f) >> 4; - lv[6] = modes[2].threshold << 4; - lv[6] |= modes[2].hysteresis & 0x0f; - - return 0; -} - -#define GSM48_HOCMD_CCHDESC_LEN 16 - -/* Chapter 9.1.15: Handover Command */ -int gsm48_send_ho_cmd(struct gsm_lchan *old_lchan, struct gsm_lchan *new_lchan, - uint8_t power_command, uint8_t ho_ref) -{ - struct msgb *msg = gsm48_msgb_alloc_name("GSM 04.08 HO CMD"); - struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh)); - struct gsm48_ho_cmd *ho = - (struct gsm48_ho_cmd *) msgb_put(msg, sizeof(*ho)); - - msg->lchan = old_lchan; - gh->proto_discr = GSM48_PDISC_RR; - gh->msg_type = GSM48_MT_RR_HANDO_CMD; - - /* mandatory bits */ - gsm48_cell_desc(&ho->cell_desc, new_lchan->ts->trx->bts); - gsm48_lchan2chan_desc(&ho->chan_desc, new_lchan); - ho->ho_ref = ho_ref; - ho->power_command = power_command; - - if (new_lchan->ts->hopping.enabled) { - struct gsm_bts *bts = new_lchan->ts->trx->bts; - struct gsm48_system_information_type_1 *si1; - uint8_t *cur; - - si1 = GSM_BTS_SI(bts, SYSINFO_TYPE_1); - /* Copy the Cell Chan Desc (ARFCNS in this cell) */ - msgb_put_u8(msg, GSM48_IE_CELL_CH_DESC); - cur = msgb_put(msg, GSM48_HOCMD_CCHDESC_LEN); - memcpy(cur, si1->cell_channel_description, - GSM48_HOCMD_CCHDESC_LEN); - /* Copy the Mobile Allocation */ - msgb_tlv_put(msg, GSM48_IE_MA_BEFORE, - new_lchan->ts->hopping.ma_len, - new_lchan->ts->hopping.ma_data); - } - /* FIXME: optional bits for type of synchronization? */ - - return gsm48_sendmsg(msg); -} - -/* Chapter 9.1.2: Assignment Command */ -int gsm48_send_rr_ass_cmd(struct gsm_lchan *dest_lchan, struct gsm_lchan *lchan, uint8_t power_command) -{ - struct msgb *msg = gsm48_msgb_alloc_name("GSM 04.08 ASS CMD"); - struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh)); - struct gsm48_ass_cmd *ass = - (struct gsm48_ass_cmd *) msgb_put(msg, sizeof(*ass)); - - DEBUGP(DRR, "-> ASSIGNMENT COMMAND tch_mode=0x%02x\n", lchan->tch_mode); - - msg->lchan = dest_lchan; - gh->proto_discr = GSM48_PDISC_RR; - gh->msg_type = GSM48_MT_RR_ASS_CMD; - - /* - * fill the channel information element, this code - * should probably be shared with rsl_rx_chan_rqd(), - * gsm48_lchan_modify(). But beware that 10.5.2.5 - * 10.5.2.5.a have slightly different semantic for - * the chan_desc. But as long as multi-slot configurations - * are not used we seem to be fine. - */ - gsm48_lchan2chan_desc(&ass->chan_desc, lchan); - ass->power_command = power_command; - - /* optional: cell channel description */ - - msgb_tv_put(msg, GSM48_IE_CHANMODE_1, lchan->tch_mode); - - /* mobile allocation in case of hopping */ - if (lchan->ts->hopping.enabled) { - msgb_tlv_put(msg, GSM48_IE_MA_BEFORE, lchan->ts->hopping.ma_len, - lchan->ts->hopping.ma_data); - } - - /* in case of multi rate we need to attach a config */ - mr_config_for_ms(lchan, msg); - - return gsm48_sendmsg(msg); -} - -/* 9.1.5 Channel mode modify: Modify the mode on the MS side */ -int gsm48_lchan_modify(struct gsm_lchan *lchan, uint8_t mode) -{ - struct msgb *msg = gsm48_msgb_alloc_name("GSM 04.08 CHN MOD"); - struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh)); - struct gsm48_chan_mode_modify *cmm = - (struct gsm48_chan_mode_modify *) msgb_put(msg, sizeof(*cmm)); - - DEBUGP(DRR, "-> CHANNEL MODE MODIFY mode=0x%02x\n", mode); - - lchan->tch_mode = mode; - msg->lchan = lchan; - gh->proto_discr = GSM48_PDISC_RR; - gh->msg_type = GSM48_MT_RR_CHAN_MODE_MODIF; - - /* fill the channel information element, this code - * should probably be shared with rsl_rx_chan_rqd() */ - gsm48_lchan2chan_desc(&cmm->chan_desc, lchan); - cmm->mode = mode; - - /* in case of multi rate we need to attach a config */ - mr_config_for_ms(lchan, msg); - - return gsm48_sendmsg(msg); -} - -int gsm48_rx_rr_modif_ack(struct msgb *msg) -{ - int rc; - struct gsm48_hdr *gh = msgb_l3(msg); - struct gsm48_chan_mode_modify *mod = - (struct gsm48_chan_mode_modify *) gh->data; - - DEBUGP(DRR, "CHANNEL MODE MODIFY ACK\n"); - - if (mod->mode != msg->lchan->tch_mode) { - LOGP(DRR, LOGL_ERROR, "CHANNEL MODE change failed. Wanted: %d Got: %d\n", - msg->lchan->tch_mode, mod->mode); - return -1; - } - - /* update the channel type */ - switch (mod->mode) { - case GSM48_CMODE_SIGN: - msg->lchan->rsl_cmode = RSL_CMOD_SPD_SIGN; - break; - case GSM48_CMODE_SPEECH_V1: - case GSM48_CMODE_SPEECH_EFR: - case GSM48_CMODE_SPEECH_AMR: - msg->lchan->rsl_cmode = RSL_CMOD_SPD_SPEECH; - break; - case GSM48_CMODE_DATA_14k5: - case GSM48_CMODE_DATA_12k0: - case GSM48_CMODE_DATA_6k0: - case GSM48_CMODE_DATA_3k6: - msg->lchan->rsl_cmode = RSL_CMOD_SPD_DATA; - break; - } - - /* We've successfully modified the MS side of the channel, - * now go on to modify the BTS side of the channel */ - rc = rsl_chan_mode_modify_req(msg->lchan); - - /* FIXME: we not only need to do this after mode modify, but - * also after channel activation */ - if (is_ipaccess_bts(msg->lchan->ts->trx->bts) && mod->mode != GSM48_CMODE_SIGN) - rsl_ipacc_crcx(msg->lchan); - return rc; -} - -int gsm48_parse_meas_rep(struct gsm_meas_rep *rep, struct msgb *msg) -{ - struct gsm48_hdr *gh = msgb_l3(msg); - uint8_t *data = gh->data; - struct gsm_bts *bts = msg->lchan->ts->trx->bts; - struct bitvec *nbv = &bts->si_common.neigh_list; - struct gsm_meas_rep_cell *mrc; - - if (gh->msg_type != GSM48_MT_RR_MEAS_REP) - return -EINVAL; - - if (data[0] & 0x80) - rep->flags |= MEAS_REP_F_BA1; - if (data[0] & 0x40) - rep->flags |= MEAS_REP_F_UL_DTX; - if ((data[1] & 0x40) == 0x00) - rep->flags |= MEAS_REP_F_DL_VALID; - - rep->dl.full.rx_lev = data[0] & 0x3f; - rep->dl.sub.rx_lev = data[1] & 0x3f; - rep->dl.full.rx_qual = (data[2] >> 4) & 0x7; - rep->dl.sub.rx_qual = (data[2] >> 1) & 0x7; - - rep->num_cell = ((data[3] >> 6) & 0x3) | ((data[2] & 0x01) << 2); - if (rep->num_cell < 1 || rep->num_cell > 6) - return 0; - - /* an encoding nightmare in perfection */ - mrc = &rep->cell[0]; - mrc->rxlev = data[3] & 0x3f; - mrc->neigh_idx = data[4] >> 3; - mrc->arfcn = bitvec_get_nth_set_bit(nbv, mrc->neigh_idx + 1); - mrc->bsic = ((data[4] & 0x07) << 3) | (data[5] >> 5); - if (rep->num_cell < 2) - return 0; - - mrc = &rep->cell[1]; - mrc->rxlev = ((data[5] & 0x1f) << 1) | (data[6] >> 7); - mrc->neigh_idx = (data[6] >> 2) & 0x1f; - mrc->arfcn = bitvec_get_nth_set_bit(nbv, mrc->neigh_idx + 1); - mrc->bsic = ((data[6] & 0x03) << 4) | (data[7] >> 4); - if (rep->num_cell < 3) - return 0; - - mrc = &rep->cell[2]; - mrc->rxlev = ((data[7] & 0x0f) << 2) | (data[8] >> 6); - mrc->neigh_idx = (data[8] >> 1) & 0x1f; - mrc->arfcn = bitvec_get_nth_set_bit(nbv, mrc->neigh_idx + 1); - mrc->bsic = ((data[8] & 0x01) << 5) | (data[9] >> 3); - if (rep->num_cell < 4) - return 0; - - mrc = &rep->cell[3]; - mrc->rxlev = ((data[9] & 0x07) << 3) | (data[10] >> 5); - mrc->neigh_idx = data[10] & 0x1f; - mrc->arfcn = bitvec_get_nth_set_bit(nbv, mrc->neigh_idx + 1); - mrc->bsic = data[11] >> 2; - if (rep->num_cell < 5) - return 0; - - mrc = &rep->cell[4]; - mrc->rxlev = ((data[11] & 0x03) << 4) | (data[12] >> 4); - mrc->neigh_idx = ((data[12] & 0xf) << 1) | (data[13] >> 7); - mrc->arfcn = bitvec_get_nth_set_bit(nbv, mrc->neigh_idx + 1); - mrc->bsic = (data[13] >> 1) & 0x3f; - if (rep->num_cell < 6) - return 0; - - mrc = &rep->cell[5]; - mrc->rxlev = ((data[13] & 0x01) << 5) | (data[14] >> 3); - mrc->neigh_idx = ((data[14] & 0x07) << 2) | (data[15] >> 6); - mrc->arfcn = bitvec_get_nth_set_bit(nbv, mrc->neigh_idx + 1); - mrc->bsic = data[15] & 0x3f; - - return 0; -} - -/* 9.2.5 CM service accept */ -int gsm48_tx_mm_serv_ack(struct gsm_subscriber_connection *conn) -{ - struct msgb *msg = gsm48_msgb_alloc_name("GSM 04.08 SERV ACK"); - struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh)); - - msg->lchan = conn->lchan; - - gh->proto_discr = GSM48_PDISC_MM; - gh->msg_type = GSM48_MT_MM_CM_SERV_ACC; - - DEBUGP(DMM, "-> CM SERVICE ACK\n"); - - return gsm0808_submit_dtap(conn, msg, 0, 0); -} - -/* 9.2.6 CM service reject */ -int gsm48_tx_mm_serv_rej(struct gsm_subscriber_connection *conn, - enum gsm48_reject_value value) -{ - struct msgb *msg; - - msg = gsm48_create_mm_serv_rej(value); - if (!msg) { - LOGP(DMM, LOGL_ERROR, "Failed to allocate CM Service Reject.\n"); - return -1; - } - - DEBUGP(DMM, "-> CM SERVICE Reject cause: %d\n", value); - - return gsm0808_submit_dtap(conn, msg, 0, 0); -} diff --git a/openbsc/src/libbsc/gsm_04_80_utils.c b/openbsc/src/libbsc/gsm_04_80_utils.c deleted file mode 100644 index e0db81ed..00000000 --- a/openbsc/src/libbsc/gsm_04_80_utils.c +++ /dev/null @@ -1,40 +0,0 @@ -/* OpenBSC utility functions for 3GPP TS 04.80 */ - -/* (C) 2016 by sysmocom s.m.f.c. GmbH - * - * All Rights Reserved - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation; either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public 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 - -int bsc_send_ussd_notify(struct gsm_subscriber_connection *conn, int level, - const char *text) -{ - struct msgb *msg = gsm0480_create_ussd_notify(level, text); - if (!msg) - return -1; - return gsm0808_submit_dtap(conn, msg, 0, 0); -} - -int bsc_send_ussd_release_complete(struct gsm_subscriber_connection *conn) -{ - struct msgb *msg = gsm0480_create_ussd_release_complete(); - if (!msg) - return -1; - return gsm0808_submit_dtap(conn, msg, 0, 0); -} diff --git a/openbsc/src/libbsc/handover_decision.c b/openbsc/src/libbsc/handover_decision.c deleted file mode 100644 index 0f07bcac..00000000 --- a/openbsc/src/libbsc/handover_decision.c +++ /dev/null @@ -1,304 +0,0 @@ -/* Handover Decision making for Inter-BTS (Intra-BSC) Handover. This - * only implements the handover algorithm/decision, but not execution - * of it */ - -/* (C) 2009 by Harald Welte - * - * All Rights Reserved - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation; either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - * - */ - -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include - -/* issue handover to a cell identified by ARFCN and BSIC */ -static int handover_to_arfcn_bsic(struct gsm_lchan *lchan, - uint16_t arfcn, uint8_t bsic) -{ - struct gsm_bts *new_bts; - - /* resolve the gsm_bts structure for the best neighbor */ - new_bts = gsm_bts_neighbor(lchan->ts->trx->bts, arfcn, bsic); - if (!new_bts) { - LOGP(DHO, LOGL_NOTICE, "unable to determine neighbor BTS " - "for ARFCN %u BSIC %u ?!?\n", arfcn, bsic); - return -EINVAL; - } - - /* and actually try to handover to that cell */ - return bsc_handover_start(lchan, new_bts); -} - -/* did we get a RXLEV for a given cell in the given report? */ -static int rxlev_for_cell_in_rep(struct gsm_meas_rep *mr, - uint16_t arfcn, uint8_t bsic) -{ - int i; - - for (i = 0; i < mr->num_cell; i++) { - struct gsm_meas_rep_cell *mrc = &mr->cell[i]; - - /* search for matching report */ - if (!(mrc->arfcn == arfcn && mrc->bsic == bsic)) - continue; - - mrc->flags |= MRC_F_PROCESSED; - return mrc->rxlev; - } - return -ENODEV; -} - -/* obtain averaged rxlev for given neighbor */ -static int neigh_meas_avg(struct neigh_meas_proc *nmp, int window) -{ - unsigned int i, idx; - int avg = 0; - - idx = calc_initial_idx(ARRAY_SIZE(nmp->rxlev), - nmp->rxlev_cnt % ARRAY_SIZE(nmp->rxlev), - window); - - for (i = 0; i < window; i++) { - int j = (idx+i) % ARRAY_SIZE(nmp->rxlev); - - avg += nmp->rxlev[j]; - } - - return avg / window; -} - -/* find empty or evict bad neighbor */ -static struct neigh_meas_proc *find_evict_neigh(struct gsm_lchan *lchan) -{ - int j, worst = 999999; - struct neigh_meas_proc *nmp_worst = NULL; - - /* first try to find an empty/unused slot */ - for (j = 0; j < ARRAY_SIZE(lchan->neigh_meas); j++) { - struct neigh_meas_proc *nmp = &lchan->neigh_meas[j]; - if (!nmp->arfcn) - return nmp; - } - - /* no empty slot found. evict worst neighbor from list */ - for (j = 0; j < ARRAY_SIZE(lchan->neigh_meas); j++) { - struct neigh_meas_proc *nmp = &lchan->neigh_meas[j]; - int avg = neigh_meas_avg(nmp, MAX_WIN_NEIGH_AVG); - if (!nmp_worst || avg < worst) { - worst = avg; - nmp_worst = nmp; - } - } - - return nmp_worst; -} - -/* process neighbor cell measurement reports */ -static void process_meas_neigh(struct gsm_meas_rep *mr) -{ - int i, j, idx; - - /* for each reported cell, try to update global state */ - for (j = 0; j < ARRAY_SIZE(mr->lchan->neigh_meas); j++) { - struct neigh_meas_proc *nmp = &mr->lchan->neigh_meas[j]; - unsigned int idx; - int rxlev; - - /* skip unused entries */ - if (!nmp->arfcn) - continue; - - rxlev = rxlev_for_cell_in_rep(mr, nmp->arfcn, nmp->bsic); - idx = nmp->rxlev_cnt % ARRAY_SIZE(nmp->rxlev); - if (rxlev >= 0) { - nmp->rxlev[idx] = rxlev; - nmp->last_seen_nr = mr->nr; - } else - nmp->rxlev[idx] = 0; - nmp->rxlev_cnt++; - } - - /* iterate over list of reported cells, check if we did not - * process all of them */ - for (i = 0; i < mr->num_cell; i++) { - struct gsm_meas_rep_cell *mrc = &mr->cell[i]; - struct neigh_meas_proc *nmp; - - if (mrc->flags & MRC_F_PROCESSED) - continue; - - nmp = find_evict_neigh(mr->lchan); - - nmp->arfcn = mrc->arfcn; - nmp->bsic = mrc->bsic; - - idx = nmp->rxlev_cnt % ARRAY_SIZE(nmp->rxlev); - nmp->rxlev[idx] = mrc->rxlev; - nmp->rxlev_cnt++; - nmp->last_seen_nr = mr->nr; - - mrc->flags |= MRC_F_PROCESSED; - } -} - -/* attempt to do a handover */ -static int attempt_handover(struct gsm_meas_rep *mr) -{ - struct gsm_network *net = mr->lchan->ts->trx->bts->network; - struct neigh_meas_proc *best_cell = NULL; - unsigned int best_better_db = 0; - int i, rc; - - /* find the best cell in this report that is at least RXLEV_HYST - * better than the current serving cell */ - - for (i = 0; i < ARRAY_SIZE(mr->lchan->neigh_meas); i++) { - struct neigh_meas_proc *nmp = &mr->lchan->neigh_meas[i]; - int avg, better; - - /* skip empty slots */ - if (nmp->arfcn == 0) - continue; - - /* caculate average rxlev for this cell over the window */ - avg = neigh_meas_avg(nmp, net->handover.win_rxlev_avg_neigh); - - /* check if hysteresis is fulfilled */ - if (avg < mr->dl.full.rx_lev + net->handover.pwr_hysteresis) - continue; - - better = avg - mr->dl.full.rx_lev; - if (better > best_better_db) { - best_cell = nmp; - best_better_db = better; - } - } - - if (!best_cell) - return 0; - - LOGP(DHO, LOGL_INFO, "%s: Cell on ARFCN %u is better: ", - gsm_ts_name(mr->lchan->ts), best_cell->arfcn); - if (!net->handover.active) { - LOGPC(DHO, LOGL_INFO, "Skipping, Handover disabled\n"); - return 0; - } - - rc = handover_to_arfcn_bsic(mr->lchan, best_cell->arfcn, best_cell->bsic); - switch (rc) { - case 0: - LOGPC(DHO, LOGL_INFO, "Starting handover\n"); - break; - case -ENOSPC: - LOGPC(DHO, LOGL_INFO, "No channel available\n"); - break; - case -EBUSY: - LOGPC(DHO, LOGL_INFO, "Handover already active\n"); - break; - default: - LOGPC(DHO, LOGL_ERROR, "Unknown error\n"); - } - return rc; -} - -/* process an already parsed measurement report and decide if we want to - * attempt a handover */ -static int process_meas_rep(struct gsm_meas_rep *mr) -{ - struct gsm_network *net = mr->lchan->ts->trx->bts->network; - enum meas_rep_field dlev, dqual; - int av_rxlev; - - /* we currently only do handover for TCH channels */ - switch (mr->lchan->type) { - case GSM_LCHAN_TCH_F: - case GSM_LCHAN_TCH_H: - break; - default: - return 0; - } - - if (mr->flags & MEAS_REP_F_DL_DTX) { - dlev = MEAS_REP_DL_RXLEV_SUB; - dqual = MEAS_REP_DL_RXQUAL_SUB; - } else { - dlev = MEAS_REP_DL_RXLEV_FULL; - dqual = MEAS_REP_DL_RXQUAL_FULL; - } - - /* parse actual neighbor cell info */ - if (mr->num_cell > 0 && mr->num_cell < 7) - process_meas_neigh(mr); - - av_rxlev = get_meas_rep_avg(mr->lchan, dlev, - net->handover.win_rxlev_avg); - - /* Interference HO */ - if (rxlev2dbm(av_rxlev) > -85 && - meas_rep_n_out_of_m_be(mr->lchan, dqual, 3, 4, 5)) - return attempt_handover(mr); - - /* Bad Quality */ - if (meas_rep_n_out_of_m_be(mr->lchan, dqual, 3, 4, 5)) - return attempt_handover(mr); - - /* Low Level */ - if (rxlev2dbm(av_rxlev) <= -110) - return attempt_handover(mr); - - /* Distance */ - if (mr->ms_l1.ta > net->handover.max_distance) - return attempt_handover(mr); - - /* Power Budget AKA Better Cell */ - if ((mr->nr % net->handover.pwr_interval) == 0) - return attempt_handover(mr); - - return 0; - -} - -static int ho_dec_sig_cb(unsigned int subsys, unsigned int signal, - void *handler_data, void *signal_data) -{ - struct lchan_signal_data *lchan_data; - - if (subsys != SS_LCHAN) - return 0; - - lchan_data = signal_data; - switch (signal) { - case S_LCHAN_MEAS_REP: - process_meas_rep(lchan_data->mr); - break; - } - - return 0; -} - -void on_dso_load_ho_dec(void) -{ - osmo_signal_register_handler(SS_LCHAN, ho_dec_sig_cb, NULL); -} diff --git a/openbsc/src/libbsc/handover_logic.c b/openbsc/src/libbsc/handover_logic.c deleted file mode 100644 index 4dd913b1..00000000 --- a/openbsc/src/libbsc/handover_logic.c +++ /dev/null @@ -1,378 +0,0 @@ -/* Handover Logic for Inter-BTS (Intra-BSC) Handover. This does not - * actually implement the handover algorithm/decision, but executes a - * handover decision */ - -/* (C) 2009 by Harald Welte - * - * All Rights Reserved - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation; either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - * - */ - -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -struct bsc_handover { - struct llist_head list; - - struct gsm_lchan *old_lchan; - struct gsm_lchan *new_lchan; - - struct osmo_timer_list T3103; - - uint8_t ho_ref; -}; - -static LLIST_HEAD(bsc_handovers); - -static void handover_free(struct bsc_handover *ho) -{ - osmo_timer_del(&ho->T3103); - llist_del(&ho->list); - talloc_free(ho); -} - -static struct bsc_handover *bsc_ho_by_new_lchan(struct gsm_lchan *new_lchan) -{ - struct bsc_handover *ho; - - llist_for_each_entry(ho, &bsc_handovers, list) { - if (ho->new_lchan == new_lchan) - return ho; - } - - return NULL; -} - -static struct bsc_handover *bsc_ho_by_old_lchan(struct gsm_lchan *old_lchan) -{ - struct bsc_handover *ho; - - llist_for_each_entry(ho, &bsc_handovers, list) { - if (ho->old_lchan == old_lchan) - return ho; - } - - return NULL; -} - -/*! \brief Hand over the specified logical channel to the specified new BTS. - * This is the main entry point for the actual handover algorithm, after the - * decision whether to initiate HO to a specific BTS. */ -int bsc_handover_start(struct gsm_lchan *old_lchan, struct gsm_bts *bts) -{ - struct gsm_lchan *new_lchan; - struct bsc_handover *ho; - static uint8_t ho_ref; - int rc; - - /* don't attempt multiple handovers for the same lchan at - * the same time */ - if (bsc_ho_by_old_lchan(old_lchan)) - return -EBUSY; - - DEBUGP(DHO, "(old_lchan on BTS %u, new BTS %u)\n", - old_lchan->ts->trx->bts->nr, bts->nr); - - rate_ctr_inc(&bts->network->bsc_ctrs->ctr[BSC_CTR_HANDOVER_ATTEMPTED]); - - if (!old_lchan->conn) { - LOGP(DHO, LOGL_ERROR, "Old lchan lacks connection data.\n"); - return -ENOSPC; - } - - new_lchan = lchan_alloc(bts, old_lchan->type, 0); - if (!new_lchan) { - LOGP(DHO, LOGL_NOTICE, "No free channel\n"); - rate_ctr_inc(&bts->network->bsc_ctrs->ctr[BSC_CTR_HANDOVER_NO_CHANNEL]); - return -ENOSPC; - } - - ho = talloc_zero(tall_bsc_ctx, struct bsc_handover); - if (!ho) { - LOGP(DHO, LOGL_FATAL, "Out of Memory\n"); - lchan_free(new_lchan); - return -ENOMEM; - } - ho->old_lchan = old_lchan; - ho->new_lchan = new_lchan; - ho->ho_ref = ho_ref++; - - /* copy some parameters from old lchan */ - memcpy(&new_lchan->encr, &old_lchan->encr, sizeof(new_lchan->encr)); - new_lchan->ms_power = old_lchan->ms_power; - new_lchan->bs_power = old_lchan->bs_power; - new_lchan->rsl_cmode = old_lchan->rsl_cmode; - new_lchan->tch_mode = old_lchan->tch_mode; - memcpy(&new_lchan->mr_ms_lv, &old_lchan->mr_ms_lv, ARRAY_SIZE(new_lchan->mr_ms_lv)); - memcpy(&new_lchan->mr_bts_lv, &old_lchan->mr_bts_lv, ARRAY_SIZE(new_lchan->mr_bts_lv)); - - new_lchan->conn = old_lchan->conn; - new_lchan->conn->ho_lchan = new_lchan; - - /* FIXME: do we have a better idea of the timing advance? */ - rc = rsl_chan_activate_lchan(new_lchan, RSL_ACT_INTER_ASYNC, ho->ho_ref); - if (rc < 0) { - LOGP(DHO, LOGL_ERROR, "could not activate channel\n"); - new_lchan->conn->ho_lchan = NULL; - new_lchan->conn = NULL; - talloc_free(ho); - lchan_free(new_lchan); - return rc; - } - - rsl_lchan_set_state(new_lchan, LCHAN_S_ACT_REQ); - llist_add(&ho->list, &bsc_handovers); - /* we continue in the SS_LCHAN handler / ho_chan_activ_ack */ - - return 0; -} - -void bsc_clear_handover(struct gsm_subscriber_connection *conn, int free_lchan) -{ - struct bsc_handover *ho; - - ho = bsc_ho_by_new_lchan(conn->ho_lchan); - - - if (!ho && conn->ho_lchan) - LOGP(DHO, LOGL_ERROR, "BUG: We lost some state.\n"); - - if (!ho) { - LOGP(DHO, LOGL_ERROR, "unable to find HO record\n"); - return; - } - - conn->ho_lchan->conn = NULL; - conn->ho_lchan = NULL; - - if (free_lchan) - lchan_release(ho->new_lchan, 0, RSL_REL_LOCAL_END); - - handover_free(ho); -} - -/* T3103 expired: Handover has failed without HO COMPLETE or HO FAIL */ -static void ho_T3103_cb(void *_ho) -{ - struct bsc_handover *ho = _ho; - struct gsm_network *net = ho->new_lchan->ts->trx->bts->network; - - DEBUGP(DHO, "HO T3103 expired\n"); - rate_ctr_inc(&net->bsc_ctrs->ctr[BSC_CTR_HANDOVER_TIMEOUT]); - - ho->new_lchan->conn->ho_lchan = NULL; - ho->new_lchan->conn = NULL; - lchan_release(ho->new_lchan, 0, RSL_REL_LOCAL_END); - handover_free(ho); -} - -/* RSL has acknowledged activation of the new lchan */ -static int ho_chan_activ_ack(struct gsm_lchan *new_lchan) -{ - struct bsc_handover *ho; - - /* we need to check if this channel activation is related to - * a handover at all (and if, which particular handover) */ - ho = bsc_ho_by_new_lchan(new_lchan); - if (!ho) - return -ENODEV; - - DEBUGP(DHO, "handover activate ack, send HO Command\n"); - - /* we can now send the 04.08 HANDOVER COMMAND to the MS - * using the old lchan */ - - gsm48_send_ho_cmd(ho->old_lchan, new_lchan, 0, ho->ho_ref); - - /* start T3103. We can continue either with T3103 expiration, - * 04.08 HANDOVER COMPLETE or 04.08 HANDOVER FAIL */ - osmo_timer_setup(&ho->T3103, ho_T3103_cb, ho); - osmo_timer_schedule(&ho->T3103, 10, 0); - - /* create a RTP connection */ - if (is_ipaccess_bts(new_lchan->ts->trx->bts)) - rsl_ipacc_crcx(new_lchan); - - return 0; -} - -/* RSL has not acknowledged activation of the new lchan */ -static int ho_chan_activ_nack(struct gsm_lchan *new_lchan) -{ - struct bsc_handover *ho; - - ho = bsc_ho_by_new_lchan(new_lchan); - if (!ho) { - LOGP(DHO, LOGL_INFO, "ACT NACK: unable to find HO record\n"); - return -ENODEV; - } - - new_lchan->conn->ho_lchan = NULL; - new_lchan->conn = NULL; - handover_free(ho); - - /* FIXME: maybe we should try to allocate a new LCHAN here? */ - - return 0; -} - -/* GSM 04.08 HANDOVER COMPLETE has been received on new channel */ -static int ho_gsm48_ho_compl(struct gsm_lchan *new_lchan) -{ - struct gsm_network *net; - struct bsc_handover *ho; - - ho = bsc_ho_by_new_lchan(new_lchan); - if (!ho) { - LOGP(DHO, LOGL_ERROR, "unable to find HO record\n"); - return -ENODEV; - } - - net = new_lchan->ts->trx->bts->network; - LOGP(DHO, LOGL_INFO, "Subscriber %s HO from BTS %u->%u on ARFCN " - "%u->%u\n", subscr_name(ho->old_lchan->conn->subscr), - ho->old_lchan->ts->trx->bts->nr, new_lchan->ts->trx->bts->nr, - ho->old_lchan->ts->trx->arfcn, new_lchan->ts->trx->arfcn); - - rate_ctr_inc(&net->bsc_ctrs->ctr[BSC_CTR_HANDOVER_COMPLETED]); - - osmo_timer_del(&ho->T3103); - - /* switch TRAU muxer for E1 based BTS from one channel to another */ - if (is_e1_bts(new_lchan->conn->bts)) - switch_trau_mux(ho->old_lchan, new_lchan); - - /* Replace the ho lchan with the primary one */ - if (ho->old_lchan != new_lchan->conn->lchan) - LOGP(DHO, LOGL_ERROR, "Primary lchan changed during handover.\n"); - - if (new_lchan != new_lchan->conn->ho_lchan) - LOGP(DHO, LOGL_ERROR, "Handover channel changed during this handover.\n"); - - new_lchan->conn->ho_lchan = NULL; - new_lchan->conn->lchan = new_lchan; - ho->old_lchan->conn = NULL; - - lchan_release(ho->old_lchan, 0, RSL_REL_LOCAL_END); - - handover_free(ho); - return 0; -} - -/* GSM 04.08 HANDOVER FAIL has been received */ -static int ho_gsm48_ho_fail(struct gsm_lchan *old_lchan) -{ - struct gsm_network *net = old_lchan->ts->trx->bts->network; - struct bsc_handover *ho; - struct gsm_lchan *new_lchan; - - ho = bsc_ho_by_old_lchan(old_lchan); - if (!ho) { - LOGP(DHO, LOGL_ERROR, "unable to find HO record\n"); - return -ENODEV; - } - - rate_ctr_inc(&net->bsc_ctrs->ctr[BSC_CTR_HANDOVER_FAILED]); - - new_lchan = ho->new_lchan; - - /* release the channel and forget about it */ - ho->new_lchan->conn->ho_lchan = NULL; - ho->new_lchan->conn = NULL; - handover_free(ho); - - lchan_release(new_lchan, 0, RSL_REL_LOCAL_END); - - - return 0; -} - -/* GSM 08.58 HANDOVER DETECT has been received */ -static int ho_rsl_detect(struct gsm_lchan *new_lchan) -{ - struct bsc_handover *ho; - - ho = bsc_ho_by_new_lchan(new_lchan); - if (!ho) { - LOGP(DHO, LOGL_ERROR, "unable to find HO record\n"); - return -ENODEV; - } - - /* FIXME: do we actually want to do something here ? */ - - return 0; -} - -static int ho_logic_sig_cb(unsigned int subsys, unsigned int signal, - void *handler_data, void *signal_data) -{ - struct lchan_signal_data *lchan_data; - struct gsm_lchan *lchan; - - lchan_data = signal_data; - switch (subsys) { - case SS_LCHAN: - lchan = lchan_data->lchan; - switch (signal) { - case S_LCHAN_ACTIVATE_ACK: - return ho_chan_activ_ack(lchan); - case S_LCHAN_ACTIVATE_NACK: - return ho_chan_activ_nack(lchan); - case S_LCHAN_HANDOVER_DETECT: - return ho_rsl_detect(lchan); - case S_LCHAN_HANDOVER_COMPL: - return ho_gsm48_ho_compl(lchan); - case S_LCHAN_HANDOVER_FAIL: - return ho_gsm48_ho_fail(lchan); - } - break; - default: - break; - } - - return 0; -} - -struct gsm_lchan *bsc_handover_pending(struct gsm_lchan *new_lchan) -{ - struct bsc_handover *ho; - ho = bsc_ho_by_new_lchan(new_lchan); - if (!ho) - return NULL; - return ho->old_lchan; -} - -static __attribute__((constructor)) void on_dso_load_ho_logic(void) -{ - osmo_signal_register_handler(SS_LCHAN, ho_logic_sig_cb, NULL); -} diff --git a/openbsc/src/libbsc/meas_proc.c b/openbsc/src/libbsc/meas_proc.c deleted file mode 100644 index 5b97e74e..00000000 --- a/openbsc/src/libbsc/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) -{ - osmo_signal_register_handler(SS_LCHAN, meas_proc_sig_cb, NULL); -} diff --git a/openbsc/src/libbsc/meas_rep.c b/openbsc/src/libbsc/meas_rep.c deleted file mode 100644 index 808103d2..00000000 --- a/openbsc/src/libbsc/meas_rep.c +++ /dev/null @@ -1,115 +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 - -static int get_field(const struct gsm_meas_rep *rep, - enum meas_rep_field field) -{ - switch (field) { - case MEAS_REP_DL_RXLEV_FULL: - return rep->dl.full.rx_lev; - case MEAS_REP_DL_RXLEV_SUB: - return rep->dl.sub.rx_lev; - case MEAS_REP_DL_RXQUAL_FULL: - return rep->dl.full.rx_qual; - case MEAS_REP_DL_RXQUAL_SUB: - return rep->dl.sub.rx_qual; - case MEAS_REP_UL_RXLEV_FULL: - return rep->ul.full.rx_lev; - case MEAS_REP_UL_RXLEV_SUB: - return rep->ul.sub.rx_lev; - case MEAS_REP_UL_RXQUAL_FULL: - return rep->ul.full.rx_qual; - case MEAS_REP_UL_RXQUAL_SUB: - return rep->ul.sub.rx_qual; - } - - return 0; -} - - -unsigned int calc_initial_idx(unsigned int array_size, - unsigned int meas_rep_idx, - unsigned int num_values) -{ - int offs, idx; - - /* from which element do we need to start if we're interested - * in an average of 'num' elements */ - offs = meas_rep_idx - num_values; - - if (offs < 0) - idx = array_size + offs; - else - idx = offs; - - return idx; -} - -/* obtain an average over the last 'num' fields in the meas reps */ -int get_meas_rep_avg(const struct gsm_lchan *lchan, - enum meas_rep_field field, unsigned int num) -{ - unsigned int i, idx; - int avg = 0; - - if (num < 1) - return 0; - - idx = calc_initial_idx(ARRAY_SIZE(lchan->meas_rep), - lchan->meas_rep_idx, num); - - for (i = 0; i < num; i++) { - int j = (idx+i) % ARRAY_SIZE(lchan->meas_rep); - - avg += get_field(&lchan->meas_rep[j], field); - } - - return avg / num; -} - -/* Check if N out of M last values for FIELD are >= bd */ -int meas_rep_n_out_of_m_be(const struct gsm_lchan *lchan, - enum meas_rep_field field, - unsigned int n, unsigned int m, int be) -{ - unsigned int i, idx; - int count = 0; - - idx = calc_initial_idx(ARRAY_SIZE(lchan->meas_rep), - lchan->meas_rep_idx, m); - - for (i = 0; i < m; i++) { - int j = (idx + i) % ARRAY_SIZE(lchan->meas_rep); - int val = get_field(&lchan->meas_rep[j], field); - - if (val >= be) - count++; - - if (count >= n) - return 1; - } - - return 0; -} diff --git a/openbsc/src/libbsc/net_init.c b/openbsc/src/libbsc/net_init.c deleted file mode 100644 index bc5ed351..00000000 --- a/openbsc/src/libbsc/net_init.c +++ /dev/null @@ -1,69 +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 - -struct gsm_network *bsc_network_init(void *ctx, - uint16_t country_code, - uint16_t network_code, - mncc_recv_cb_t mncc_recv) -{ - struct gsm_network *net; - - net = gsm_network_init(ctx, country_code, network_code, mncc_recv); - - net->bsc_data = talloc_zero(net, struct osmo_bsc_data); - if (!net->bsc_data) { - talloc_free(net); - return NULL; - } - - /* Init back pointer */ - net->bsc_data->auto_off_timeout = -1; - net->bsc_data->network = net; - INIT_LLIST_HEAD(&net->bsc_data->mscs); - - net->num_bts = 0; - net->reject_cause = GSM48_REJECT_ROAMING_NOT_ALLOWED; - net->T3101 = GSM_T3101_DEFAULT; - net->T3105 = GSM_T3105_DEFAULT; - net->T3113 = GSM_T3113_DEFAULT; - net->T3122 = GSM_T3122_DEFAULT; - /* FIXME: initialize all other timers! */ - - /* default set of handover parameters */ - net->handover.win_rxlev_avg = 10; - net->handover.win_rxqual_avg = 1; - net->handover.win_rxlev_avg_neigh = 10; - net->handover.pwr_interval = 6; - net->handover.pwr_hysteresis = 3; - net->handover.max_distance = 9999; - - INIT_LLIST_HEAD(&net->bts_list); - - /* init statistics */ - net->bsc_ctrs = rate_ctr_group_alloc(net, &bsc_ctrg_desc, 0); - - gsm_net_update_ctype(net); - - return net; -} - diff --git a/openbsc/src/libbsc/paging.c b/openbsc/src/libbsc/paging.c deleted file mode 100644 index 78e39c55..00000000 --- a/openbsc/src/libbsc/paging.c +++ /dev/null @@ -1,449 +0,0 @@ -/* Paging helper and manager.... */ -/* (C) 2009,2013 by Holger Hans Peter Freyther - * All Rights Reserved - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation; either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - * - */ - -/* - * Relevant specs: - * 12.21: - * - 9.4.12 for CCCH Local Threshold - * - * 05.58: - * - 8.5.2 CCCH Load indication - * - 9.3.15 Paging Load - * - * Approach: - * - Send paging command to subscriber - * - On Channel Request we will remember the reason - * - After the ACK we will request the identity - * - Then we will send assign the gsm_subscriber and - * - and call a callback - */ - -#include -#include -#include - -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include - -void *tall_paging_ctx; - -#define PAGING_TIMER 0, 500000 - -/* - * Kill one paging request update the internal list... - */ -static void paging_remove_request(struct gsm_bts_paging_state *paging_bts, - struct gsm_paging_request *to_be_deleted) -{ - osmo_timer_del(&to_be_deleted->T3113); - llist_del(&to_be_deleted->entry); - bsc_subscr_put(to_be_deleted->bsub); - talloc_free(to_be_deleted); -} - -static void page_ms(struct gsm_paging_request *request) -{ - uint8_t mi[128]; - unsigned int mi_len; - unsigned int page_group; - struct gsm_bts *bts = request->bts; - - /* the bts is down.. we will just wait for the paging to expire */ - if (!bts->oml_link) - return; - - log_set_context(LOG_CTX_BSC_SUBSCR, request->bsub); - - LOGP(DPAG, LOGL_INFO, "Going to send paging commands: imsi: %s tmsi: " - "0x%08x for ch. type %d (attempt %d)\n", request->bsub->imsi, - request->bsub->tmsi, request->chan_type, request->attempts); - - if (request->bsub->tmsi == GSM_RESERVED_TMSI) - mi_len = gsm48_generate_mid_from_imsi(mi, request->bsub->imsi); - else - mi_len = gsm48_generate_mid_from_tmsi(mi, request->bsub->tmsi); - - page_group = gsm0502_calc_paging_group(&bts->si_common.chan_desc, - str_to_imsi(request->bsub->imsi)); - gsm0808_page(bts, page_group, mi_len, mi, request->chan_type); - log_set_context(LOG_CTX_BSC_SUBSCR, NULL); -} - -static void paging_schedule_if_needed(struct gsm_bts_paging_state *paging_bts) -{ - if (llist_empty(&paging_bts->pending_requests)) - return; - - if (!osmo_timer_pending(&paging_bts->work_timer)) - osmo_timer_schedule(&paging_bts->work_timer, PAGING_TIMER); -} - - -static void paging_handle_pending_requests(struct gsm_bts_paging_state *paging_bts); -static void paging_give_credit(void *data) -{ - struct gsm_bts_paging_state *paging_bts = data; - - LOGP(DPAG, LOGL_NOTICE, "No slots available on bts nr %d\n", paging_bts->bts->nr); - paging_bts->available_slots = 20; - paging_handle_pending_requests(paging_bts); -} - -static int can_send_pag_req(struct gsm_bts *bts, int rsl_type) -{ - struct pchan_load pl; - int count; - - memset(&pl, 0, sizeof(pl)); - bts_chan_load(&pl, bts); - - switch (rsl_type) { - case RSL_CHANNEED_TCH_F: - case RSL_CHANNEED_TCH_ForH: - goto count_tch; - break; - case RSL_CHANNEED_SDCCH: - goto count_sdcch; - break; - case RSL_CHANNEED_ANY: - default: - if (bts->network->pag_any_tch) - goto count_tch; - else - goto count_sdcch; - break; - } - - return 0; - - /* could available SDCCH */ -count_sdcch: - count = 0; - count += pl.pchan[GSM_PCHAN_SDCCH8_SACCH8C].total - - pl.pchan[GSM_PCHAN_SDCCH8_SACCH8C].used; - count += pl.pchan[GSM_PCHAN_CCCH_SDCCH4].total - - pl.pchan[GSM_PCHAN_CCCH_SDCCH4].used; - return bts->paging.free_chans_need > count; - -count_tch: - count = 0; - count += pl.pchan[GSM_PCHAN_TCH_F].total - - pl.pchan[GSM_PCHAN_TCH_F].used; - if (bts->network->neci) - count += pl.pchan[GSM_PCHAN_TCH_H].total - - pl.pchan[GSM_PCHAN_TCH_H].used; - return bts->paging.free_chans_need > count; -} - -/* - * This is kicked by the periodic PAGING LOAD Indicator - * coming from abis_rsl.c - * - * We attempt to iterate once over the list of items but - * only upto available_slots. - */ -static void paging_handle_pending_requests(struct gsm_bts_paging_state *paging_bts) -{ - struct gsm_paging_request *request = NULL; - - /* - * Determine if the pending_requests list is empty and - * return then. - */ - if (llist_empty(&paging_bts->pending_requests)) { - /* since the list is empty, no need to reschedule the timer */ - return; - } - - /* - * In case the BTS does not provide us with load indication and we - * ran out of slots, call an autofill routine. It might be that the - * BTS did not like our paging messages and then we have counted down - * to zero and we do not get any messages. - */ - if (paging_bts->available_slots == 0) { - osmo_timer_setup(&paging_bts->credit_timer, paging_give_credit, - paging_bts); - osmo_timer_schedule(&paging_bts->credit_timer, 5, 0); - return; - } - - request = llist_entry(paging_bts->pending_requests.next, - struct gsm_paging_request, entry); - - /* we need to determine the number of free channels */ - if (paging_bts->free_chans_need != -1) { - if (can_send_pag_req(request->bts, request->chan_type) != 0) - goto skip_paging; - } - - /* handle the paging request now */ - page_ms(request); - paging_bts->available_slots--; - request->attempts++; - - /* take the current and add it to the back */ - llist_del(&request->entry); - llist_add_tail(&request->entry, &paging_bts->pending_requests); - -skip_paging: - osmo_timer_schedule(&paging_bts->work_timer, PAGING_TIMER); -} - -static void paging_worker(void *data) -{ - struct gsm_bts_paging_state *paging_bts = data; - - paging_handle_pending_requests(paging_bts); -} - -static void paging_init_if_needed(struct gsm_bts *bts) -{ - if (bts->paging.bts) - return; - - bts->paging.bts = bts; - INIT_LLIST_HEAD(&bts->paging.pending_requests); - osmo_timer_setup(&bts->paging.work_timer, paging_worker, - &bts->paging); - - /* Large number, until we get a proper message */ - bts->paging.available_slots = 20; -} - -static int paging_pending_request(struct gsm_bts_paging_state *bts, - struct bsc_subscr *bsub) -{ - struct gsm_paging_request *req; - - llist_for_each_entry(req, &bts->pending_requests, entry) { - if (bsub == req->bsub) - return 1; - } - - return 0; -} - -static void paging_T3113_expired(void *data) -{ - struct gsm_paging_request *req = (struct gsm_paging_request *)data; - void *cbfn_param; - gsm_cbfn *cbfn; - int msg; - - log_set_context(LOG_CTX_BSC_SUBSCR, req->bsub); - - LOGP(DPAG, LOGL_INFO, "T3113 expired for request %p (%s)\n", - req, bsc_subscr_name(req->bsub)); - - /* must be destroyed before calling cbfn, to prevent double free */ - rate_ctr_inc(&req->bts->network->bsc_ctrs->ctr[BSC_CTR_PAGING_EXPIRED]); - cbfn_param = req->cbfn_param; - cbfn = req->cbfn; - - /* did we ever manage to page the subscriber */ - msg = req->attempts > 0 ? GSM_PAGING_EXPIRED : GSM_PAGING_BUSY; - - /* destroy it now. Do not access req afterwards */ - paging_remove_request(&req->bts->paging, req); - - if (cbfn) - cbfn(GSM_HOOK_RR_PAGING, msg, NULL, NULL, - cbfn_param); - -} - -static int _paging_request(struct gsm_bts *bts, struct bsc_subscr *bsub, - int type, gsm_cbfn *cbfn, void *data) -{ - struct gsm_bts_paging_state *bts_entry = &bts->paging; - struct gsm_paging_request *req; - - if (paging_pending_request(bts_entry, bsub)) { - LOGP(DPAG, LOGL_INFO, "Paging request already pending for %s\n", - bsc_subscr_name(bsub)); - return -EEXIST; - } - - LOGP(DPAG, LOGL_DEBUG, "Start paging of subscriber %s on bts %d.\n", - bsc_subscr_name(bsub), bts->nr); - req = talloc_zero(tall_paging_ctx, struct gsm_paging_request); - req->bsub = bsc_subscr_get(bsub); - req->bts = bts; - req->chan_type = type; - req->cbfn = cbfn; - req->cbfn_param = data; - osmo_timer_setup(&req->T3113, paging_T3113_expired, req); - osmo_timer_schedule(&req->T3113, bts->network->T3113, 0); - llist_add_tail(&req->entry, &bts_entry->pending_requests); - paging_schedule_if_needed(bts_entry); - - return 0; -} - -int paging_request_bts(struct gsm_bts *bts, struct bsc_subscr *bsub, - int type, gsm_cbfn *cbfn, void *data) -{ - int rc; - - /* skip all currently inactive TRX */ - if (!trx_is_usable(bts->c0)) - return 0; - - /* maybe it is the first time we use it */ - paging_init_if_needed(bts); - - /* Trigger paging, pass any error to the caller */ - rc = _paging_request(bts, bsub, type, cbfn, data); - if (rc < 0) - return rc; - return 1; -} - -int paging_request(struct gsm_network *network, struct bsc_subscr *bsub, - int type, gsm_cbfn *cbfn, void *data) -{ - struct gsm_bts *bts = NULL; - int num_pages = 0; - - rate_ctr_inc(&network->bsc_ctrs->ctr[BSC_CTR_PAGING_ATTEMPTED]); - - /* start paging subscriber on all BTS within Location Area */ - do { - int rc; - - bts = gsm_bts_by_lac(network, bsub->lac, bts); - if (!bts) - break; - - rc = paging_request_bts(bts, bsub, type, cbfn, data); - if (rc < 0) { - paging_request_stop(&network->bts_list, NULL, bsub, - NULL, NULL); - return rc; - } - num_pages += rc; - } while (1); - - if (num_pages == 0) - rate_ctr_inc(&network->bsc_ctrs->ctr[BSC_CTR_PAGING_DETACHED]); - - return num_pages; -} - - -/* we consciously ignore the type of the request here */ -static void _paging_request_stop(struct gsm_bts *bts, struct bsc_subscr *bsub, - struct gsm_subscriber_connection *conn, - struct msgb *msg) -{ - struct gsm_bts_paging_state *bts_entry = &bts->paging; - struct gsm_paging_request *req, *req2; - - paging_init_if_needed(bts); - - llist_for_each_entry_safe(req, req2, &bts_entry->pending_requests, - entry) { - if (req->bsub == bsub) { - gsm_cbfn *cbfn = req->cbfn; - void *param = req->cbfn_param; - - /* now give up the data structure */ - paging_remove_request(&bts->paging, req); - req = NULL; - - if (conn && cbfn) { - LOGP(DPAG, LOGL_DEBUG, "Stop paging %s on bts %d, calling cbfn.\n", bsub->imsi, bts->nr); - cbfn(GSM_HOOK_RR_PAGING, GSM_PAGING_SUCCEEDED, - msg, conn, param); - } else - LOGP(DPAG, LOGL_DEBUG, "Stop paging %s on bts %d silently.\n", bsub->imsi, bts->nr); - break; - } - } -} - -/* Stop paging on all other bts' */ -void paging_request_stop(struct llist_head *bts_list, - struct gsm_bts *_bts, struct bsc_subscr *bsub, - struct gsm_subscriber_connection *conn, - struct msgb *msg) -{ - struct gsm_bts *bts; - - log_set_context(LOG_CTX_BSC_SUBSCR, bsub); - - /* Stop this first and dispatch the request */ - if (_bts) - _paging_request_stop(_bts, bsub, conn, msg); - - /* Make sure to cancel this everywhere else */ - llist_for_each_entry(bts, bts_list, list) { - /* Sort of an optimization. */ - if (bts == _bts) - continue; - _paging_request_stop(bts, bsub, NULL, NULL); - } -} - -void paging_update_buffer_space(struct gsm_bts *bts, uint16_t free_slots) -{ - paging_init_if_needed(bts); - - osmo_timer_del(&bts->paging.credit_timer); - bts->paging.available_slots = free_slots; - paging_schedule_if_needed(&bts->paging); -} - -unsigned int paging_pending_requests_nr(struct gsm_bts *bts) -{ - unsigned int requests = 0; - struct gsm_paging_request *req; - - paging_init_if_needed(bts); - - llist_for_each_entry(req, &bts->paging.pending_requests, entry) - ++requests; - - return requests; -} - -/** - * Find any paging data for the given subscriber at the given BTS. - */ -void *paging_get_data(struct gsm_bts *bts, struct bsc_subscr *bsub) -{ - struct gsm_paging_request *req; - - llist_for_each_entry(req, &bts->paging.pending_requests, entry) - if (req->bsub == bsub) - return req->cbfn_param; - - return NULL; -} diff --git a/openbsc/src/libbsc/pcu_sock.c b/openbsc/src/libbsc/pcu_sock.c deleted file mode 100644 index 98e12fad..00000000 --- a/openbsc/src/libbsc/pcu_sock.c +++ /dev/null @@ -1,742 +0,0 @@ -/* pcu_sock.c: Connect from PCU via unix domain socket */ - -/* (C) 2008-2010 by Harald Welte - * (C) 2009-2012 by Andreas Eversberg - * (C) 2012 by Holger Hans Peter Freyther - * All Rights Reserved - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - * - */ - -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include - -static int pcu_sock_send(struct gsm_bts *bts, struct msgb *msg); -uint32_t trx_get_hlayer1(struct gsm_bts_trx *trx); -int pcu_direct = 1; - -static const char *sapi_string[] = { - [PCU_IF_SAPI_RACH] = "RACH", - [PCU_IF_SAPI_AGCH] = "AGCH", - [PCU_IF_SAPI_PCH] = "PCH", - [PCU_IF_SAPI_BCCH] = "BCCH", - [PCU_IF_SAPI_PDTCH] = "PDTCH", - [PCU_IF_SAPI_PRACH] = "PRACH", - [PCU_IF_SAPI_PTCCH] = "PTCCH", - [PCU_IF_SAPI_AGCH_DT] = "AGCH_DT", -}; - -/* Check if BTS has a PCU connection */ -static bool pcu_connected(struct gsm_bts *bts) -{ - struct pcu_sock_state *state = bts->pcu_state; - - if (!state) - return false; - if (state->conn_bfd.fd <= 0) - return false; - return true; -} - -/* - * PCU messages - */ - -/* Set up an message buffer to package an pcu interface message */ -struct msgb *pcu_msgb_alloc(uint8_t msg_type, uint8_t bts_nr) -{ - struct msgb *msg; - struct gsm_pcu_if *pcu_prim; - - msg = msgb_alloc(sizeof(struct gsm_pcu_if), "pcu_sock_tx"); - if (!msg) - return NULL; - - msgb_put(msg, sizeof(struct gsm_pcu_if)); - pcu_prim = (struct gsm_pcu_if *) msg->data; - pcu_prim->msg_type = msg_type; - pcu_prim->bts_nr = bts_nr; - - return msg; -} - -/* Helper function exclusivly used by pcu_if_signal_cb() */ -static bool ts_should_be_pdch(struct gsm_bts_trx_ts *ts) { - if (ts->pchan == GSM_PCHAN_PDCH) - return true; - if (ts->pchan == GSM_PCHAN_TCH_F_PDCH) { - /* When we're busy deactivating the PDCH, we first set - * DEACT_PENDING, tell the PCU about it and wait for a - * response. So DEACT_PENDING means "no PDCH" to the PCU. - * Similarly, when we're activating PDCH, we set the - * ACT_PENDING and wait for an activation response from the - * PCU, so ACT_PENDING means "is PDCH". */ - if (ts->flags & TS_F_PDCH_ACTIVE) - return !(ts->flags & TS_F_PDCH_DEACT_PENDING); - else - return (ts->flags & TS_F_PDCH_ACT_PENDING); - } - if (ts->pchan == GSM_PCHAN_TCH_F_TCH_H_PDCH) { - /* - * When we're busy de-/activating the PDCH, we first set - * ts->dyn.pchan_want, tell the PCU about it and wait for a - * response. So only care about dyn.pchan_want here. - */ - return ts->dyn.pchan_want == GSM_PCHAN_PDCH; - } - return false; -} - -/* Send BTS properties to the PCU */ -static int pcu_tx_info_ind(struct gsm_bts *bts) -{ - struct msgb *msg; - struct gsm_pcu_if *pcu_prim; - struct gsm_pcu_if_info_ind *info_ind; - struct gprs_rlc_cfg *rlcc; - struct gsm_bts_gprs_nsvc *nsvc; - struct gsm_bts_trx *trx; - struct gsm_bts_trx_ts *ts; - int i, j; - - OSMO_ASSERT(bts); - OSMO_ASSERT(bts->network); - - LOGP(DPCU, LOGL_INFO, "Sending info for BTS %d\n",bts->nr); - - rlcc = &bts->gprs.cell.rlc_cfg; - - msg = pcu_msgb_alloc(PCU_IF_MSG_INFO_IND, bts->nr); - if (!msg) - return -ENOMEM; - - pcu_prim = (struct gsm_pcu_if *) msg->data; - info_ind = &pcu_prim->u.info_ind; - info_ind->version = PCU_IF_VERSION; - info_ind->flags |= PCU_IF_FLAG_ACTIVE; - - if (pcu_direct) - info_ind->flags |= PCU_IF_FLAG_SYSMO; - - /* RAI */ - info_ind->mcc = bts->network->country_code; - info_ind->mnc = bts->network->network_code; - info_ind->lac = bts->location_area_code; - info_ind->rac = bts->gprs.rac; - - /* NSE */ - info_ind->nsei = bts->gprs.nse.nsei; - memcpy(info_ind->nse_timer, bts->gprs.nse.timer, 7); - memcpy(info_ind->cell_timer, bts->gprs.cell.timer, 11); - - /* cell attributes */ - info_ind->cell_id = bts->cell_identity; - info_ind->repeat_time = rlcc->paging.repeat_time; - info_ind->repeat_count = rlcc->paging.repeat_count; - info_ind->bvci = bts->gprs.cell.bvci; - info_ind->t3142 = rlcc->parameter[RLC_T3142]; - info_ind->t3169 = rlcc->parameter[RLC_T3169]; - info_ind->t3191 = rlcc->parameter[RLC_T3191]; - info_ind->t3193_10ms = rlcc->parameter[RLC_T3193]; - info_ind->t3195 = rlcc->parameter[RLC_T3195]; - info_ind->n3101 = rlcc->parameter[RLC_N3101]; - info_ind->n3103 = rlcc->parameter[RLC_N3103]; - info_ind->n3105 = rlcc->parameter[RLC_N3105]; - info_ind->cv_countdown = rlcc->parameter[CV_COUNTDOWN]; - if (rlcc->cs_mask & (1 << GPRS_CS1)) - info_ind->flags |= PCU_IF_FLAG_CS1; - if (rlcc->cs_mask & (1 << GPRS_CS2)) - info_ind->flags |= PCU_IF_FLAG_CS2; - if (rlcc->cs_mask & (1 << GPRS_CS3)) - info_ind->flags |= PCU_IF_FLAG_CS3; - if (rlcc->cs_mask & (1 << GPRS_CS4)) - info_ind->flags |= PCU_IF_FLAG_CS4; - if (bts->gprs.mode == BTS_GPRS_EGPRS) { - if (rlcc->cs_mask & (1 << GPRS_MCS1)) - info_ind->flags |= PCU_IF_FLAG_MCS1; - if (rlcc->cs_mask & (1 << GPRS_MCS2)) - info_ind->flags |= PCU_IF_FLAG_MCS2; - if (rlcc->cs_mask & (1 << GPRS_MCS3)) - info_ind->flags |= PCU_IF_FLAG_MCS3; - if (rlcc->cs_mask & (1 << GPRS_MCS4)) - info_ind->flags |= PCU_IF_FLAG_MCS4; - if (rlcc->cs_mask & (1 << GPRS_MCS5)) - info_ind->flags |= PCU_IF_FLAG_MCS5; - if (rlcc->cs_mask & (1 << GPRS_MCS6)) - info_ind->flags |= PCU_IF_FLAG_MCS6; - if (rlcc->cs_mask & (1 << GPRS_MCS7)) - info_ind->flags |= PCU_IF_FLAG_MCS7; - if (rlcc->cs_mask & (1 << GPRS_MCS8)) - info_ind->flags |= PCU_IF_FLAG_MCS8; - if (rlcc->cs_mask & (1 << GPRS_MCS9)) - info_ind->flags |= PCU_IF_FLAG_MCS9; - } -#warning "isn't dl_tbf_ext wrong?: * 10 and no ntohs" - info_ind->dl_tbf_ext = rlcc->parameter[T_DL_TBF_EXT]; -#warning "isn't ul_tbf_ext wrong?: * 10 and no ntohs" - info_ind->ul_tbf_ext = rlcc->parameter[T_UL_TBF_EXT]; - info_ind->initial_cs = rlcc->initial_cs; - info_ind->initial_mcs = rlcc->initial_mcs; - - /* NSVC */ - for (i = 0; i < ARRAY_SIZE(info_ind->nsvci); i++) { - nsvc = &bts->gprs.nsvc[i]; - info_ind->nsvci[i] = nsvc->nsvci; - info_ind->local_port[i] = nsvc->local_port; - info_ind->remote_port[i] = nsvc->remote_port; - info_ind->remote_ip[i] = nsvc->remote_ip; - } - - for (i = 0; i < ARRAY_SIZE(info_ind->trx); i++) { - trx = gsm_bts_trx_num(bts, i); - if (!trx) - continue; - info_ind->trx[i].hlayer1 = 0x2342; - info_ind->trx[i].pdch_mask = 0; - info_ind->trx[i].arfcn = trx->arfcn; - for (j = 0; j < ARRAY_SIZE(trx->ts); j++) { - ts = &trx->ts[j]; - if (ts->mo.nm_state.operational == NM_OPSTATE_ENABLED - && ts_should_be_pdch(ts)) { - info_ind->trx[i].pdch_mask |= (1 << j); - info_ind->trx[i].tsc[j] = - (ts->tsc >= 0) ? ts->tsc : bts->bsic & 7; - LOGP(DPCU, LOGL_INFO, "trx=%d ts=%d: " - "available (tsc=%d arfcn=%d)\n", - trx->nr, ts->nr, - info_ind->trx[i].tsc[j], - info_ind->trx[i].arfcn); - } - } - } - - return pcu_sock_send(bts, msg); -} - -void pcu_info_update(struct gsm_bts *bts) -{ - if (pcu_connected(bts)) - pcu_tx_info_ind(bts); -} - -/* Forward rach indication to PCU */ -int pcu_tx_rach_ind(struct gsm_bts *bts, int16_t qta, uint16_t ra, uint32_t fn, - uint8_t is_11bit, enum ph_burst_type burst_type) -{ - struct msgb *msg; - struct gsm_pcu_if *pcu_prim; - struct gsm_pcu_if_rach_ind *rach_ind; - - /* Bail if no PCU is connected */ - if (!pcu_connected(bts)) { - LOGP(DRSL, LOGL_ERROR, "BTS %d CHAN RQD(GPRS) but PCU not " - "connected!\n", bts->nr); - return -ENODEV; - } - - LOGP(DPCU, LOGL_INFO, "Sending RACH indication: qta=%d, ra=%d, " - "fn=%d\n", qta, ra, fn); - - msg = pcu_msgb_alloc(PCU_IF_MSG_RACH_IND, bts->nr); - if (!msg) - return -ENOMEM; - pcu_prim = (struct gsm_pcu_if *) msg->data; - rach_ind = &pcu_prim->u.rach_ind; - - rach_ind->sapi = PCU_IF_SAPI_RACH; - rach_ind->ra = ra; - rach_ind->qta = qta; - rach_ind->fn = fn; - rach_ind->is_11bit = is_11bit; - rach_ind->burst_type = burst_type; - - return pcu_sock_send(bts, msg); -} - -/* Confirm the sending of an immediate assignment to the pcu */ -int pcu_tx_imm_ass_sent(struct gsm_bts *bts, uint32_t tlli) -{ - struct msgb *msg; - struct gsm_pcu_if *pcu_prim; - struct gsm_pcu_if_data_cnf_dt *data_cnf_dt; - - LOGP(DPCU, LOGL_INFO, "Sending PCH confirm with direct TLLI\n"); - - msg = pcu_msgb_alloc(PCU_IF_MSG_DATA_CNF_DT, bts->nr); - if (!msg) - return -ENOMEM; - pcu_prim = (struct gsm_pcu_if *) msg->data; - data_cnf_dt = &pcu_prim->u.data_cnf_dt; - - data_cnf_dt->sapi = PCU_IF_SAPI_PCH; - data_cnf_dt->tlli = tlli; - - return pcu_sock_send(bts, msg); -} - -/* we need to decode the raw RR paging messsage (see PCU code - * Encoding::write_paging_request) and extract the mobile identity - * (P-TMSI) from it */ -static int pcu_rx_rr_paging(struct gsm_bts *bts, uint8_t paging_group, - const uint8_t *raw_rr_msg) -{ - struct gsm48_paging1 *p1 = (struct gsm48_paging1 *) raw_rr_msg; - uint8_t chan_needed; - unsigned int mi_len; - uint8_t *mi; - int rc; - - switch (p1->msg_type) { - case GSM48_MT_RR_PAG_REQ_1: - chan_needed = (p1->cneed2 << 2) | p1->cneed1; - mi_len = p1->data[0]; - mi = p1->data+1; - LOGP(DPCU, LOGL_ERROR, "PCU Sends paging " - "request type %02x (chan_needed=%02x, mi_len=%u, mi=%s)\n", - p1->msg_type, chan_needed, mi_len, - osmo_hexdump_nospc(mi,mi_len)); - /* NOTE: We will have to add 2 to mi_len and subtract 2 from - * the mi pointer because rsl_paging_cmd() will perform the - * reverse operations. This is because rsl_paging_cmd() is - * normally expected to chop off the element identifier (0xC0) - * and the length field. In our parameter, we do not have - * those fields included. */ - rc = rsl_paging_cmd(bts, paging_group, mi_len+2, mi-2, - chan_needed, true); - break; - case GSM48_MT_RR_PAG_REQ_2: - case GSM48_MT_RR_PAG_REQ_3: - LOGP(DPCU, LOGL_ERROR, "PCU Sends unsupported paging " - "request type %02x\n", p1->msg_type); - rc = -EINVAL; - break; - default: - LOGP(DPCU, LOGL_ERROR, "PCU Sends unknown paging " - "request type %02x\n", p1->msg_type); - rc = -EINVAL; - break; - } - - return rc; -} - -static int pcu_rx_data_req(struct gsm_bts *bts, uint8_t msg_type, - struct gsm_pcu_if_data *data_req) -{ - uint8_t is_ptcch; - struct gsm_bts_trx *trx; - struct gsm_bts_trx_ts *ts; - struct msgb *msg; - char imsi_digit_buf[4]; - uint32_t tlli = -1; - uint8_t pag_grp; - int rc = 0; - - LOGP(DPCU, LOGL_DEBUG, "Data request received: sapi=%s arfcn=%d " - "block=%d data=%s\n", sapi_string[data_req->sapi], - data_req->arfcn, data_req->block_nr, - osmo_hexdump(data_req->data, data_req->len)); - - switch (data_req->sapi) { - case PCU_IF_SAPI_PCH: - /* the first three bytes are the last three digits of - * the IMSI, which we need to compute the paging group */ - imsi_digit_buf[0] = data_req->data[0]; - imsi_digit_buf[1] = data_req->data[1]; - imsi_digit_buf[2] = data_req->data[2]; - imsi_digit_buf[3] = '\0'; - LOGP(DPCU, LOGL_DEBUG, "SAPI PCH imsi %s\n", imsi_digit_buf); - pag_grp = gsm0502_calc_paging_group(&bts->si_common.chan_desc, - str_to_imsi(imsi_digit_buf)); - pcu_rx_rr_paging(bts, pag_grp, data_req->data+3); - break; - case PCU_IF_SAPI_AGCH: - msg = msgb_alloc(data_req->len, "pcu_agch"); - if (!msg) { - rc = -ENOMEM; - break; - } - msg->l3h = msgb_put(msg, data_req->len); - memcpy(msg->l3h, data_req->data, data_req->len); - - if (rsl_imm_assign_cmd(bts, msg->len, msg->data)) { - msgb_free(msg); - rc = -EIO; - } - break; - case PCU_IF_SAPI_AGCH_DT: - /* DT = direct tlli. A tlli is prefixed */ - - if (data_req->len < 5) { - LOGP(DPCU, LOGL_ERROR, "Received PCU data request with " - "invalid/small length %d\n", data_req->len); - break; - } - tlli = *((uint32_t *)data_req->data); - - msg = msgb_alloc(data_req->len - 4, "pcu_agch"); - if (!msg) { - rc = -ENOMEM; - break; - } - msg->l3h = msgb_put(msg, data_req->len - 4); - memcpy(msg->l3h, data_req->data + 4, data_req->len - 4); - - if (bts->type == GSM_BTS_TYPE_RBS2000) - rc = rsl_ericsson_imm_assign_cmd(bts, tlli, msg->len, msg->data); - else - rc = rsl_imm_assign_cmd(bts, msg->len, msg->data); - - if (rc) { - msgb_free(msg); - rc = -EIO; - } - break; - default: - LOGP(DPCU, LOGL_ERROR, "Received PCU data request with " - "unsupported sapi %d\n", data_req->sapi); - rc = -EINVAL; - } - - return rc; -} - -static int pcu_rx(struct gsm_network *net, uint8_t msg_type, - struct gsm_pcu_if *pcu_prim) -{ - int rc = 0; - struct gsm_bts *bts; - - /* FIXME: allow multiple BTS */ - bts = llist_entry(net->bts_list.next, struct gsm_bts, list); - - switch (msg_type) { - case PCU_IF_MSG_DATA_REQ: - case PCU_IF_MSG_PAG_REQ: - rc = pcu_rx_data_req(bts, msg_type, &pcu_prim->u.data_req); - break; - default: - LOGP(DPCU, LOGL_ERROR, "Received unknwon PCU msg type %d\n", - msg_type); - rc = -EINVAL; - } - - return rc; -} - -/* - * PCU socket interface - */ - -static int pcu_sock_send(struct gsm_bts *bts, struct msgb *msg) -{ - struct pcu_sock_state *state = bts->pcu_state; - struct osmo_fd *conn_bfd; - struct gsm_pcu_if *pcu_prim = (struct gsm_pcu_if *) msg->data; - - if (!state) { - if (pcu_prim->msg_type != PCU_IF_MSG_TIME_IND) - LOGP(DPCU, LOGL_INFO, "PCU socket not created, " - "dropping message\n"); - msgb_free(msg); - return -EINVAL; - } - conn_bfd = &state->conn_bfd; - if (conn_bfd->fd <= 0) { - if (pcu_prim->msg_type != PCU_IF_MSG_TIME_IND) - LOGP(DPCU, LOGL_NOTICE, "PCU socket not connected, " - "dropping message\n"); - msgb_free(msg); - return -EIO; - } - msgb_enqueue(&state->upqueue, msg); - conn_bfd->when |= BSC_FD_WRITE; - - return 0; -} - -static void pcu_sock_close(struct pcu_sock_state *state) -{ - struct osmo_fd *bfd = &state->conn_bfd; - struct gsm_bts *bts; - struct gsm_bts_trx *trx; - struct gsm_bts_trx_ts *ts; - int i, j; - - /* FIXME: allow multiple BTS */ - bts = llist_entry(state->net->bts_list.next, struct gsm_bts, list); - - LOGP(DPCU, LOGL_NOTICE, "PCU socket has LOST connection\n"); - - close(bfd->fd); - bfd->fd = -1; - osmo_fd_unregister(bfd); - - /* re-enable the generation of ACCEPT for new connections */ - state->listen_bfd.when |= BSC_FD_READ; - -#if 0 - /* remove si13, ... */ - bts->si_valid &= ~(1 << SYSINFO_TYPE_13); - osmo_signal_dispatch(SS_GLOBAL, S_NEW_SYSINFO, bts); -#endif - - /* release PDCH */ - for (i = 0; i < 8; i++) { - trx = gsm_bts_trx_num(bts, i); - if (!trx) - break; - for (j = 0; j < 8; j++) { - ts = &trx->ts[j]; - if (ts->mo.nm_state.operational == NM_OPSTATE_ENABLED - && ts->pchan == GSM_PCHAN_PDCH) { - printf("l1sap_chan_rel(trx,gsm_lchan2chan_nr(ts->lchan));\n"); - } - } - } - - /* flush the queue */ - while (!llist_empty(&state->upqueue)) { - struct msgb *msg = msgb_dequeue(&state->upqueue); - msgb_free(msg); - } -} - -static int pcu_sock_read(struct osmo_fd *bfd) -{ - struct pcu_sock_state *state = (struct pcu_sock_state *)bfd->data; - struct gsm_pcu_if *pcu_prim; - struct msgb *msg; - int rc; - - msg = msgb_alloc(sizeof(*pcu_prim), "pcu_sock_rx"); - if (!msg) - return -ENOMEM; - - pcu_prim = (struct gsm_pcu_if *) msg->tail; - - rc = recv(bfd->fd, msg->tail, msgb_tailroom(msg), 0); - if (rc == 0) - goto close; - - if (rc < 0) { - if (errno == EAGAIN) - return 0; - goto close; - } - - rc = pcu_rx(state->net, pcu_prim->msg_type, pcu_prim); - - /* as we always synchronously process the message in pcu_rx() and - * its callbacks, we can free the message here. */ - msgb_free(msg); - - return rc; - -close: - msgb_free(msg); - pcu_sock_close(state); - return -1; -} - -static int pcu_sock_write(struct osmo_fd *bfd) -{ - struct pcu_sock_state *state = bfd->data; - int rc; - - while (!llist_empty(&state->upqueue)) { - struct msgb *msg, *msg2; - struct gsm_pcu_if *pcu_prim; - - /* peek at the beginning of the queue */ - msg = llist_entry(state->upqueue.next, struct msgb, list); - pcu_prim = (struct gsm_pcu_if *)msg->data; - - bfd->when &= ~BSC_FD_WRITE; - - /* bug hunter 8-): maybe someone forgot msgb_put(...) ? */ - if (!msgb_length(msg)) { - LOGP(DPCU, LOGL_ERROR, "message type (%d) with ZERO " - "bytes!\n", pcu_prim->msg_type); - goto dontsend; - } - - /* try to send it over the socket */ - rc = write(bfd->fd, msgb_data(msg), msgb_length(msg)); - if (rc == 0) - goto close; - if (rc < 0) { - if (errno == EAGAIN) { - bfd->when |= BSC_FD_WRITE; - break; - } - goto close; - } - -dontsend: - /* _after_ we send it, we can deueue */ - msg2 = msgb_dequeue(&state->upqueue); - assert(msg == msg2); - msgb_free(msg); - } - return 0; - -close: - pcu_sock_close(state); - - return -1; -} - -static int pcu_sock_cb(struct osmo_fd *bfd, unsigned int flags) -{ - int rc = 0; - - if (flags & BSC_FD_READ) - rc = pcu_sock_read(bfd); - if (rc < 0) - return rc; - - if (flags & BSC_FD_WRITE) - rc = pcu_sock_write(bfd); - - return rc; -} - -/* accept connection comming from PCU */ -static int pcu_sock_accept(struct osmo_fd *bfd, unsigned int flags) -{ - struct pcu_sock_state *state = (struct pcu_sock_state *)bfd->data; - struct osmo_fd *conn_bfd = &state->conn_bfd; - struct sockaddr_un un_addr; - socklen_t len; - int rc; - - len = sizeof(un_addr); - rc = accept(bfd->fd, (struct sockaddr *) &un_addr, &len); - if (rc < 0) { - LOGP(DPCU, LOGL_ERROR, "Failed to accept a new connection\n"); - return -1; - } - - if (conn_bfd->fd >= 0) { - LOGP(DPCU, LOGL_NOTICE, "PCU connects but we already have " - "another active connection ?!?\n"); - /* We already have one PCU connected, this is all we support */ - state->listen_bfd.when &= ~BSC_FD_READ; - close(rc); - return 0; - } - - conn_bfd->fd = rc; - conn_bfd->when = BSC_FD_READ; - conn_bfd->cb = pcu_sock_cb; - conn_bfd->data = state; - - if (osmo_fd_register(conn_bfd) != 0) { - LOGP(DPCU, LOGL_ERROR, "Failed to register new connection " - "fd\n"); - close(conn_bfd->fd); - conn_bfd->fd = -1; - return -1; - } - - LOGP(DPCU, LOGL_NOTICE, "PCU socket connected to external PCU\n"); - - return 0; -} - -/* Open connection to PCU */ -int pcu_sock_init(const char *path, struct gsm_bts *bts) -{ - struct pcu_sock_state *state; - struct osmo_fd *bfd; - int rc; - - state = talloc_zero(NULL, struct pcu_sock_state); - if (!state) - return -ENOMEM; - - INIT_LLIST_HEAD(&state->upqueue); - state->net = bts->network; - state->conn_bfd.fd = -1; - - bfd = &state->listen_bfd; - - bfd->fd = osmo_sock_unix_init(SOCK_SEQPACKET, 0, path, - OSMO_SOCK_F_BIND); - if (bfd->fd < 0) { - LOGP(DPCU, LOGL_ERROR, "Could not create unix socket: %s\n", - strerror(errno)); - talloc_free(state); - return -1; - } - - bfd->when = BSC_FD_READ; - bfd->cb = pcu_sock_accept; - bfd->data = state; - - rc = osmo_fd_register(bfd); - if (rc < 0) { - LOGP(DPCU, LOGL_ERROR, "Could not register listen fd: %d\n", - rc); - close(bfd->fd); - talloc_free(state); - return rc; - } - - bts->pcu_state = state; - return 0; -} - -/* Close connection to PCU */ -void pcu_sock_exit(struct gsm_bts *bts) -{ - struct pcu_sock_state *state = bts->pcu_state; - struct osmo_fd *bfd, *conn_bfd; - - if (!state) - return; - - conn_bfd = &state->conn_bfd; - if (conn_bfd->fd > 0) - pcu_sock_close(state); - bfd = &state->listen_bfd; - close(bfd->fd); - osmo_fd_unregister(bfd); - talloc_free(state); - bts->pcu_state = NULL; -} - diff --git a/openbsc/src/libbsc/rest_octets.c b/openbsc/src/libbsc/rest_octets.c deleted file mode 100644 index fdab70a0..00000000 --- a/openbsc/src/libbsc/rest_octets.c +++ /dev/null @@ -1,860 +0,0 @@ -/* GSM Mobile Radio Interface Layer 3 messages on the A-bis interface, - * rest octet handling according to - * 3GPP TS 04.08 version 7.21.0 Release 1998 / ETSI TS 100 940 V7.21.0 */ - -/* (C) 2009 by Harald Welte - * - * All Rights Reserved - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation; either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public 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 - -/* generate SI1 rest octets */ -int rest_octets_si1(uint8_t *data, uint8_t *nch_pos, int is1800_net) -{ - struct bitvec bv; - - memset(&bv, 0, sizeof(bv)); - bv.data = data; - bv.data_len = 1; - - if (nch_pos) { - bitvec_set_bit(&bv, H); - bitvec_set_uint(&bv, *nch_pos, 5); - } else - bitvec_set_bit(&bv, L); - - if (is1800_net) - bitvec_set_bit(&bv, L); - else - bitvec_set_bit(&bv, H); - - bitvec_spare_padding(&bv, 6); - return bv.data_len; -} - -/* Append Repeated E-UTRAN Neighbour Cell to bitvec: see 3GPP TS 44.018 Table 10.5.2.33b.1 */ -static inline void append_eutran_neib_cell(struct bitvec *bv, struct gsm_bts *bts, uint8_t budget) -{ - const struct osmo_earfcn_si2q *e = &bts->si_common.si2quater_neigh_list; - unsigned i, skip = 0; - size_t offset = bts->e_offset; - uint8_t rem = budget - 6, earfcn_budget; /* account for mandatory stop bit and THRESH_E-UTRAN_high */ - - if (budget <= 6) - return; - - OSMO_ASSERT(budget <= SI2Q_MAX_LEN); - - /* first we have to properly adjust budget requirements */ - if (e->prio_valid) /* E-UTRAN_PRIORITY: 3GPP TS 45.008*/ - rem -= 4; - else - rem--; - - if (e->thresh_lo_valid) /* THRESH_E-UTRAN_low: */ - rem -= 6; - else - rem--; - - if (e->qrxlm_valid) /* E-UTRAN_QRXLEVMIN: */ - rem -= 6; - else - rem--; - - /* now we can proceed with actually adding EARFCNs within adjusted budget limit */ - for (i = 0; i < e->length; i++) { - if (e->arfcn[i] != OSMO_EARFCN_INVALID) { - if (skip < offset) { - skip++; /* ignore EARFCNs added on previous calls */ - } else { - earfcn_budget = 17; /* compute budget per-EARFCN */ - if (OSMO_EARFCN_MEAS_INVALID == e->meas_bw[i]) - earfcn_budget++; - else - earfcn_budget += 4; - - if (rem - earfcn_budget < 0) - break; - else { - bts->e_offset++; - rem -= earfcn_budget; - bitvec_set_bit(bv, 1); /* EARFCN: */ - bitvec_set_uint(bv, e->arfcn[i], 16); - - if (OSMO_EARFCN_MEAS_INVALID == e->meas_bw[i]) - bitvec_set_bit(bv, 0); - else { /* Measurement Bandwidth: 9.1.54 */ - bitvec_set_bit(bv, 1); - bitvec_set_uint(bv, e->meas_bw[i], 3); - } - } - } - } - } - - /* stop bit - end of EARFCN + Measurement Bandwidth sequence */ - bitvec_set_bit(bv, 0); - - /* Note: we don't support different EARFCN arrays each with different priority, threshold etc. */ - - if (e->prio_valid) { - /* E-UTRAN_PRIORITY: 3GPP TS 45.008*/ - bitvec_set_bit(bv, 1); - bitvec_set_uint(bv, e->prio, 3); - } else - bitvec_set_bit(bv, 0); - - /* THRESH_E-UTRAN_high */ - bitvec_set_uint(bv, e->thresh_hi, 5); - - if (e->thresh_lo_valid) { - /* THRESH_E-UTRAN_low: */ - bitvec_set_bit(bv, 1); - bitvec_set_uint(bv, e->thresh_lo, 5); - } else - bitvec_set_bit(bv, 0); - - if (e->qrxlm_valid) { - /* E-UTRAN_QRXLEVMIN: */ - bitvec_set_bit(bv, 1); - bitvec_set_uint(bv, e->qrxlm, 5); - } else - bitvec_set_bit(bv, 0); -} - -static inline void append_earfcn(struct bitvec *bv, struct gsm_bts *bts, uint8_t budget) -{ - int rem = budget - 25; - if (rem <= 0) - return; - - OSMO_ASSERT(budget <= SI2Q_MAX_LEN); - - /* Additions in Rel-5: */ - bitvec_set_bit(bv, H); - /* No 3G Additional Measurement Param. Descr. */ - bitvec_set_bit(bv, 0); - /* No 3G ADDITIONAL MEASUREMENT Param. Descr. 2 */ - bitvec_set_bit(bv, 0); - /* Additions in Rel-6: */ - bitvec_set_bit(bv, H); - /* 3G_CCN_ACTIVE */ - bitvec_set_bit(bv, 0); - /* Additions in Rel-7: */ - bitvec_set_bit(bv, H); - /* No 700_REPORTING_OFFSET */ - bitvec_set_bit(bv, 0); - /* No 810_REPORTING_OFFSET */ - bitvec_set_bit(bv, 0); - /* Additions in Rel-8: */ - bitvec_set_bit(bv, H); - - /* Priority and E-UTRAN Parameters Description */ - bitvec_set_bit(bv, 1); - - /* No Serving Cell Priority Parameters Descr. */ - bitvec_set_bit(bv, 0); - /* No 3G Priority Parameters Description */ - bitvec_set_bit(bv, 0); - /* E-UTRAN Parameters Description */ - bitvec_set_bit(bv, 1); - - /* E-UTRAN_CCN_ACTIVE */ - bitvec_set_bit(bv, 0); - /* E-UTRAN_Start: 9.1.54 */ - bitvec_set_bit(bv, 1); - /* E-UTRAN_Stop: 9.1.54 */ - bitvec_set_bit(bv, 1); - - /* No E-UTRAN Measurement Parameters Descr. */ - bitvec_set_bit(bv, 0); - /* No GPRS E-UTRAN Measurement Param. Descr. */ - bitvec_set_bit(bv, 0); - - /* Note: each of next 3 "repeated" structures might be repeated any - (0, 1, 2...) times - we only support 1 and 0 */ - - /* Repeated E-UTRAN Neighbour Cells */ - bitvec_set_bit(bv, 1); - - /* N. B: 25 bits are set in append_earfcn() - keep it in sync with budget adjustment below: */ - append_eutran_neib_cell(bv, bts, rem); - - /* stop bit - end of Repeated E-UTRAN Neighbour Cells sequence: */ - bitvec_set_bit(bv, 0); - - /* Note: following 2 repeated structs are not supported ATM */ - /* stop bit - end of Repeated E-UTRAN Not Allowed Cells sequence: */ - bitvec_set_bit(bv, 0); - /* stop bit - end of Repeated E-UTRAN PCID to TA mapping sequence: */ - bitvec_set_bit(bv, 0); - - /* Priority and E-UTRAN Parameters Description ends here */ - /* No 3G CSG Description */ - bitvec_set_bit(bv, 0); - /* No E-UTRAN CSG Description */ - bitvec_set_bit(bv, 0); - /* No Additions in Rel-9: */ - bitvec_set_bit(bv, L); -} - -static inline int f0_helper(int *sc, size_t length, uint8_t *chan_list) -{ - int w[RANGE_ENC_MAX_ARFCNS] = { 0 }; - - return range_encode(ARFCN_RANGE_1024, sc, length, w, 0, chan_list); -} - -/* Estimate how many bits it'll take to append single FDD UARFCN */ -static inline int append_utran_fdd_length(uint16_t u, int *sc, size_t sc_len, size_t length) -{ - uint8_t chan_list[16] = { 0 }; - int tmp[sc_len], f0; - - memcpy(tmp, sc, sizeof(tmp)); - - f0 = f0_helper(tmp, length, chan_list); - if (f0 < 0) - return f0; - - return 21 + range1024_p(length); -} - -/* Append single FDD UARFCN */ -static inline int append_utran_fdd(struct bitvec *bv, uint16_t u, int *sc, size_t length) -{ - uint8_t chan_list[16] = { 0 }; - int f0 = f0_helper(sc, length, chan_list); - - if (f0 < 0) - return f0; - - /* Repeated UTRAN FDD Neighbour Cells */ - bitvec_set_bit(bv, 1); - - /* FDD-ARFCN */ - bitvec_set_bit(bv, 0); - bitvec_set_uint(bv, u, 14); - - /* FDD_Indic0: parameter value '0000000000' is a member of the set? */ - bitvec_set_bit(bv, f0); - /* NR_OF_FDD_CELLS */ - bitvec_set_uint(bv, length, 5); - - f0 = bv->cur_bit; - bitvec_add_range1024(bv, (struct gsm48_range_1024 *)chan_list); - bv->cur_bit = f0 + range1024_p(length); - - return 21 + range1024_p(length); -} - -/* Append multiple FDD UARFCNs */ -static inline int append_uarfcns(struct bitvec *bv, struct gsm_bts *bts, uint8_t budget) -{ - const uint16_t *u = bts->si_common.data.uarfcn_list, *sc = bts->si_common.data.scramble_list; - int i, j, k, rc, st = 0, a[bts->si_common.uarfcn_length]; - uint16_t cu = u[bts->u_offset]; /* caller ensures that length is positive */ - uint8_t rem = budget - 7, offset_diff; /* account for constant bits right away */ - - OSMO_ASSERT(budget <= SI2Q_MAX_LEN); - - if (budget <= 7) - return -ENOMEM; - - /* 3G Neighbour Cell Description */ - bitvec_set_bit(bv, 1); - /* No Index_Start_3G */ - bitvec_set_bit(bv, 0); - /* No Absolute_Index_Start_EMR */ - bitvec_set_bit(bv, 0); - - /* UTRAN FDD Description */ - bitvec_set_bit(bv, 1); - /* No Bandwidth_FDD */ - bitvec_set_bit(bv, 0); - - for (i = bts->u_offset; i < bts->si_common.uarfcn_length; i++) { - offset_diff = 0; - for (j = st, k = 0; j < i; j++) { - a[k++] = sc[j]; /* copy corresponding SCs */ - offset_diff++; /* compute proper offset step */ - } - if (u[i] != cu) { /* we've reached new UARFCN */ - rc = append_utran_fdd_length(cu, a, bts->si_common.uarfcn_length, k); - if (rc < 0) { /* estimate bit length requirements */ - return rc; - } - - if (rem - rc <= 0) - break; /* we have ran out of budget in current SI2q */ - else { - rem -= append_utran_fdd(bv, cu, a, k); - bts->u_offset += offset_diff; - } - cu = u[i]; - st = i; /* update start position */ - } - } - - if (rem > 22) { /* add last UARFCN not covered by previous cycle if it could possibly fit into budget */ - offset_diff = 0; - for (i = st, k = 0; i < bts->si_common.uarfcn_length; i++) { - a[k++] = sc[i]; - offset_diff++; - } - rc = append_utran_fdd_length(cu, a, bts->si_common.uarfcn_length, k); - if (rc < 0) { - return rc; - } - - if (rem - rc >= 0) { - rem -= append_utran_fdd(bv, cu, a, k); - bts->u_offset += offset_diff; - } - } - - /* stop bit - end of Repeated UTRAN FDD Neighbour Cells */ - bitvec_set_bit(bv, 0); - - /* UTRAN TDD Description */ - bitvec_set_bit(bv, 0); - - return 0; -} - -/* generate SI2quater rest octets: 3GPP TS 44.018 § 10.5.2.33b */ -int rest_octets_si2quater(uint8_t *data, struct gsm_bts *bts) -{ - int rc; - struct bitvec bv; - - if (bts->si2q_count < bts->si2q_index) - return -EINVAL; - - bv.data = data; - bv.data_len = 20; - bitvec_zero(&bv); - - /* BA_IND */ - bitvec_set_bit(&bv, 1); - /* 3G_BA_IND */ - bitvec_set_bit(&bv, 1); - /* MP_CHANGE_MARK */ - bitvec_set_bit(&bv, 0); - - /* SI2quater_INDEX */ - bitvec_set_uint(&bv, bts->si2q_index, 4); - /* SI2quater_COUNT */ - bitvec_set_uint(&bv, bts->si2q_count, 4); - - /* No Measurement_Parameters Description */ - bitvec_set_bit(&bv, 0); - /* No GPRS_Real Time Difference Description */ - bitvec_set_bit(&bv, 0); - /* No GPRS_BSIC Description */ - bitvec_set_bit(&bv, 0); - /* No GPRS_REPORT PRIORITY Description */ - bitvec_set_bit(&bv, 0); - /* No GPRS_MEASUREMENT_Parameters Description */ - bitvec_set_bit(&bv, 0); - /* No NC Measurement Parameters */ - bitvec_set_bit(&bv, 0); - /* No extension (length) */ - bitvec_set_bit(&bv, 0); - - rc = SI2Q_MAX_LEN - (bv.cur_bit + 3); - if (rc > 0 && bts->si_common.uarfcn_length - bts->u_offset > 0) { - rc = append_uarfcns(&bv, bts, rc); - if (rc < 0) { - LOGP(DRR, LOGL_ERROR, "SI2quater [%u/%u]: failed to append %zu UARFCNs due to range encoding " - "failure: %s\n", - bts->si2q_index, bts->si2q_count, bts->si_common.uarfcn_length, strerror(-rc)); - return rc; - } - } else /* No 3G Neighbour Cell Description */ - bitvec_set_bit(&bv, 0); - - /* No 3G Measurement Parameters Description */ - bitvec_set_bit(&bv, 0); - /* No GPRS_3G_MEASUREMENT Parameters Descr. */ - bitvec_set_bit(&bv, 0); - - rc = SI2Q_MAX_LEN - bv.cur_bit; - if (rc > 0 && si2q_earfcn_count(&bts->si_common.si2quater_neigh_list) - bts->e_offset > 0) - append_earfcn(&bv, bts, rc); - else /* No Additions in Rel-5: */ - bitvec_set_bit(&bv, L); - - bitvec_spare_padding(&bv, (bv.data_len * 8) - 1); - return bv.data_len; -} - -/* Append selection parameters to bitvec */ -static void append_selection_params(struct bitvec *bv, - const struct gsm48_si_selection_params *sp) -{ - if (sp->present) { - bitvec_set_bit(bv, H); - bitvec_set_bit(bv, sp->cbq); - bitvec_set_uint(bv, sp->cell_resel_off, 6); - bitvec_set_uint(bv, sp->temp_offs, 3); - bitvec_set_uint(bv, sp->penalty_time, 5); - } else - bitvec_set_bit(bv, L); -} - -/* Append power offset to bitvec */ -static void append_power_offset(struct bitvec *bv, - const struct gsm48_si_power_offset *po) -{ - if (po->present) { - bitvec_set_bit(bv, H); - bitvec_set_uint(bv, po->power_offset, 2); - } else - bitvec_set_bit(bv, L); -} - -/* Append GPRS indicator to bitvec */ -static void append_gprs_ind(struct bitvec *bv, - const struct gsm48_si3_gprs_ind *gi) -{ - if (gi->present) { - bitvec_set_bit(bv, H); - bitvec_set_uint(bv, gi->ra_colour, 3); - /* 0 == SI13 in BCCH Norm, 1 == SI13 sent on BCCH Ext */ - bitvec_set_bit(bv, gi->si13_position); - } else - bitvec_set_bit(bv, L); -} - - -/* Generate SI3 Rest Octests (Chapter 10.5.2.34 / Table 10.4.72) */ -int rest_octets_si3(uint8_t *data, const struct gsm48_si_ro_info *si3) -{ - struct bitvec bv; - - memset(&bv, 0, sizeof(bv)); - bv.data = data; - bv.data_len = 4; - - /* Optional Selection Parameters */ - append_selection_params(&bv, &si3->selection_params); - - /* Optional Power Offset */ - append_power_offset(&bv, &si3->power_offset); - - /* Do we have a SI2ter on the BCCH? */ - if (si3->si2ter_indicator) - bitvec_set_bit(&bv, H); - else - bitvec_set_bit(&bv, L); - - /* Early Classmark Sending Control */ - if (si3->early_cm_ctrl) - bitvec_set_bit(&bv, H); - else - bitvec_set_bit(&bv, L); - - /* Do we have a SI Type 9 on the BCCH? */ - if (si3->scheduling.present) { - bitvec_set_bit(&bv, H); - bitvec_set_uint(&bv, si3->scheduling.where, 3); - } else - bitvec_set_bit(&bv, L); - - /* GPRS Indicator */ - append_gprs_ind(&bv, &si3->gprs_ind); - - /* 3G Early Classmark Sending Restriction controlled by - * early_cm_ctrl above */ - bitvec_set_bit(&bv, H); - - if (si3->si2quater_indicator) { - bitvec_set_bit(&bv, H); /* indicator struct present */ - bitvec_set_uint(&bv, 0, 1); /* message is sent on BCCH Norm */ - } - - bitvec_spare_padding(&bv, (bv.data_len*8)-1); - return bv.data_len; -} - -static int append_lsa_params(struct bitvec *bv, - const struct gsm48_lsa_params *lsa_params) -{ - /* FIXME */ - return -1; -} - -/* Generate SI4 Rest Octets (Chapter 10.5.2.35) */ -int rest_octets_si4(uint8_t *data, const struct gsm48_si_ro_info *si4, int len) -{ - struct bitvec bv; - - memset(&bv, 0, sizeof(bv)); - bv.data = data; - bv.data_len = len; - - /* SI4 Rest Octets O */ - append_selection_params(&bv, &si4->selection_params); - append_power_offset(&bv, &si4->power_offset); - append_gprs_ind(&bv, &si4->gprs_ind); - - if (0 /* FIXME */) { - /* H and SI4 Rest Octets S */ - bitvec_set_bit(&bv, H); - - /* LSA Parameters */ - if (si4->lsa_params.present) { - bitvec_set_bit(&bv, H); - append_lsa_params(&bv, &si4->lsa_params); - } else - bitvec_set_bit(&bv, L); - - /* Cell Identity */ - if (1) { - bitvec_set_bit(&bv, H); - bitvec_set_uint(&bv, si4->cell_id, 16); - } else - bitvec_set_bit(&bv, L); - - /* LSA ID Information */ - if (0) { - bitvec_set_bit(&bv, H); - /* FIXME */ - } else - bitvec_set_bit(&bv, L); - } else { - /* L and break indicator */ - bitvec_set_bit(&bv, L); - bitvec_set_bit(&bv, si4->break_ind ? H : L); - } - - return bv.data_len; -} - - -/* GSM 04.18 ETSI TS 101 503 V8.27.0 (2006-05) - - ::= -{L | H } -{L | H } -{ < DTM_support : bit == L > I < DTM_support : bit == H > -< RAC : bit (8) > -< MAX_LAPDm : bit (3) > } -< Band indicator > -{ L | H < GPRS_MS_TXPWR_MAX_CCH : bit (5) > } -; -*/ -int rest_octets_si6(uint8_t *data, bool is1800_net) -{ - struct bitvec bv; - - memset(&bv, 0, sizeof(bv)); - bv.data = data; - bv.data_len = 1; - - /* no PCH/NCH info */ - bitvec_set_bit(&bv, L); - /* no VBS/VGCS options */ - bitvec_set_bit(&bv, L); - /* no DTM_support */ - bitvec_set_bit(&bv, L); - /* band indicator */ - if (is1800_net) - bitvec_set_bit(&bv, L); - else - bitvec_set_bit(&bv, H); - /* no GPRS_MS_TXPWR_MAX_CCH */ - bitvec_set_bit(&bv, L); - - bitvec_spare_padding(&bv, (bv.data_len * 8) - 1); - return bv.data_len; -} - -/* GPRS Mobile Allocation as per TS 04.60 Chapter 12.10a: - < GPRS Mobile Allocation IE > ::= - < HSN : bit (6) > - { 0 | 1 < RFL number list : < RFL number list struct > > } - { 0 < MA_LENGTH : bit (6) > - < MA_BITMAP: bit (val(MA_LENGTH) + 1) > - | 1 { 0 | 1 > } } ; - - < RFL number list struct > :: = - < RFL_NUMBER : bit (4) > - { 0 | 1 < RFL number list struct > } ; - < ARFCN index list struct > ::= - < ARFCN_INDEX : bit(6) > - { 0 | 1 < ARFCN index list struct > } ; - */ -static int append_gprs_mobile_alloc(struct bitvec *bv) -{ - /* Hopping Sequence Number */ - bitvec_set_uint(bv, 0, 6); - - if (0) { - /* We want to use a RFL number list */ - bitvec_set_bit(bv, 1); - /* FIXME: RFL number list */ - } else - bitvec_set_bit(bv, 0); - - if (0) { - /* We want to use a MA_BITMAP */ - bitvec_set_bit(bv, 0); - /* FIXME: MA_LENGTH, MA_BITMAP, ... */ - } else { - bitvec_set_bit(bv, 1); - if (0) { - /* We want to provide an ARFCN index list */ - bitvec_set_bit(bv, 1); - /* FIXME */ - } else - bitvec_set_bit(bv, 0); - } - return 0; -} - -static int encode_t3192(unsigned int t3192) -{ - /* See also 3GPP TS 44.060 - Table 12.24.2: GPRS Cell Options information element details */ - if (t3192 == 0) - return 3; - else if (t3192 <= 80) - return 4; - else if (t3192 <= 120) - return 5; - else if (t3192 <= 160) - return 6; - else if (t3192 <= 200) - return 7; - else if (t3192 <= 500) - return 0; - else if (t3192 <= 1000) - return 1; - else if (t3192 <= 1500) - return 2; - else - return -EINVAL; -} - -static int encode_drx_timer(unsigned int drx) -{ - if (drx == 0) - return 0; - else if (drx == 1) - return 1; - else if (drx == 2) - return 2; - else if (drx <= 4) - return 3; - else if (drx <= 8) - return 4; - else if (drx <= 16) - return 5; - else if (drx <= 32) - return 6; - else if (drx <= 64) - return 7; - else - return -EINVAL; -} - -/* GPRS Cell Options as per TS 04.60 Chapter 12.24 - < GPRS Cell Options IE > ::= - < NMO : bit(2) > - < T3168 : bit(3) > - < T3192 : bit(3) > - < DRX_TIMER_MAX: bit(3) > - < ACCESS_BURST_TYPE: bit > - < CONTROL_ACK_TYPE : bit > - < BS_CV_MAX: bit(4) > - { 0 | 1 < PAN_DEC : bit(3) > - < PAN_INC : bit(3) > - < PAN_MAX : bit(3) > - { 0 | 1 < Extension Length : bit(6) > - < bit (val(Extension Length) + 1 - & { < Extension Information > ! { bit ** = } } ; - < 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); - - /* See also 3GPP TS 44.060 - Table 12.24.2: GPRS Cell Options information element details */ - bitvec_set_uint(bv, gco->t3168 / 500 - 1, 3); - - bitvec_set_uint(bv, t3192, 3); - bitvec_set_uint(bv, drx_timer_max, 3); - /* ACCESS_BURST_TYPE: Hard-code 8bit */ - bitvec_set_bit(bv, 0); - /* CONTROL_ACK_TYPE: */ - bitvec_set_bit(bv, gco->ctrl_ack_type_use_block); - bitvec_set_uint(bv, gco->bs_cv_max, 4); - - if (0) { - /* hard-code no PAN_{DEC,INC,MAX} */ - bitvec_set_bit(bv, 0); - } else { - /* copied from ip.access BSC protocol trace */ - bitvec_set_bit(bv, 1); - bitvec_set_uint(bv, 1, 3); /* DEC */ - bitvec_set_uint(bv, 1, 3); /* INC */ - bitvec_set_uint(bv, 15, 3); /* MAX */ - } - - if (!gco->ext_info_present) { - /* no extension information */ - bitvec_set_bit(bv, 0); - } else { - /* extension information */ - bitvec_set_bit(bv, 1); - if (!gco->ext_info.egprs_supported) { - /* 6bit length of extension */ - bitvec_set_uint(bv, (1 + 3)-1, 6); - /* EGPRS supported in the cell */ - bitvec_set_bit(bv, 0); - } else { - /* 6bit length of extension */ - bitvec_set_uint(bv, (1 + 5 + 3)-1, 6); - /* EGPRS supported in the cell */ - bitvec_set_bit(bv, 1); - - /* 1bit EGPRS PACKET CHANNEL REQUEST */ - if (gco->supports_egprs_11bit_rach == 0) { - bitvec_set_bit(bv, - gco->ext_info.use_egprs_p_ch_req); - } else { - bitvec_set_bit(bv, 0); - } - - /* 4bit BEP PERIOD */ - bitvec_set_uint(bv, gco->ext_info.bep_period, 4); - } - bitvec_set_bit(bv, gco->ext_info.pfc_supported); - bitvec_set_bit(bv, gco->ext_info.dtm_supported); - bitvec_set_bit(bv, gco->ext_info.bss_paging_coordination); - } - - return 0; -} - -static void append_gprs_pwr_ctrl_pars(struct bitvec *bv, - const struct gprs_power_ctrl_pars *pcp) -{ - bitvec_set_uint(bv, pcp->alpha, 4); - bitvec_set_uint(bv, pcp->t_avg_w, 5); - bitvec_set_uint(bv, pcp->t_avg_t, 5); - bitvec_set_uint(bv, pcp->pc_meas_chan, 1); - bitvec_set_uint(bv, pcp->n_avg_i, 4); -} - -/* Generate SI13 Rest Octests (04.08 Chapter 10.5.2.37b) */ -int rest_octets_si13(uint8_t *data, const struct gsm48_si13_info *si13) -{ - struct bitvec bv; - - memset(&bv, 0, sizeof(bv)); - bv.data = data; - bv.data_len = 20; - - if (0) { - /* No rest octets */ - bitvec_set_bit(&bv, L); - } else { - bitvec_set_bit(&bv, H); - bitvec_set_uint(&bv, si13->bcch_change_mark, 3); - bitvec_set_uint(&bv, si13->si_change_field, 4); - if (1) { - bitvec_set_bit(&bv, 0); - } else { - bitvec_set_bit(&bv, 1); - bitvec_set_uint(&bv, si13->bcch_change_mark, 2); - append_gprs_mobile_alloc(&bv); - } - if (!si13->pbcch_present) { - /* PBCCH not present in cell */ - bitvec_set_bit(&bv, 0); - bitvec_set_uint(&bv, si13->no_pbcch.rac, 8); - bitvec_set_bit(&bv, si13->no_pbcch.spgc_ccch_sup); - bitvec_set_uint(&bv, si13->no_pbcch.prio_acc_thr, 3); - bitvec_set_uint(&bv, si13->no_pbcch.net_ctrl_ord, 2); - append_gprs_cell_opt(&bv, &si13->cell_opts); - append_gprs_pwr_ctrl_pars(&bv, &si13->pwr_ctrl_pars); - } else { - /* PBCCH present in cell */ - bitvec_set_bit(&bv, 1); - bitvec_set_uint(&bv, si13->pbcch.psi1_rep_per, 4); - /* PBCCH Descripiton */ - bitvec_set_uint(&bv, si13->pbcch.pb, 4); - bitvec_set_uint(&bv, si13->pbcch.tsc, 3); - bitvec_set_uint(&bv, si13->pbcch.tn, 3); - switch (si13->pbcch.carrier_type) { - case PBCCH_BCCH: - bitvec_set_bit(&bv, 0); - bitvec_set_bit(&bv, 0); - break; - case PBCCH_ARFCN: - bitvec_set_bit(&bv, 0); - bitvec_set_bit(&bv, 1); - bitvec_set_uint(&bv, si13->pbcch.arfcn, 10); - break; - case PBCCH_MAIO: - bitvec_set_bit(&bv, 1); - bitvec_set_uint(&bv, si13->pbcch.maio, 6); - break; - } - } - /* 3GPP TS 44.018 Release 6 / 10.5.2.37b */ - bitvec_set_bit(&bv, H); /* added Release 99 */ - /* claim our SGSN is compatible with Release 99, as EDGE and EGPRS - * was only added in this Release */ - bitvec_set_bit(&bv, 1); - } - bitvec_spare_padding(&bv, (bv.data_len*8)-1); - return bv.data_len; -} diff --git a/openbsc/src/libbsc/system_information.c b/openbsc/src/libbsc/system_information.c deleted file mode 100644 index dcabbbdd..00000000 --- a/openbsc/src/libbsc/system_information.c +++ /dev/null @@ -1,1169 +0,0 @@ -/* GSM 04.08 System Information (SI) encoding and decoding - * 3GPP TS 04.08 version 7.21.0 Release 1998 / ETSI TS 100 940 V7.21.0 */ - -/* (C) 2008-2010 by Harald Welte - * (C) 2012 Holger Hans Peter Freyther - * - * All Rights Reserved - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation; either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - * - */ - -#include -#include -#include -#include -#include - -#include -#include -#include - -#include -#include -#include -#include -#include -#include - -/* - * DCS1800 and PCS1900 have overlapping ARFCNs. We would need to set the - * ARFCN_PCS flag on the 1900 ARFCNs but this would increase cell_alloc - * and other arrays to make sure (ARFCN_PCS + 1024)/8 ARFCNs fit into the - * array. DCS1800 and PCS1900 can not be used at the same time so conserve - * memory and do the below. - */ -static int band_compatible(const struct gsm_bts *bts, int arfcn) -{ - enum gsm_band band = gsm_arfcn2band(arfcn); - - /* normal case */ - if (band == bts->band) - return 1; - /* deal with ARFCN_PCS not set */ - if (band == GSM_BAND_1800 && bts->band == GSM_BAND_1900) - return 1; - - return 0; -} - -static int is_dcs_net(const struct gsm_bts *bts) -{ - if (bts->band == GSM_BAND_850) - return 0; - if (bts->band == GSM_BAND_1900) - return 0; - return 1; -} - -/* Return p(n) for given NR_OF_TDD_CELLS - see Table 9.1.54.1a, 3GPP TS 44.018 */ -unsigned range1024_p(unsigned n) -{ - switch (n) { - case 0: return 0; - case 1: return 10; - case 2: return 19; - case 3: return 28; - case 4: return 36; - case 5: return 44; - case 6: return 52; - case 7: return 60; - case 8: return 67; - case 9: return 74; - case 10: return 81; - case 11: return 88; - case 12: return 95; - case 13: return 102; - case 14: return 109; - case 15: return 116; - case 16: return 122; - default: return 0; - } -} - -/* Return q(m) for given NR_OF_TDD_CELLS - see Table 9.1.54.1b, 3GPP TS 44.018 */ -unsigned range512_q(unsigned m) -{ - switch (m) { - case 0: return 0; - case 1: return 9; - case 2: return 17; - case 3: return 25; - case 4: return 32; - case 5: return 39; - case 6: return 46; - case 7: return 53; - case 8: return 59; - case 9: return 65; - case 10: return 71; - case 11: return 77; - case 12: return 83; - case 13: return 89; - case 14: return 95; - case 15: return 101; - case 16: return 106; - case 17: return 111; - case 18: return 116; - case 19: return 121; - case 20: return 126; - default: return 0; - } -} - -size_t si2q_earfcn_count(const struct osmo_earfcn_si2q *e) -{ - unsigned i, ret = 0; - - if (!e) - return 0; - - for (i = 0; i < e->length; i++) - if (e->arfcn[i] != OSMO_EARFCN_INVALID) - ret++; - - return ret; -} - -/* generate SI2quater messages, return rest octets length of last generated message or negative error code */ -static int make_si2quaters(struct gsm_bts *bts, bool counting) -{ - int rc; - bool memory_exceeded = true; - struct gsm48_system_information_type_2quater *si2q; - - for (bts->si2q_index = 0; bts->si2q_index < SI2Q_MAX_NUM; bts->si2q_index++) { - si2q = GSM_BTS_SI2Q(bts, bts->si2q_index); - if (counting) { /* that's legitimate if we're called for counting purpose: */ - if (bts->si2q_count < bts->si2q_index) - bts->si2q_count = bts->si2q_index; - } else { - memset(si2q, GSM_MACBLOCK_PADDING, GSM_MACBLOCK_LEN); - - si2q->header.l2_plen = GSM48_LEN2PLEN(22); - si2q->header.rr_protocol_discriminator = GSM48_PDISC_RR; - si2q->header.skip_indicator = 0; - si2q->header.system_information = GSM48_MT_RR_SYSINFO_2quater; - } - - rc = rest_octets_si2quater(si2q->rest_octets, bts); - if (rc < 0) - return rc; - - if (bts->u_offset >= bts->si_common.uarfcn_length && - bts->e_offset >= si2q_earfcn_count(&bts->si_common.si2quater_neigh_list)) { - memory_exceeded = false; - break; - } - } - - if (memory_exceeded) - return -ENOMEM; - - return rc; -} - -/* we generate SI2q rest octets twice to get proper estimation but it's one time cost anyway */ -uint8_t si2q_num(struct gsm_bts *bts) -{ - int rc = make_si2quaters(bts, true); - uint8_t num = bts->si2q_index + 1; /* number of SI2quater messages */ - - /* N. B: si2q_num() should NEVER be called during actualSI2q rest octets generation - we're not re-entrant because of the following code: */ - bts->u_offset = 0; - bts->e_offset = 0; - - if (rc < 0) - return 0xFF; /* return impossible index as an indicator of error in generating SI2quater */ - - return num; -} - -/* 3GPP TS 44.018, Table 9.1.54.1 - prepend diversity bit to scrambling code */ -static inline uint16_t encode_fdd(uint16_t scramble, bool diversity) -{ - if (diversity) - return scramble | (1 << 9); - return scramble; -} - -int bts_earfcn_add(struct gsm_bts *bts, uint16_t earfcn, uint8_t thresh_hi, uint8_t thresh_lo, uint8_t prio, - uint8_t qrx, uint8_t meas_bw) -{ - struct osmo_earfcn_si2q *e = &bts->si_common.si2quater_neigh_list; - int r = osmo_earfcn_add(e, earfcn, (meas_bw < EARFCN_MEAS_BW_INVALID) ? meas_bw : OSMO_EARFCN_MEAS_INVALID); - - if (r < 0) - return r; - - if (e->thresh_hi && thresh_hi != e->thresh_hi) - r = 1; - - e->thresh_hi = thresh_hi; - - if (thresh_lo != EARFCN_THRESH_LOW_INVALID) { - if (e->thresh_lo_valid && e->thresh_lo != thresh_lo) - r = EARFCN_THRESH_LOW_INVALID; - e->thresh_lo = thresh_lo; - e->thresh_lo_valid = true; - } - - if (qrx != EARFCN_QRXLV_INVALID) { - if (e->qrxlm_valid && e->qrxlm != qrx) - r = EARFCN_QRXLV_INVALID + 1; - e->qrxlm = qrx; - e->qrxlm_valid = true; - } - - if (prio != EARFCN_PRIO_INVALID) { - if (e->prio_valid && e->prio != prio) - r = EARFCN_PRIO_INVALID; - e->prio = prio; - e->prio_valid = true; - } - - return r; -} - -int bts_uarfcn_del(struct gsm_bts *bts, uint16_t arfcn, uint16_t scramble) -{ - uint16_t sc0 = encode_fdd(scramble, false), sc1 = encode_fdd(scramble, true), - *ual = bts->si_common.data.uarfcn_list, - *scl = bts->si_common.data.scramble_list; - size_t len = bts->si_common.uarfcn_length, i; - for (i = 0; i < len; i++) { - if (arfcn == ual[i] && (sc0 == scl[i] || sc1 == scl[i])) { - /* we rely on the assumption that (uarfcn, scramble) - tuple is unique in the lists */ - if (i != len - 1) { /* move the tail if necessary */ - memmove(ual + i, ual + i + 1, 2 * (len - i + 1)); - memmove(scl + i, scl + i + 1, 2 * (len - i + 1)); - } - break; - } - } - - if (i == len) - return -EINVAL; - - bts->si_common.uarfcn_length--; - return 0; -} - -int bts_uarfcn_add(struct gsm_bts *bts, uint16_t arfcn, uint16_t scramble, bool diversity) -{ - size_t len = bts->si_common.uarfcn_length, i, k = 0; - uint16_t scr, chk, - *ual = bts->si_common.data.uarfcn_list, - *scl = bts->si_common.data.scramble_list, - scramble1 = encode_fdd(scramble, true), - scramble0 = encode_fdd(scramble, false); - - scr = diversity ? scramble1 : scramble0; - chk = diversity ? scramble0 : scramble1; - - if (len == MAX_EARFCN_LIST) - return -ENOMEM; - - for (i = 0; i < len; i++) /* find the position of arfcn if any */ - if (arfcn == ual[i]) - break; - - for (k = 0; i < len; i++) { - if (arfcn == ual[i] && (scr == scl[i] || chk == scl[i])) - return -EADDRINUSE; - if (scr > scl[i]) - k = i + 1; - } - /* we keep lists sorted by scramble code: - insert into appropriate position and move the tail */ - if (len - k) { - memmove(ual + k + 1, ual + k, (len - k) * 2); - memmove(scl + k + 1, scl + k, (len - k) * 2); - } - - ual[k] = arfcn; - scl[k] = scr; - bts->si_common.uarfcn_length++; - - if (si2q_num(bts) <= SI2Q_MAX_NUM) { - bts->si2q_count = si2q_num(bts) - 1; - return 0; - } - - bts_uarfcn_del(bts, arfcn, scramble); - return -ENOSPC; -} - -static inline int use_arfcn(const struct gsm_bts *bts, const bool bis, const bool ter, - const bool pgsm, const int arfcn) -{ - if (bts->force_combined_si) - return !bis && !ter; - if (!bis && !ter && band_compatible(bts, arfcn)) - return 1; - /* Correct but somehow broken with either the nanoBTS or the iPhone5 */ - if (bis && pgsm && band_compatible(bts, arfcn) && (arfcn < 1 || arfcn > 124)) - return 1; - if (ter && !band_compatible(bts, arfcn)) - return 1; - return 0; -} - -/* Frequency Lists as per TS 04.08 10.5.2.13 */ - -/* 10.5.2.13.2: Bit map 0 format */ -static int freq_list_bm0_set_arfcn(uint8_t *chan_list, unsigned int arfcn) -{ - unsigned int byte, bit; - - if (arfcn > 124 || arfcn < 1) { - LOGP(DRR, LOGL_ERROR, "Bitmap 0 only supports ARFCN 1...124\n"); - return -EINVAL; - } - - /* the bitmask is from 1..124, not from 0..123 */ - arfcn--; - - byte = arfcn / 8; - bit = arfcn % 8; - - chan_list[GSM48_CELL_CHAN_DESC_SIZE-1-byte] |= (1 << bit); - - return 0; -} - -/* 10.5.2.13.7: Variable bit map format */ -static int freq_list_bmrel_set_arfcn(uint8_t *chan_list, unsigned int arfcn) -{ - unsigned int byte, bit; - unsigned int min_arfcn; - unsigned int bitno; - - min_arfcn = (chan_list[0] & 1) << 9; - min_arfcn |= chan_list[1] << 1; - min_arfcn |= (chan_list[2] >> 7) & 1; - - /* The lower end of our bitmaks is always implicitly included */ - if (arfcn == min_arfcn) - return 0; - - if (((arfcn - min_arfcn) & 1023) > 111) { - LOGP(DRR, LOGL_ERROR, "arfcn(%u) > min(%u) + 111\n", arfcn, min_arfcn); - return -EINVAL; - } - - bitno = (arfcn - min_arfcn) & 1023; - byte = bitno / 8; - bit = bitno % 8; - - chan_list[2 + byte] |= 1 << (7 - bit); - - return 0; -} - -/* generate a variable bitmap */ -static inline int enc_freq_lst_var_bitmap(uint8_t *chan_list, - struct bitvec *bv, const struct gsm_bts *bts, - bool bis, bool ter, int min, bool pgsm) -{ - int i; - - /* set it to 'Variable bitmap format' */ - chan_list[0] = 0x8e; - - chan_list[0] |= (min >> 9) & 1; - chan_list[1] = (min >> 1); - chan_list[2] = (min & 1) << 7; - - for (i = 0; i < bv->data_len*8; i++) { - /* see notes in bitvec2freq_list */ - if (bitvec_get_bit_pos(bv, i) - && ((!bis && !ter && band_compatible(bts,i)) - || (bis && pgsm && band_compatible(bts,i) && (i < 1 || i > 124)) - || (ter && !band_compatible(bts, i)))) { - int rc = freq_list_bmrel_set_arfcn(chan_list, i); - if (rc < 0) - return rc; - } - } - - return 0; -} - -int range_encode(enum gsm48_range r, int *arfcns, int arfcns_used, int *w, - int f0, uint8_t *chan_list) -{ - /* - * Manipulate the ARFCN list according to the rules in J4 depending - * on the selected range. - */ - int rc, f0_included; - - range_enc_filter_arfcns(arfcns, arfcns_used, f0, &f0_included); - - rc = range_enc_arfcns(r, arfcns, arfcns_used, w, 0); - if (rc < 0) - return rc; - - /* Select the range and the amount of bits needed */ - switch (r) { - case ARFCN_RANGE_128: - return range_enc_range128(chan_list, f0, w); - case ARFCN_RANGE_256: - return range_enc_range256(chan_list, f0, w); - case ARFCN_RANGE_512: - return range_enc_range512(chan_list, f0, w); - case ARFCN_RANGE_1024: - return range_enc_range1024(chan_list, f0, f0_included, w); - default: - return -ERANGE; - }; - - return f0_included; -} - -/* generate a frequency list with the range 512 format */ -static inline int enc_freq_lst_range(uint8_t *chan_list, - struct bitvec *bv, const struct gsm_bts *bts, - bool bis, bool ter, bool pgsm) -{ - int arfcns[RANGE_ENC_MAX_ARFCNS]; - int w[RANGE_ENC_MAX_ARFCNS]; - int arfcns_used = 0; - int i, range, f0; - - /* - * Select ARFCNs according to the rules in bitvec2freq_list - */ - for (i = 0; i < bv->data_len * 8; ++i) { - /* More ARFCNs than the maximum */ - if (arfcns_used > ARRAY_SIZE(arfcns)) - return -1; - /* Check if we can select it? */ - if (bitvec_get_bit_pos(bv, i) && use_arfcn(bts, bis, ter, pgsm, i)) - arfcns[arfcns_used++] = i; - } - - /* - * Check if the given list of ARFCNs can be encoded. - */ - range = range_enc_determine_range(arfcns, arfcns_used, &f0); - if (range == ARFCN_RANGE_INVALID) - return -2; - - memset(w, 0, sizeof(w)); - return range_encode(range, arfcns, arfcns_used, w, f0, chan_list); -} - -/* generate a cell channel list as per Section 10.5.2.1b of 04.08 */ -static int bitvec2freq_list(uint8_t *chan_list, struct bitvec *bv, - const struct gsm_bts *bts, bool bis, bool ter) -{ - int i, rc, min = -1, max = -1, arfcns = 0; - bool pgsm = false; - memset(chan_list, 0, 16); - - if (bts->band == GSM_BAND_900 - && bts->c0->arfcn >= 1 && bts->c0->arfcn <= 124) - pgsm = true; - /* P-GSM-only handsets only support 'bit map 0 format' */ - if (!bis && !ter && pgsm) { - chan_list[0] = 0; - - for (i = 0; i < bv->data_len*8; i++) { - if (i >= 1 && i <= 124 - && bitvec_get_bit_pos(bv, i)) { - rc = freq_list_bm0_set_arfcn(chan_list, i); - if (rc < 0) - return rc; - } - } - return 0; - } - - for (i = 0; i < bv->data_len*8; i++) { - /* in case of SI2 or SI5 allow all neighbours in same band - * in case of SI*bis, allow neighbours in same band ouside pgsm - * in case of SI*ter, allow neighbours in different bands - */ - if (!bitvec_get_bit_pos(bv, i)) - continue; - if (!use_arfcn(bts, bis, ter, pgsm, i)) - continue; - /* count the arfcns we want to carry */ - arfcns += 1; - - /* 955..1023 < 0..885 */ - if (min < 0) - min = i; - if (i >= 955 && min < 955) - min = i; - if (i >= 955 && min >= 955 && i < min) - min = i; - if (i < 955 && min < 955 && i < min) - min = i; - if (max < 0) - max = i; - if (i < 955 && max >= 955) - max = i; - if (i >= 955 && max >= 955 && i > max) - max = i; - if (i < 955 && max < 955 && i > max) - max = i; - } - - if (max == -1) { - /* Empty set, use 'bit map 0 format' */ - chan_list[0] = 0; - return 0; - } - - /* Now find the best encoding */ - if (((max - min) & 1023) <= 111) - return enc_freq_lst_var_bitmap(chan_list, bv, bts, bis, - ter, min, pgsm); - - /* Attempt to do the range encoding */ - rc = enc_freq_lst_range(chan_list, bv, bts, bis, ter, pgsm); - if (rc >= 0) - return 0; - - LOGP(DRR, LOGL_ERROR, "min_arfcn=%u, max_arfcn=%u, arfcns=%d " - "can not generate ARFCN list", min, max, arfcns); - return -EINVAL; -} - -/* generate a cell channel list as per Section 10.5.2.1b of 04.08 */ -/* static*/ int generate_cell_chan_list(uint8_t *chan_list, struct gsm_bts *bts) -{ - struct gsm_bts_trx *trx; - struct bitvec *bv = &bts->si_common.cell_alloc; - - /* Zero-initialize the bit-vector */ - memset(bv->data, 0, bv->data_len); - - /* first we generate a bitvec of all TRX ARFCN's in our BTS */ - llist_for_each_entry(trx, &bts->trx_list, list) { - unsigned int i, j; - /* Always add the TRX's ARFCN */ - bitvec_set_bit_pos(bv, trx->arfcn, 1); - for (i = 0; i < ARRAY_SIZE(trx->ts); i++) { - struct gsm_bts_trx_ts *ts = &trx->ts[i]; - /* Add any ARFCNs present in hopping channels */ - for (j = 0; j < 1024; j++) { - if (bitvec_get_bit_pos(&ts->hopping.arfcns, j)) - bitvec_set_bit_pos(bv, j, 1); - } - } - } - - /* then we generate a GSM 04.08 frequency list from the bitvec */ - return bitvec2freq_list(chan_list, bv, bts, false, false); -} - -/* generate a cell channel list as per Section 10.5.2.1b of 04.08 */ -static int generate_bcch_chan_list(uint8_t *chan_list, struct gsm_bts *bts, - bool si5, bool bis, bool ter) -{ - struct gsm_bts *cur_bts; - struct bitvec *bv; - - if (si5 && bts->neigh_list_manual_mode == NL_MODE_MANUAL_SI5SEP) - bv = &bts->si_common.si5_neigh_list; - else - bv = &bts->si_common.neigh_list; - - /* Generate list of neighbor cells if we are in automatic mode */ - if (bts->neigh_list_manual_mode == NL_MODE_AUTOMATIC) { - /* Zero-initialize the bit-vector */ - memset(bv->data, 0, bv->data_len); - - /* first we generate a bitvec of the BCCH ARFCN's in our BSC */ - llist_for_each_entry(cur_bts, &bts->network->bts_list, list) { - if (cur_bts == bts) - continue; - bitvec_set_bit_pos(bv, cur_bts->c0->arfcn, 1); - } - } - - /* then we generate a GSM 04.08 frequency list from the bitvec */ - return bitvec2freq_list(chan_list, bv, bts, bis, ter); -} - -static int list_arfcn(uint8_t *chan_list, uint8_t mask, char *text) -{ - int n = 0, i; - struct gsm_sysinfo_freq freq[1024]; - - memset(freq, 0, sizeof(freq)); - gsm48_decode_freq_list(freq, chan_list, 16, 0xce, 1); - for (i = 0; i < 1024; i++) { - if (freq[i].mask) { - if (!n) - LOGP(DRR, LOGL_INFO, "%s", text); - LOGPC(DRR, LOGL_INFO, " %d", i); - n++; - } - } - if (n) - LOGPC(DRR, LOGL_INFO, "\n"); - - return n; -} - -static int generate_si1(enum osmo_sysinfo_type t, struct gsm_bts *bts) -{ - int rc; - struct gsm48_system_information_type_1 *si1 = (struct gsm48_system_information_type_1 *) GSM_BTS_SI(bts, t); - - memset(si1, GSM_MACBLOCK_PADDING, GSM_MACBLOCK_LEN); - - si1->header.l2_plen = GSM48_LEN2PLEN(21); - si1->header.rr_protocol_discriminator = GSM48_PDISC_RR; - si1->header.skip_indicator = 0; - si1->header.system_information = GSM48_MT_RR_SYSINFO_1; - - rc = generate_cell_chan_list(si1->cell_channel_description, bts); - if (rc < 0) - return rc; - list_arfcn(si1->cell_channel_description, 0xce, "Serving cell:"); - - si1->rach_control = bts->si_common.rach_control; - - /* - * SI1 Rest Octets (10.5.2.32), contains NCH position and band - * indicator but that is not in the 04.08. - */ - rc = rest_octets_si1(si1->rest_octets, NULL, is_dcs_net(bts)); - - return sizeof(*si1) + rc; -} - -static int generate_si2(enum osmo_sysinfo_type t, struct gsm_bts *bts) -{ - int rc; - struct gsm48_system_information_type_2 *si2 = (struct gsm48_system_information_type_2 *) GSM_BTS_SI(bts, t); - - memset(si2, GSM_MACBLOCK_PADDING, GSM_MACBLOCK_LEN); - - si2->header.l2_plen = GSM48_LEN2PLEN(22); - si2->header.rr_protocol_discriminator = GSM48_PDISC_RR; - si2->header.skip_indicator = 0; - si2->header.system_information = GSM48_MT_RR_SYSINFO_2; - - rc = generate_bcch_chan_list(si2->bcch_frequency_list, bts, false, false, false); - if (rc < 0) - return rc; - list_arfcn(si2->bcch_frequency_list, 0xce, - "SI2 Neighbour cells in same band:"); - - si2->ncc_permitted = bts->si_common.ncc_permitted; - si2->rach_control = bts->si_common.rach_control; - - return sizeof(*si2); -} - -static int generate_si2bis(enum osmo_sysinfo_type t, struct gsm_bts *bts) -{ - int rc; - struct gsm48_system_information_type_2bis *si2b = - (struct gsm48_system_information_type_2bis *) GSM_BTS_SI(bts, t); - int n; - - memset(si2b, GSM_MACBLOCK_PADDING, GSM_MACBLOCK_LEN); - - si2b->header.l2_plen = GSM48_LEN2PLEN(22); - si2b->header.rr_protocol_discriminator = GSM48_PDISC_RR; - si2b->header.skip_indicator = 0; - si2b->header.system_information = GSM48_MT_RR_SYSINFO_2bis; - - rc = generate_bcch_chan_list(si2b->bcch_frequency_list, bts, false, true, false); - if (rc < 0) - return rc; - n = list_arfcn(si2b->bcch_frequency_list, 0xce, - "Neighbour cells in same band, but outside P-GSM:"); - if (n) { - /* indicate in SI2 and SI2bis: there is an extension */ - struct gsm48_system_information_type_2 *si2 = - (struct gsm48_system_information_type_2 *) GSM_BTS_SI(bts, SYSINFO_TYPE_2); - si2->bcch_frequency_list[0] |= 0x20; - si2b->bcch_frequency_list[0] |= 0x20; - } else - bts->si_valid &= ~(1 << SYSINFO_TYPE_2bis); - - si2b->rach_control = bts->si_common.rach_control; - - return sizeof(*si2b); -} - -static int generate_si2ter(enum osmo_sysinfo_type t, struct gsm_bts *bts) -{ - int rc; - struct gsm48_system_information_type_2ter *si2t = - (struct gsm48_system_information_type_2ter *) GSM_BTS_SI(bts, t); - int n; - - memset(si2t, GSM_MACBLOCK_PADDING, GSM_MACBLOCK_LEN); - - si2t->header.l2_plen = GSM48_LEN2PLEN(22); - si2t->header.rr_protocol_discriminator = GSM48_PDISC_RR; - si2t->header.skip_indicator = 0; - si2t->header.system_information = GSM48_MT_RR_SYSINFO_2ter; - - rc = generate_bcch_chan_list(si2t->ext_bcch_frequency_list, bts, false, false, true); - if (rc < 0) - return rc; - n = list_arfcn(si2t->ext_bcch_frequency_list, 0x8e, - "Neighbour cells in different band:"); - if (!n) - bts->si_valid &= ~(1 << SYSINFO_TYPE_2ter); - - return sizeof(*si2t); -} - -/* SI2quater messages are optional - we only generate them when neighbor UARFCNs or EARFCNs are configured */ -static inline bool si2quater_not_needed(struct gsm_bts *bts) -{ - unsigned i = MAX_EARFCN_LIST; - - if (bts->si_common.si2quater_neigh_list.arfcn) - for (i = 0; i < MAX_EARFCN_LIST; i++) - if (bts->si_common.si2quater_neigh_list.arfcn[i] != OSMO_EARFCN_INVALID) - break; - - if (!bts->si_common.uarfcn_length && i == MAX_EARFCN_LIST) { - bts->si_valid &= ~(1 << SYSINFO_TYPE_2quater); /* mark SI2q as invalid if no (E|U)ARFCNs are present */ - return true; - } - - return false; -} - -static int generate_si2quater(enum osmo_sysinfo_type t, struct gsm_bts *bts) -{ - int rc; - struct gsm48_system_information_type_2quater *si2q; - - if (si2quater_not_needed(bts)) /* generate rest_octets for SI2q only when necessary */ - return GSM_MACBLOCK_LEN; - - bts->u_offset = 0; - bts->e_offset = 0; - bts->si2q_index = 0; - bts->si2q_count = si2q_num(bts) - 1; - - rc = make_si2quaters(bts, false); - if (rc < 0) - return rc; - - OSMO_ASSERT(bts->si2q_count == bts->si2q_index); - OSMO_ASSERT(bts->si2q_count <= SI2Q_MAX_NUM); - - return sizeof(*si2q) + rc; -} - -static struct gsm48_si_ro_info si_info = { - .selection_params = { - .present = 0, - }, - .power_offset = { - .present = 0, - }, - .si2ter_indicator = 0, - .early_cm_ctrl = 1, - .scheduling = { - .present = 0, - }, - .gprs_ind = { - .si13_position = 0, - .ra_colour = 0, - .present = 1, - }, - .si2quater_indicator = 0, - .lsa_params = { - .present = 0, - }, - .cell_id = 0, /* FIXME: doesn't the bts have this? */ - .break_ind = 0, -}; - -static int generate_si3(enum osmo_sysinfo_type t, struct gsm_bts *bts) -{ - int rc; - struct gsm48_system_information_type_3 *si3 = (struct gsm48_system_information_type_3 *) GSM_BTS_SI(bts, t); - - memset(si3, GSM_MACBLOCK_PADDING, GSM_MACBLOCK_LEN); - - si3->header.l2_plen = GSM48_LEN2PLEN(18); - si3->header.rr_protocol_discriminator = GSM48_PDISC_RR; - si3->header.skip_indicator = 0; - si3->header.system_information = GSM48_MT_RR_SYSINFO_3; - - si3->cell_identity = htons(bts->cell_identity); - gsm48_generate_lai(&si3->lai, bts->network->country_code, - bts->network->network_code, - bts->location_area_code); - si3->control_channel_desc = bts->si_common.chan_desc; - si3->cell_options = bts->si_common.cell_options; - si3->cell_sel_par = bts->si_common.cell_sel_par; - si3->rach_control = bts->si_common.rach_control; - - /* allow/disallow DTXu */ - gsm48_set_dtx(&si3->cell_options, bts->dtxu, bts->dtxu, true); - - if (GSM_BTS_HAS_SI(bts, SYSINFO_TYPE_2ter)) { - LOGP(DRR, LOGL_INFO, "SI 2ter is included.\n"); - si_info.si2ter_indicator = 1; - } else { - si_info.si2ter_indicator = 0; - } - if (GSM_BTS_HAS_SI(bts, SYSINFO_TYPE_2quater)) { - LOGP(DRR, LOGL_INFO, "SI 2quater is included, based on %zu EARFCNs and %zu UARFCNs.\n", - si2q_earfcn_count(&bts->si_common.si2quater_neigh_list), bts->si_common.uarfcn_length); - si_info.si2quater_indicator = 1; - } else { - si_info.si2quater_indicator = 0; - } - si_info.early_cm_ctrl = bts->early_classmark_allowed; - - /* SI3 Rest Octets (10.5.2.34), containing - CBQ, CELL_RESELECT_OFFSET, TEMPORARY_OFFSET, PENALTY_TIME - Power Offset, 2ter Indicator, Early Classmark Sending, - Scheduling if and WHERE, GPRS Indicator, SI13 position */ - rc = rest_octets_si3(si3->rest_octets, &si_info); - - return sizeof(*si3) + rc; -} - -static int generate_si4(enum osmo_sysinfo_type t, struct gsm_bts *bts) -{ - int rc; - struct gsm48_system_information_type_4 *si4 = (struct gsm48_system_information_type_4 *) GSM_BTS_SI(bts, t); - struct gsm_lchan *cbch_lchan; - uint8_t *restoct = si4->data; - - /* length of all IEs present except SI4 rest octets and l2_plen */ - int l2_plen = sizeof(*si4) - 1; - - memset(si4, GSM_MACBLOCK_PADDING, GSM_MACBLOCK_LEN); - - si4->header.rr_protocol_discriminator = GSM48_PDISC_RR; - si4->header.skip_indicator = 0; - si4->header.system_information = GSM48_MT_RR_SYSINFO_4; - - gsm48_generate_lai(&si4->lai, bts->network->country_code, - bts->network->network_code, - bts->location_area_code); - si4->cell_sel_par = bts->si_common.cell_sel_par; - si4->rach_control = bts->si_common.rach_control; - - /* Optional: CBCH Channel Description + CBCH Mobile Allocation */ - cbch_lchan = gsm_bts_get_cbch(bts); - if (cbch_lchan) { - struct gsm48_chan_desc cd; - gsm48_lchan2chan_desc(&cd, cbch_lchan); - tv_fixed_put(si4->data, GSM48_IE_CBCH_CHAN_DESC, 3, - (uint8_t *) &cd); - l2_plen += 3 + 1; - restoct += 3 + 1; - /* we don't use hopping and thus don't need a CBCH MA */ - } - - si4->header.l2_plen = GSM48_LEN2PLEN(l2_plen); - - /* SI4 Rest Octets (10.5.2.35), containing - Optional Power offset, GPRS Indicator, - Cell Identity, LSA ID, Selection Parameter */ - rc = rest_octets_si4(restoct, &si_info, (uint8_t *)GSM_BTS_SI(bts, t) + GSM_MACBLOCK_LEN - restoct); - - return l2_plen + 1 + rc; -} - -static int generate_si5(enum osmo_sysinfo_type t, struct gsm_bts *bts) -{ - struct gsm48_system_information_type_5 *si5; - uint8_t *output = GSM_BTS_SI(bts, t); - int rc, l2_plen = 18; - - memset(output, GSM_MACBLOCK_PADDING, GSM_MACBLOCK_LEN); - - /* ip.access nanoBTS needs l2_plen!! */ - switch (bts->type) { - case GSM_BTS_TYPE_NANOBTS: - case GSM_BTS_TYPE_OSMOBTS: - *output++ = GSM48_LEN2PLEN(l2_plen); - l2_plen++; - break; - default: - break; - } - - si5 = (struct gsm48_system_information_type_5 *) GSM_BTS_SI(bts, t); - - /* l2 pseudo length, not part of msg: 18 */ - si5->rr_protocol_discriminator = GSM48_PDISC_RR; - si5->skip_indicator = 0; - si5->system_information = GSM48_MT_RR_SYSINFO_5; - rc = generate_bcch_chan_list(si5->bcch_frequency_list, bts, true, false, false); - if (rc < 0) - return rc; - list_arfcn(si5->bcch_frequency_list, 0xce, - "SI5 Neighbour cells in same band:"); - - /* 04.08 9.1.37: L2 Pseudo Length of 18 */ - return l2_plen; -} - -static int generate_si5bis(enum osmo_sysinfo_type t, struct gsm_bts *bts) -{ - struct gsm48_system_information_type_5bis *si5b; - uint8_t *output = GSM_BTS_SI(bts, t); - int rc, l2_plen = 18; - int n; - - memset(output, GSM_MACBLOCK_PADDING, GSM_MACBLOCK_LEN); - - /* ip.access nanoBTS needs l2_plen!! */ - switch (bts->type) { - case GSM_BTS_TYPE_NANOBTS: - case GSM_BTS_TYPE_OSMOBTS: - *output++ = GSM48_LEN2PLEN(l2_plen); - l2_plen++; - break; - default: - break; - } - - si5b = (struct gsm48_system_information_type_5bis *) GSM_BTS_SI(bts, t); - - /* l2 pseudo length, not part of msg: 18 */ - si5b->rr_protocol_discriminator = GSM48_PDISC_RR; - si5b->skip_indicator = 0; - si5b->system_information = GSM48_MT_RR_SYSINFO_5bis; - rc = generate_bcch_chan_list(si5b->bcch_frequency_list, bts, true, true, false); - if (rc < 0) - return rc; - n = list_arfcn(si5b->bcch_frequency_list, 0xce, - "Neighbour cells in same band, but outside P-GSM:"); - if (n) { - /* indicate in SI5 and SI5bis: there is an extension */ - struct gsm48_system_information_type_5 *si5 = - (struct gsm48_system_information_type_5 *) GSM_BTS_SI(bts, SYSINFO_TYPE_5); - si5->bcch_frequency_list[0] |= 0x20; - si5b->bcch_frequency_list[0] |= 0x20; - } else - bts->si_valid &= ~(1 << SYSINFO_TYPE_5bis); - - /* 04.08 9.1.37: L2 Pseudo Length of 18 */ - return l2_plen; -} - -static int generate_si5ter(enum osmo_sysinfo_type t, struct gsm_bts *bts) -{ - struct gsm48_system_information_type_5ter *si5t; - uint8_t *output = GSM_BTS_SI(bts, t); - int rc, l2_plen = 18; - int n; - - memset(output, GSM_MACBLOCK_PADDING, GSM_MACBLOCK_LEN); - - /* ip.access nanoBTS needs l2_plen!! */ - switch (bts->type) { - case GSM_BTS_TYPE_NANOBTS: - case GSM_BTS_TYPE_OSMOBTS: - *output++ = GSM48_LEN2PLEN(l2_plen); - l2_plen++; - break; - default: - break; - } - - si5t = (struct gsm48_system_information_type_5ter *) GSM_BTS_SI(bts, t); - - /* l2 pseudo length, not part of msg: 18 */ - si5t->rr_protocol_discriminator = GSM48_PDISC_RR; - si5t->skip_indicator = 0; - si5t->system_information = GSM48_MT_RR_SYSINFO_5ter; - rc = generate_bcch_chan_list(si5t->bcch_frequency_list, bts, true, false, true); - if (rc < 0) - return rc; - n = list_arfcn(si5t->bcch_frequency_list, 0x8e, - "Neighbour cells in different band:"); - if (!n) - bts->si_valid &= ~(1 << SYSINFO_TYPE_5ter); - - /* 04.08 9.1.37: L2 Pseudo Length of 18 */ - return l2_plen; -} - -static int generate_si6(enum osmo_sysinfo_type t, struct gsm_bts *bts) -{ - struct gsm48_system_information_type_6 *si6; - uint8_t *output = GSM_BTS_SI(bts, t); - int l2_plen = 11; - int rc; - - memset(output, GSM_MACBLOCK_PADDING, GSM_MACBLOCK_LEN); - - /* ip.access nanoBTS needs l2_plen!! */ - switch (bts->type) { - case GSM_BTS_TYPE_NANOBTS: - case GSM_BTS_TYPE_OSMOBTS: - *output++ = GSM48_LEN2PLEN(l2_plen); - l2_plen++; - break; - default: - break; - } - - si6 = (struct gsm48_system_information_type_6 *) GSM_BTS_SI(bts, t); - - /* l2 pseudo length, not part of msg: 11 */ - si6->rr_protocol_discriminator = GSM48_PDISC_RR; - si6->skip_indicator = 0; - si6->system_information = GSM48_MT_RR_SYSINFO_6; - si6->cell_identity = htons(bts->cell_identity); - gsm48_generate_lai(&si6->lai, bts->network->country_code, - bts->network->network_code, - bts->location_area_code); - si6->cell_options = bts->si_common.cell_options; - si6->ncc_permitted = bts->si_common.ncc_permitted; - /* allow/disallow DTXu */ - gsm48_set_dtx(&si6->cell_options, bts->dtxu, bts->dtxu, false); - - /* SI6 Rest Octets: 10.5.2.35a: PCH / NCH info, VBS/VGCS options */ - rc = rest_octets_si6(si6->rest_octets, is_dcs_net(bts)); - - return l2_plen + rc; -} - -static struct gsm48_si13_info si13_default = { - .cell_opts = { - .nmo = GPRS_NMO_II, - .t3168 = 2000, - .t3192 = 1500, - .drx_timer_max = 3, - .bs_cv_max = 15, - .ctrl_ack_type_use_block = true, - .ext_info_present = 0, - .supports_egprs_11bit_rach = 0, - .ext_info = { - /* The values below are just guesses ! */ - .egprs_supported = 0, - .use_egprs_p_ch_req = 1, - .bep_period = 5, - .pfc_supported = 0, - .dtm_supported = 0, - .bss_paging_coordination = 0, - }, - }, - .pwr_ctrl_pars = { - .alpha = 0, /* a = 0.0 */ - .t_avg_w = 16, - .t_avg_t = 16, - .pc_meas_chan = 0, /* downling measured on CCCH */ - .n_avg_i = 8, - }, - .bcch_change_mark = 1, - .si_change_field = 0, - .pbcch_present = 0, - { - .no_pbcch = { - .rac = 0, /* needs to be patched */ - .spgc_ccch_sup = 0, - .net_ctrl_ord = 0, - .prio_acc_thr = 6, - }, - }, -}; - -static int generate_si13(enum osmo_sysinfo_type t, struct gsm_bts *bts) -{ - struct gsm48_system_information_type_13 *si13 = - (struct gsm48_system_information_type_13 *) GSM_BTS_SI(bts, t); - int ret; - - memset(si13, GSM_MACBLOCK_PADDING, GSM_MACBLOCK_LEN); - - si13->header.rr_protocol_discriminator = GSM48_PDISC_RR; - si13->header.skip_indicator = 0; - si13->header.system_information = GSM48_MT_RR_SYSINFO_13; - - si13_default.no_pbcch.rac = bts->gprs.rac; - si13_default.no_pbcch.net_ctrl_ord = bts->gprs.net_ctrl_ord; - - si13_default.cell_opts.ctrl_ack_type_use_block = - bts->gprs.ctrl_ack_type_use_block; - - /* Information about the other SIs */ - si13_default.bcch_change_mark = bts->bcch_change_mark; - si13_default.cell_opts.supports_egprs_11bit_rach = - bts->gprs.supports_egprs_11bit_rach; - - ret = rest_octets_si13(si13->rest_octets, &si13_default); - if (ret < 0) - return ret; - - /* length is coded in bit 2 an up */ - si13->header.l2_plen = 0x01; - - return sizeof (*si13) + ret; -} - -typedef int (*gen_si_fn_t)(enum osmo_sysinfo_type t, struct gsm_bts *bts); - -static const gen_si_fn_t gen_si_fn[_MAX_SYSINFO_TYPE] = { - [SYSINFO_TYPE_1] = &generate_si1, - [SYSINFO_TYPE_2] = &generate_si2, - [SYSINFO_TYPE_2bis] = &generate_si2bis, - [SYSINFO_TYPE_2ter] = &generate_si2ter, - [SYSINFO_TYPE_2quater] = &generate_si2quater, - [SYSINFO_TYPE_3] = &generate_si3, - [SYSINFO_TYPE_4] = &generate_si4, - [SYSINFO_TYPE_5] = &generate_si5, - [SYSINFO_TYPE_5bis] = &generate_si5bis, - [SYSINFO_TYPE_5ter] = &generate_si5ter, - [SYSINFO_TYPE_6] = &generate_si6, - [SYSINFO_TYPE_13] = &generate_si13, -}; - -int gsm_generate_si(struct gsm_bts *bts, enum osmo_sysinfo_type si_type) -{ - gen_si_fn_t gen_si; - - switch (bts->gprs.mode) { - case BTS_GPRS_EGPRS: - si13_default.cell_opts.ext_info_present = 1; - si13_default.cell_opts.ext_info.egprs_supported = 1; - /* fallthrough */ - case BTS_GPRS_GPRS: - si_info.gprs_ind.present = 1; - break; - case BTS_GPRS_NONE: - si_info.gprs_ind.present = 0; - break; - } - - memcpy(&si_info.selection_params, - &bts->si_common.cell_ro_sel_par, - sizeof(struct gsm48_si_selection_params)); - - gen_si = gen_si_fn[si_type]; - if (!gen_si) - return -EINVAL; - - return gen_si(si_type, bts); -} diff --git a/openbsc/src/libcommon-cs/Makefile.am b/openbsc/src/libcommon-cs/Makefile.am deleted file mode 100644 index f3921ba5..00000000 --- a/openbsc/src/libcommon-cs/Makefile.am +++ /dev/null @@ -1,20 +0,0 @@ -AM_CPPFLAGS = \ - $(all_includes) \ - -I$(top_srcdir)/include \ - -I$(top_builddir) \ - $(NULL) - -AM_CFLAGS = \ - -Wall \ - $(LIBOSMOCORE_CFLAGS) \ - $(LIBOSMOGSM_CFLAGS) \ - $(LIBOSMOVTY_CFLAGS) \ - $(LIBOSMOABIS_CFLAGS) \ - $(COVERAGE_CFLAGS) \ - $(NULL) - -noinst_LIBRARIES = libcommon-cs.a - -libcommon_cs_a_SOURCES = \ - common_cs.c \ - common_cs_vty.c diff --git a/openbsc/src/libcommon-cs/common_cs.c b/openbsc/src/libcommon-cs/common_cs.c deleted file mode 100644 index 7905802b..00000000 --- a/openbsc/src/libcommon-cs/common_cs.c +++ /dev/null @@ -1,133 +0,0 @@ -/* Code used by both libbsc and libmsc (common_cs means "BSC or MSC"). - * - * (C) 2016 by sysmocom s.m.f.c. - * (C) 2008-2010 by Harald Welte - * (C) 2014 by Holger Hans Peter Freyther - * All Rights Reserved - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation; either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - * - */ - -#include - -#include - -#include -#include -#include -#include -#include - -/* Warning: if bsc_network_init() is not called, some of the members of - * gsm_network are not initialized properly and must not be used! (In - * particular the llist heads and stats counters.) - * The long term aim should be to have entirely separate structs for libbsc and - * libmsc with some common general items. - */ -struct gsm_network *gsm_network_init(void *ctx, - uint16_t country_code, - uint16_t network_code, - mncc_recv_cb_t mncc_recv) -{ - struct gsm_network *net; - - const char *default_regexp = ".*"; - - net = talloc_zero(ctx, struct gsm_network); - if (!net) - return NULL; - - net->subscr_group = talloc_zero(net, struct gsm_subscriber_group); - if (!net->subscr_group) { - talloc_free(net); - return NULL; - } - - if (gsm_parse_reg(net, &net->authorized_regexp, &net->authorized_reg_str, 1, - &default_regexp) != 0) - return NULL; - - net->subscr_group->net = net; - net->auto_create_subscr = true; - net->auto_assign_exten = true; - - net->country_code = country_code; - net->network_code = network_code; - - INIT_LLIST_HEAD(&net->trans_list); - INIT_LLIST_HEAD(&net->upqueue); - INIT_LLIST_HEAD(&net->subscr_conns); - - net->bsc_subscribers = talloc_zero(net, struct llist_head); - INIT_LLIST_HEAD(net->bsc_subscribers); - - /* init statistics */ - net->msc_ctrs = rate_ctr_group_alloc(net, &msc_ctrg_desc, 0); - net->active_calls = osmo_counter_alloc("msc.active_calls"); - - net->mncc_recv = mncc_recv; - net->ext_min = GSM_MIN_EXTEN; - net->ext_max = GSM_MAX_EXTEN; - - net->dyn_ts_allow_tch_f = true; - - return net; -} - -struct msgb *gsm48_create_mm_serv_rej(enum gsm48_reject_value value) -{ - struct msgb *msg; - struct gsm48_hdr *gh; - - msg = gsm48_msgb_alloc_name("GSM 04.08 SERV REJ"); - if (!msg) - return NULL; - - gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh) + 1); - gh->proto_discr = GSM48_PDISC_MM; - gh->msg_type = GSM48_MT_MM_CM_SERV_REJ; - gh->data[0] = value; - - return msg; -} - -struct msgb *gsm48_create_loc_upd_rej(uint8_t cause) -{ - struct gsm48_hdr *gh; - struct msgb *msg; - - msg = gsm48_msgb_alloc_name("GSM 04.08 LOC UPD REJ"); - if (!msg) - return NULL; - - gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh) + 1); - gh->proto_discr = GSM48_PDISC_MM; - gh->msg_type = GSM48_MT_MM_LOC_UPD_REJECT; - gh->data[0] = cause; - return msg; -} - -uint8_t sms_next_rp_msg_ref(uint8_t *next_rp_ref) -{ - const uint8_t rp_msg_ref = *next_rp_ref; - /* - * This should wrap as the valid range is 0 to 255. We only - * transfer one SMS at a time so we don't need to check if - * the id has been already assigned. - */ - *next_rp_ref += 1; - - return rp_msg_ref; -} diff --git a/openbsc/src/libcommon-cs/common_cs_vty.c b/openbsc/src/libcommon-cs/common_cs_vty.c deleted file mode 100644 index bcc001d5..00000000 --- a/openbsc/src/libcommon-cs/common_cs_vty.c +++ /dev/null @@ -1,325 +0,0 @@ -/* Code used by both libbsc and libmsc (common_cs means "BSC or MSC"). - * - * (C) 2016 by sysmocom s.m.f.c. - * (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 - -struct cmd_node net_node = { - GSMNET_NODE, - "%s(config-net)# ", - 1, -}; - -#define NETWORK_STR "Configure the GSM network\n" -#define CODE_CMD_STR "Code commands\n" -#define NAME_CMD_STR "Name Commands\n" -#define NAME_STR "Name to use\n" - -DEFUN(cfg_net, - cfg_net_cmd, - "network", NETWORK_STR) -{ - vty->index = gsmnet_from_vty(vty); - vty->node = GSMNET_NODE; - - return CMD_SUCCESS; -} - -DEFUN(cfg_net_ncc, - cfg_net_ncc_cmd, - "network country code <1-999>", - "Set the GSM network country code\n" - "Country commands\n" - CODE_CMD_STR - "Network Country Code to use\n") -{ - struct gsm_network *gsmnet = gsmnet_from_vty(vty); - - gsmnet->country_code = atoi(argv[0]); - - return CMD_SUCCESS; -} - -DEFUN(cfg_net_mnc, - cfg_net_mnc_cmd, - "mobile network code <0-999>", - "Set the GSM mobile network code\n" - "Network Commands\n" - CODE_CMD_STR - "Mobile Network Code to use\n") -{ - struct gsm_network *gsmnet = gsmnet_from_vty(vty); - - gsmnet->network_code = atoi(argv[0]); - - return CMD_SUCCESS; -} - -DEFUN(cfg_net_name_short, - cfg_net_name_short_cmd, - "short name NAME", - "Set the short GSM network name\n" NAME_CMD_STR NAME_STR) -{ - struct gsm_network *gsmnet = gsmnet_from_vty(vty); - - osmo_talloc_replace_string(gsmnet, &gsmnet->name_short, argv[0]); - return CMD_SUCCESS; -} - -DEFUN(cfg_net_name_long, - cfg_net_name_long_cmd, - "long name NAME", - "Set the long GSM network name\n" NAME_CMD_STR NAME_STR) -{ - struct gsm_network *gsmnet = gsmnet_from_vty(vty); - - osmo_talloc_replace_string(gsmnet, &gsmnet->name_long, argv[0]); - return CMD_SUCCESS; -} - -DEFUN(cfg_net_auth_policy, - cfg_net_auth_policy_cmd, - "auth policy (closed|accept-all|regexp|token)", - "Authentication (not cryptographic)\n" - "Set the GSM network authentication policy\n" - "Require the MS to be activated in HLR\n" - "Accept all MS, whether in HLR or not\n" - "Use regular expression for IMSI authorization decision\n" - "Use SMS-token based authentication\n") -{ - enum gsm_auth_policy policy = gsm_auth_policy_parse(argv[0]); - struct gsm_network *gsmnet = gsmnet_from_vty(vty); - - gsmnet->auth_policy = policy; - - return CMD_SUCCESS; -} - -DEFUN(cfg_net_authorize_regexp, cfg_net_authorize_regexp_cmd, - "authorized-regexp REGEXP", - "Set regexp for IMSI which will be used for authorization decision\n" - "Regular expression, IMSIs matching it are allowed to use the network\n") -{ - struct gsm_network *gsmnet = gsmnet_from_vty(vty); - if (gsm_parse_reg(gsmnet, &gsmnet->authorized_regexp, - &gsmnet->authorized_reg_str, argc, argv) != 0) { - vty_out(vty, "%%Failed to parse the authorized-regexp: '%s'%s", - argv[0], VTY_NEWLINE); - return CMD_WARNING; - } - - return CMD_SUCCESS; -} - -DEFUN(cfg_net_reject_cause, - cfg_net_reject_cause_cmd, - "location updating reject cause <2-111>", - "Set the reject cause of location updating reject\n" - "Set the reject cause of location updating reject\n" - "Set the reject cause of location updating reject\n" - "Set the reject cause of location updating reject\n" - "Cause Value as Per GSM TS 04.08\n") -{ - struct gsm_network *gsmnet = gsmnet_from_vty(vty); - - gsmnet->reject_cause = atoi(argv[0]); - - return CMD_SUCCESS; -} - -DEFUN(cfg_net_encryption, - cfg_net_encryption_cmd, - "encryption a5 (0|1|2|3)", - "Encryption options\n" - "A5 encryption\n" "A5/0: No encryption\n" - "A5/1: Encryption\n" "A5/2: Export-grade Encryption\n" - "A5/3: 'New' Secure Encryption\n") -{ - struct gsm_network *gsmnet = gsmnet_from_vty(vty); - - gsmnet->a5_encryption = atoi(argv[0]); - - return CMD_SUCCESS; -} - -DEFUN(cfg_net_rrlp_mode, cfg_net_rrlp_mode_cmd, - "rrlp mode (none|ms-based|ms-preferred|ass-preferred)", - "Radio Resource Location Protocol\n" - "Set the Radio Resource Location Protocol Mode\n" - "Don't send RRLP request\n" - "Request MS-based location\n" - "Request any location, prefer MS-based\n" - "Request any location, prefer MS-assisted\n") -{ - struct gsm_network *gsmnet = gsmnet_from_vty(vty); - - gsmnet->rrlp.mode = rrlp_mode_parse(argv[0]); - - return CMD_SUCCESS; -} - -DEFUN(cfg_net_mm_info, cfg_net_mm_info_cmd, - "mm info (0|1)", - "Mobility Management\n" - "Send MM INFO after LOC UPD ACCEPT\n" - "Disable\n" "Enable\n") -{ - struct gsm_network *gsmnet = gsmnet_from_vty(vty); - - gsmnet->send_mm_info = atoi(argv[0]); - - return CMD_SUCCESS; -} - -DEFUN(cfg_net_dyn_ts_allow_tch_f, - cfg_net_dyn_ts_allow_tch_f_cmd, - "dyn_ts_allow_tch_f (0|1)", - "Allow or disallow allocating TCH/F on TCH_F_TCH_H_PDCH timeslots\n" - "Disallow TCH/F on TCH_F_TCH_H_PDCH (default)\n" - "Allow TCH/F on TCH_F_TCH_H_PDCH\n") -{ - struct gsm_network *gsmnet = gsmnet_from_vty(vty); - gsmnet->dyn_ts_allow_tch_f = atoi(argv[0]) ? true : false; - return CMD_SUCCESS; -} - -DEFUN(cfg_net_subscr_keep, - cfg_net_subscr_keep_cmd, - "subscriber-keep-in-ram (0|1)", - "Keep unused subscribers in RAM.\n" - "Delete unused subscribers\n" "Keep unused subscribers\n") -{ - struct gsm_network *gsmnet = gsmnet_from_vty(vty); - gsmnet->subscr_group->keep_subscr = atoi(argv[0]); - return CMD_SUCCESS; -} - -DEFUN(cfg_net_timezone, - cfg_net_timezone_cmd, - "timezone <-19-19> (0|15|30|45)", - "Set the Timezone Offset of the network\n" - "Timezone offset (hours)\n" - "Timezone offset (00 minutes)\n" - "Timezone offset (15 minutes)\n" - "Timezone offset (30 minutes)\n" - "Timezone offset (45 minutes)\n" - ) -{ - struct gsm_network *net = vty->index; - int tzhr = atoi(argv[0]); - int tzmn = atoi(argv[1]); - - net->tz.hr = tzhr; - net->tz.mn = tzmn; - net->tz.dst = 0; - net->tz.override = 1; - - return CMD_SUCCESS; -} - -DEFUN(cfg_net_timezone_dst, - cfg_net_timezone_dst_cmd, - "timezone <-19-19> (0|15|30|45) <0-2>", - "Set the Timezone Offset of the network\n" - "Timezone offset (hours)\n" - "Timezone offset (00 minutes)\n" - "Timezone offset (15 minutes)\n" - "Timezone offset (30 minutes)\n" - "Timezone offset (45 minutes)\n" - "DST offset (hours)\n" - ) -{ - struct gsm_network *net = vty->index; - int tzhr = atoi(argv[0]); - int tzmn = atoi(argv[1]); - int tzdst = atoi(argv[2]); - - net->tz.hr = tzhr; - net->tz.mn = tzmn; - net->tz.dst = tzdst; - net->tz.override = 1; - - return CMD_SUCCESS; -} - -DEFUN(cfg_net_no_timezone, - cfg_net_no_timezone_cmd, - "no timezone", - NO_STR - "Disable network timezone override, use system tz\n") -{ - struct gsm_network *net = vty->index; - - net->tz.override = 0; - - return CMD_SUCCESS; -} - -static struct gsm_network *vty_global_gsm_network = NULL; - -/* initialize VTY elements used in both BSC and MSC */ -int common_cs_vty_init(struct gsm_network *network, - int (* config_write_net )(struct vty *)) -{ - OSMO_ASSERT(vty_global_gsm_network == NULL); - vty_global_gsm_network = network; - - install_element(CONFIG_NODE, &cfg_net_cmd); - install_node(&net_node, config_write_net); - vty_install_default(GSMNET_NODE); - install_element(GSMNET_NODE, &cfg_net_ncc_cmd); - install_element(GSMNET_NODE, &cfg_net_mnc_cmd); - install_element(GSMNET_NODE, &cfg_net_name_short_cmd); - install_element(GSMNET_NODE, &cfg_net_name_long_cmd); - install_element(GSMNET_NODE, &cfg_net_auth_policy_cmd); - install_element(GSMNET_NODE, &cfg_net_authorize_regexp_cmd); - install_element(GSMNET_NODE, &cfg_net_reject_cause_cmd); - install_element(GSMNET_NODE, &cfg_net_encryption_cmd); - install_element(GSMNET_NODE, &cfg_net_rrlp_mode_cmd); - install_element(GSMNET_NODE, &cfg_net_mm_info_cmd); - install_element(GSMNET_NODE, &cfg_net_subscr_keep_cmd); - install_element(GSMNET_NODE, &cfg_net_timezone_cmd); - install_element(GSMNET_NODE, &cfg_net_timezone_dst_cmd); - install_element(GSMNET_NODE, &cfg_net_no_timezone_cmd); - install_element(GSMNET_NODE, &cfg_net_dyn_ts_allow_tch_f_cmd); - - return CMD_SUCCESS; -} - -struct gsm_network *gsmnet_from_vty(struct vty *v) -{ - /* It can't hurt to force callers to continue to pass the vty instance - * to this function, in case we'd like to retrieve the global - * gsm_network instance from the vty at some point in the future. But - * until then, just return the global pointer, which should have been - * initialized by common_cs_vty_init(). - */ - OSMO_ASSERT(vty_global_gsm_network); - return vty_global_gsm_network; -} diff --git a/openbsc/src/libcommon/Makefile.am b/openbsc/src/libcommon/Makefile.am deleted file mode 100644 index 0b258c08..00000000 --- a/openbsc/src/libcommon/Makefile.am +++ /dev/null @@ -1,47 +0,0 @@ -AM_CPPFLAGS = \ - $(all_includes) \ - -I$(top_srcdir)/include \ - -I$(top_builddir) \ - $(NULL) - -AM_CFLAGS = \ - -Wall \ - $(LIBOSMOCORE_CFLAGS) \ - $(LIBOSMOGSM_CFLAGS) \ - $(LIBOSMOVTY_CFLAGS) \ - $(LIBOSMOABIS_CFLAGS) \ - $(COVERAGE_CFLAGS) \ - $(NULL) - -noinst_LIBRARIES = \ - libcommon.a \ - $(NULL) - -libcommon_a_SOURCES = \ - bsc_version.c \ - common_vty.c \ - debug.c \ - gsm_data.c \ - gsm_data_shared.c \ - gsup_client.c \ - oap_client.c \ - socket.c \ - talloc_ctx.c \ - gsm_subscriber_base.c \ - $(NULL) - -noinst_PROGRAMS = \ - gsup_test_client \ - $(NULL) - -gsup_test_client_SOURCES = \ - gsup_test_client.c \ - $(NULL) -gsup_test_client_LDADD = \ - libcommon.a \ - $(LIBOSMOCORE_LIBS) \ - $(LIBOSMOGSM_LIBS) \ - $(LIBOSMOVTY_LIBS) \ - $(LIBOSMOABIS_LIBS) \ - -lrt \ - $(NULL) diff --git a/openbsc/src/libcommon/bsc_version.c b/openbsc/src/libcommon/bsc_version.c deleted file mode 100644 index f0369bff..00000000 --- a/openbsc/src/libcommon/bsc_version.c +++ /dev/null @@ -1,30 +0,0 @@ -/* Hold the copyright and version string */ -/* (C) 2010-2016 by Harald Welte - * All Rights Reserved - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation; either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public 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-2016 Harald Welte, Holger Freyther\r\n" - "Contributions by Daniel Willmann, Jan Lübbe, Stefan Schmidt\r\n" - "Dieter Spaar, Andreas Eversberg, Sylvain Munaut, Neels Hofmeyr\r\n\r\n" - "License AGPLv3+: GNU AGPL version 3 or later \r\n" - "This is free software: you are free to change and redistribute it.\r\n" - "There is NO WARRANTY, to the extent permitted by law.\r\n"; - - diff --git a/openbsc/src/libcommon/common_vty.c b/openbsc/src/libcommon/common_vty.c deleted file mode 100644 index 6e1c10b0..00000000 --- a/openbsc/src/libcommon/common_vty.c +++ /dev/null @@ -1,145 +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 -#include - - -int bsc_vty_go_parent(struct vty *vty) -{ - switch (vty->node) { - case GSMNET_NODE: - vty->node = CONFIG_NODE; - vty->index = NULL; - break; - case BTS_NODE: - vty->node = GSMNET_NODE; - { - /* set vty->index correctly ! */ - struct gsm_bts *bts = vty->index; - vty->index = bts->network; - vty->index_sub = NULL; - } - break; - case TRX_NODE: - vty->node = BTS_NODE; - { - /* set vty->index correctly ! */ - struct gsm_bts_trx *trx = vty->index; - vty->index = trx->bts; - vty->index_sub = &trx->bts->description; - } - break; - case TS_NODE: - vty->node = TRX_NODE; - { - /* set vty->index correctly ! */ - struct gsm_bts_trx_ts *ts = vty->index; - vty->index = ts->trx; - vty->index_sub = &ts->trx->description; - } - break; - case OML_NODE: - case OM2K_NODE: - vty->node = ENABLE_NODE; - /* NOTE: this only works because it's not part of the config - * tree, where outer commands are searched via vty_go_parent() - * and only (!) executed when a matching one is found. - */ - talloc_free(vty->index); - vty->index = NULL; - break; - case OM2K_CON_GROUP_NODE: - vty->node = BTS_NODE; - { - struct con_group *cg = vty->index; - struct gsm_bts *bts = cg->bts; - vty->index = bts; - vty->index_sub = &bts->description; - } - break; - case NAT_BSC_NODE: - vty->node = NAT_NODE; - { - struct bsc_config *bsc_config = vty->index; - vty->index = bsc_config->nat; - } - break; - case PGROUP_NODE: - vty->node = NAT_NODE; - vty->index = NULL; - break; - case TRUNK_NODE: - vty->node = MGCP_NODE; - vty->index = NULL; - break; - case SMPP_ESME_NODE: - vty->node = SMPP_NODE; - vty->index = NULL; - break; - case SMPP_NODE: - case MGCP_NODE: - case GBPROXY_NODE: - case SGSN_NODE: - case NAT_NODE: - case BSC_NODE: - case MSC_NODE: - case MNCC_INT_NODE: - case NITB_NODE: - default: - if (bsc_vty_is_config_node(vty, vty->node)) - vty->node = CONFIG_NODE; - else - vty->node = ENABLE_NODE; - - vty->index = NULL; - } - - return vty->node; -} - -int bsc_vty_is_config_node(struct vty *vty, int node) -{ - switch (node) { - /* add items that are not config */ - case OML_NODE: - case OM2K_NODE: - case SUBSCR_NODE: - case CONFIG_NODE: - return 0; - - default: - return 1; - } -} diff --git a/openbsc/src/libcommon/debug.c b/openbsc/src/libcommon/debug.c deleted file mode 100644 index f29f1683..00000000 --- a/openbsc/src/libcommon/debug.c +++ /dev/null @@ -1,235 +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 -#include - -/* default categories */ -static const struct log_info_cat default_categories[] = { - [DRLL] = { - .name = "DRLL", - .description = "A-bis Radio Link Layer (RLL)", - .color = "\033[1;31m", - .enabled = 1, .loglevel = LOGL_NOTICE, - }, - [DCC] = { - .name = "DCC", - .description = "Layer3 Call Control (CC)", - .color = "\033[1;32m", - .enabled = 1, .loglevel = LOGL_NOTICE, - }, - [DMM] = { - .name = "DMM", - .description = "Layer3 Mobility Management (MM)", - .color = "\033[1;33m", - .enabled = 1, .loglevel = LOGL_NOTICE, - }, - [DRR] = { - .name = "DRR", - .description = "Layer3 Radio Resource (RR)", - .color = "\033[1;34m", - .enabled = 1, .loglevel = LOGL_NOTICE, - }, - [DRSL] = { - .name = "DRSL", - .description = "A-bis Radio Siganlling Link (RSL)", - .color = "\033[1;35m", - .enabled = 1, .loglevel = LOGL_NOTICE, - }, - [DNM] = { - .name = "DNM", - .description = "A-bis Network Management / O&M (NM/OML)", - .color = "\033[1;36m", - .enabled = 1, .loglevel = LOGL_INFO, - }, - [DMNCC] = { - .name = "DMNCC", - .description = "MNCC API for Call Control application", - .color = "\033[1;39m", - .enabled = 1, .loglevel = LOGL_NOTICE, - }, - [DPAG] = { - .name = "DPAG", - .description = "Paging Subsystem", - .color = "\033[1;38m", - .enabled = 1, .loglevel = LOGL_NOTICE, - }, - [DMEAS] = { - .name = "DMEAS", - .description = "Radio Measurement Processing", - .enabled = 0, .loglevel = LOGL_NOTICE, - }, - [DSCCP] = { - .name = "DSCCP", - .description = "SCCP Protocol", - .enabled = 1, .loglevel = LOGL_NOTICE, - }, - [DMSC] = { - .name = "DMSC", - .description = "Mobile Switching Center", - .enabled = 1, .loglevel = LOGL_NOTICE, - }, - [DMGCP] = { - .name = "DMGCP", - .description = "Media Gateway Control Protocol", - .enabled = 1, .loglevel = LOGL_NOTICE, - }, - [DHO] = { - .name = "DHO", - .description = "Hand-Over", - .enabled = 1, .loglevel = LOGL_NOTICE, - }, - [DDB] = { - .name = "DDB", - .description = "Database Layer", - .enabled = 1, .loglevel = LOGL_NOTICE, - }, - [DREF] = { - .name = "DREF", - .description = "Reference Counting", - .enabled = 0, .loglevel = LOGL_NOTICE, - }, - [DGPRS] = { - .name = "DGPRS", - .description = "GPRS Packet Service", - .enabled = 1, .loglevel = LOGL_DEBUG, - }, - [DNS] = { - .name = "DNS", - .description = "GPRS Network Service (NS)", - .enabled = 1, .loglevel = LOGL_INFO, - }, - [DBSSGP] = { - .name = "DBSSGP", - .description = "GPRS BSS Gateway Protocol (BSSGP)", - .enabled = 1, .loglevel = LOGL_DEBUG, - }, - [DLLC] = { - .name = "DLLC", - .description = "GPRS Logical Link Control Protocol (LLC)", - .enabled = 1, .loglevel = LOGL_DEBUG, - }, - [DSNDCP] = { - .name = "DSNDCP", - .description = "GPRS Sub-Network Dependent Control Protocol (SNDCP)", - .enabled = 1, .loglevel = LOGL_DEBUG, - }, - [DNAT] = { - .name = "DNAT", - .description = "GSM 08.08 NAT/Multiplexer", - .enabled = 1, .loglevel = LOGL_NOTICE, - }, - [DCTRL] = { - .name = "DCTRL", - .description = "Control interface", - .enabled = 1, .loglevel = LOGL_NOTICE, - }, - [DSMPP] = { - .name = "DSMPP", - .description = "SMPP interface for external SMS apps", - .enabled = 1, .loglevel = LOGL_DEBUG, - }, - [DFILTER] = { - .name = "DFILTER", - .description = "BSC/NAT IMSI based filtering", - .enabled = 1, .loglevel = LOGL_DEBUG, - }, - [DRANAP] = { - .name = "DRANAP", - .description = "Radio Access Network Application Part Protocol", - .enabled = 1, .loglevel = LOGL_DEBUG, - }, - [DSUA] = { - .name = "DSUA", - .description = "SCCP User Adaptation Protocol", - .enabled = 1, .loglevel = LOGL_DEBUG, - }, - [DPCU] = { - .name = "DPCU", - .description = "PCU Interface", - .enabled = 1, .loglevel = LOGL_DEBUG, - }, -}; - -static int filter_fn(const struct log_context *ctx, struct log_target *tar) -{ - const struct gsm_subscriber *subscr = ctx->ctx[LOG_CTX_VLR_SUBSCR]; - const struct bsc_subscr *bsub = ctx->ctx[LOG_CTX_BSC_SUBSCR]; - const struct gprs_nsvc *nsvc = ctx->ctx[LOG_CTX_GB_NSVC]; - const struct gprs_nsvc *bvc = ctx->ctx[LOG_CTX_GB_BVC]; - - if ((tar->filter_map & (1 << LOG_FLT_VLR_SUBSCR)) != 0 - && subscr && subscr == tar->filter_data[LOG_FLT_VLR_SUBSCR]) - return 1; - - if ((tar->filter_map & (1 << LOG_FLT_BSC_SUBSCR)) != 0 - && bsub && bsub == tar->filter_data[LOG_FLT_BSC_SUBSCR]) - return 1; - - /* Filter on the NS Virtual Connection */ - if ((tar->filter_map & (1 << LOG_FLT_GB_NSVC)) != 0 - && nsvc && (nsvc == tar->filter_data[LOG_FLT_GB_NSVC])) - return 1; - - /* Filter on the NS Virtual Connection */ - if ((tar->filter_map & (1 << LOG_FLT_GB_BVC)) != 0 - && bvc && (bvc == tar->filter_data[LOG_FLT_GB_BVC])) - return 1; - - return 0; -} - -const struct log_info log_info = { - .filter_fn = filter_fn, - .cat = default_categories, - .num_cat = ARRAY_SIZE(default_categories), -}; - -void log_set_filter_vlr_subscr(struct log_target *target, - struct gsm_subscriber *vlr_subscr) -{ - struct gsm_subscriber **fsub = (void*)&target->filter_data[LOG_FLT_VLR_SUBSCR]; - - /* free the old data */ - if (*fsub) { - subscr_put(*fsub); - *fsub = NULL; - } - - if (vlr_subscr) { - target->filter_map |= (1 << LOG_FLT_VLR_SUBSCR); - *fsub = subscr_get(vlr_subscr); - } else - target->filter_map &= ~(1 << LOG_FLT_VLR_SUBSCR); -} diff --git a/openbsc/src/libcommon/gsm_data.c b/openbsc/src/libcommon/gsm_data.c deleted file mode 100644 index db7de082..00000000 --- a/openbsc/src/libcommon/gsm_data.c +++ /dev/null @@ -1,473 +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 - -#include -#include -#include - -void *tall_bsc_ctx; - -static LLIST_HEAD(bts_models); - -void set_ts_e1link(struct gsm_bts_trx_ts *ts, uint8_t e1_nr, - uint8_t e1_ts, uint8_t e1_ts_ss) -{ - ts->e1_link.e1_nr = e1_nr; - ts->e1_link.e1_ts = e1_ts; - ts->e1_link.e1_ts_ss = e1_ts_ss; -} - -static struct gsm_bts_model *bts_model_find(enum gsm_bts_type type) -{ - struct gsm_bts_model *model; - - llist_for_each_entry(model, &bts_models, list) { - if (model->type == type) - return model; - } - - return NULL; -} - -int gsm_bts_model_register(struct gsm_bts_model *model) -{ - if (bts_model_find(model->type)) - return -EEXIST; - - tlv_def_patch(&model->nm_att_tlvdef, &abis_nm_att_tlvdef); - llist_add_tail(&model->list, &bts_models); - return 0; -} - -/* Get reference to a neighbor cell on a given BCCH ARFCN */ -struct gsm_bts *gsm_bts_neighbor(const struct gsm_bts *bts, - uint16_t arfcn, uint8_t bsic) -{ - struct gsm_bts *neigh; - /* FIXME: use some better heuristics here to determine which cell - * using this ARFCN really is closest to the target cell. For - * now we simply assume that each ARFCN will only be used by one - * cell */ - - llist_for_each_entry(neigh, &bts->network->bts_list, list) { - if (neigh->c0->arfcn == arfcn && - neigh->bsic == bsic) - return neigh; - } - - return NULL; -} - -const struct value_string bts_type_descs[_NUM_GSM_BTS_TYPE+1] = { - { GSM_BTS_TYPE_UNKNOWN, "Unknown BTS Type" }, - { GSM_BTS_TYPE_BS11, "Siemens BTS (BS-11 or compatible)" }, - { GSM_BTS_TYPE_NANOBTS, "ip.access nanoBTS or compatible" }, - { GSM_BTS_TYPE_RBS2000, "Ericsson RBS2000 Series" }, - { GSM_BTS_TYPE_NOKIA_SITE, "Nokia {Metro,Ultra,In}Site" }, - { GSM_BTS_TYPE_OSMOBTS, "sysmocom sysmoBTS" }, - { 0, NULL } -}; - -struct gsm_bts_trx *gsm_bts_trx_by_nr(struct gsm_bts *bts, int nr) -{ - struct gsm_bts_trx *trx; - - llist_for_each_entry(trx, &bts->trx_list, list) { - if (trx->nr == nr) - return trx; - } - return NULL; -} - -/* Search for a BTS in the given Location Area; optionally start searching - * with start_bts (for continuing to search after the first result) */ -struct gsm_bts *gsm_bts_by_lac(struct gsm_network *net, unsigned int lac, - struct gsm_bts *start_bts) -{ - int i; - struct gsm_bts *bts; - int skip = 0; - - if (start_bts) - skip = 1; - - for (i = 0; i < net->num_bts; i++) { - bts = gsm_bts_num(net, i); - - if (skip) { - if (start_bts == bts) - skip = 0; - continue; - } - - if (lac == GSM_LAC_RESERVED_ALL_BTS || bts->location_area_code == lac) - return bts; - } - return NULL; -} - -static const struct value_string auth_policy_names[] = { - { GSM_AUTH_POLICY_CLOSED, "closed" }, - { GSM_AUTH_POLICY_ACCEPT_ALL, "accept-all" }, - { GSM_AUTH_POLICY_TOKEN, "token" }, - { GSM_AUTH_POLICY_REGEXP, "regexp" }, - { 0, NULL } -}; - -enum gsm_auth_policy gsm_auth_policy_parse(const char *arg) -{ - return get_string_value(auth_policy_names, arg); -} - -const char *gsm_auth_policy_name(enum gsm_auth_policy policy) -{ - return get_value_string(auth_policy_names, policy); -} - -static const struct value_string rrlp_mode_names[] = { - { RRLP_MODE_NONE, "none" }, - { RRLP_MODE_MS_BASED, "ms-based" }, - { RRLP_MODE_MS_PREF, "ms-preferred" }, - { RRLP_MODE_ASS_PREF, "ass-preferred" }, - { 0, NULL } -}; - -enum rrlp_mode rrlp_mode_parse(const char *arg) -{ - return get_string_value(rrlp_mode_names, arg); -} - -const char *rrlp_mode_name(enum rrlp_mode mode) -{ - return get_value_string(rrlp_mode_names, mode); -} - -static const struct value_string bts_gprs_mode_names[] = { - { BTS_GPRS_NONE, "none" }, - { BTS_GPRS_GPRS, "gprs" }, - { BTS_GPRS_EGPRS, "egprs" }, - { 0, NULL } -}; - -enum bts_gprs_mode bts_gprs_mode_parse(const char *arg, int *valid) -{ - int rc; - - rc = get_string_value(bts_gprs_mode_names, arg); - if (valid) - *valid = rc != -EINVAL; - return rc; -} - -const char *bts_gprs_mode_name(enum bts_gprs_mode mode) -{ - return get_value_string(bts_gprs_mode_names, mode); -} - -int bts_gprs_mode_is_compat(struct gsm_bts *bts, enum bts_gprs_mode mode) -{ - if (mode != BTS_GPRS_NONE && - !gsm_btsmodel_has_feature(bts->model, BTS_FEAT_GPRS)) { - return 0; - } - if (mode == BTS_GPRS_EGPRS && - !gsm_btsmodel_has_feature(bts->model, BTS_FEAT_EGPRS)) { - return 0; - } - - return 1; -} - -struct gsm_meas_rep *lchan_next_meas_rep(struct gsm_lchan *lchan) -{ - struct gsm_meas_rep *meas_rep; - - meas_rep = &lchan->meas_rep[lchan->meas_rep_idx]; - memset(meas_rep, 0, sizeof(*meas_rep)); - meas_rep->lchan = lchan; - lchan->meas_rep_idx = (lchan->meas_rep_idx + 1) - % ARRAY_SIZE(lchan->meas_rep); - - return meas_rep; -} - -int gsm_btsmodel_set_feature(struct gsm_bts_model *model, enum gsm_bts_features feat) -{ - OSMO_ASSERT(_NUM_BTS_FEAT < MAX_BTS_FEATURES); - return bitvec_set_bit_pos(&model->features, feat, 1); -} - -bool gsm_btsmodel_has_feature(struct gsm_bts_model *model, enum gsm_bts_features feat) -{ - OSMO_ASSERT(_NUM_BTS_FEAT < MAX_BTS_FEATURES); - return bitvec_get_bit_pos(&model->features, feat); -} - -int gsm_set_bts_type(struct gsm_bts *bts, enum gsm_bts_type type) -{ - struct gsm_bts_model *model; - - model = bts_model_find(type); - if (!model) - return -EINVAL; - - bts->type = type; - bts->model = model; - - if (model->start && !model->started) { - int ret = model->start(bts->network); - if (ret < 0) - return ret; - - model->started = true; - } - - switch (bts->type) { - case GSM_BTS_TYPE_NANOBTS: - case GSM_BTS_TYPE_OSMOBTS: - /* Set the default OML Stream ID to 0xff */ - bts->oml_tei = 0xff; - bts->c0->nominal_power = 23; - break; - case GSM_BTS_TYPE_RBS2000: - INIT_LLIST_HEAD(&bts->rbs2000.is.conn_groups); - INIT_LLIST_HEAD(&bts->rbs2000.con.conn_groups); - break; - case GSM_BTS_TYPE_BS11: - case GSM_BTS_TYPE_UNKNOWN: - case GSM_BTS_TYPE_NOKIA_SITE: - /* Set default BTS reset timer */ - bts->nokia.bts_reset_timer_cnf = 15; - case _NUM_GSM_BTS_TYPE: - break; - } - - return 0; -} - -struct gsm_bts *gsm_bts_alloc_register(struct gsm_network *net, enum gsm_bts_type type, - uint8_t bsic) -{ - struct gsm_bts_model *model = bts_model_find(type); - struct gsm_bts *bts; - - if (!model && type != GSM_BTS_TYPE_UNKNOWN) - return NULL; - - bts = gsm_bts_alloc(net); - if (!bts) - return NULL; - - bts->network = net; - bts->nr = net->num_bts++; - bts->type = type; - bts->model = model; - bts->bsic = bsic; - bts->dtxu = GSM48_DTX_SHALL_NOT_BE_USED; - bts->dtxd = false; - bts->gprs.ctrl_ack_type_use_block = true; /* use RLC/MAC control block */ - bts->neigh_list_manual_mode = 0; - bts->si_common.cell_sel_par.cell_resel_hyst = 2; /* 4 dB */ - bts->si_common.cell_sel_par.rxlev_acc_min = 0; - bts->si_common.si2quater_neigh_list.arfcn = bts->si_common.data.earfcn_list; - bts->si_common.si2quater_neigh_list.meas_bw = bts->si_common.data.meas_bw_list; - bts->si_common.si2quater_neigh_list.length = MAX_EARFCN_LIST; - bts->si_common.si2quater_neigh_list.thresh_hi = 0; - osmo_earfcn_init(&bts->si_common.si2quater_neigh_list); - bts->si_common.neigh_list.data = bts->si_common.data.neigh_list; - bts->si_common.neigh_list.data_len = - sizeof(bts->si_common.data.neigh_list); - bts->si_common.si5_neigh_list.data = bts->si_common.data.si5_neigh_list; - bts->si_common.si5_neigh_list.data_len = - sizeof(bts->si_common.data.si5_neigh_list); - bts->si_common.cell_alloc.data = bts->si_common.data.cell_alloc; - bts->si_common.cell_alloc.data_len = - sizeof(bts->si_common.data.cell_alloc); - bts->si_common.rach_control.re = 1; /* no re-establishment */ - bts->si_common.rach_control.tx_integer = 9; /* 12 slots spread - 217/115 slots delay */ - bts->si_common.rach_control.max_trans = 3; /* 7 retransmissions */ - bts->si_common.rach_control.t2 = 4; /* no emergency calls */ - bts->si_common.chan_desc.att = 1; /* attachment required */ - bts->si_common.chan_desc.bs_pa_mfrms = RSL_BS_PA_MFRMS_5; /* paging frames */ - bts->si_common.chan_desc.bs_ag_blks_res = 1; /* reserved AGCH blocks */ - bts->si_common.chan_desc.t3212 = 5; /* Use 30 min periodic update interval as sane default */ - gsm_bts_set_radio_link_timeout(bts, 32); /* Use RADIO LINK TIMEOUT of 32 */ - - llist_add_tail(&bts->list, &net->bts_list); - - INIT_LLIST_HEAD(&bts->abis_queue); - - INIT_LLIST_HEAD(&bts->loc_list); - - return bts; -} - -void gprs_ra_id_by_bts(struct gprs_ra_id *raid, struct gsm_bts *bts) -{ - raid->mcc = bts->network->country_code; - raid->mnc = bts->network->network_code; - raid->lac = bts->location_area_code; - raid->rac = bts->gprs.rac; -} - -int gsm48_ra_id_by_bts(uint8_t *buf, struct gsm_bts *bts) -{ - struct gprs_ra_id raid; - - gprs_ra_id_by_bts(&raid, bts); - - return gsm48_construct_ra(buf, &raid); -} - -int gsm_parse_reg(void *ctx, regex_t *reg, char **str, int argc, const char **argv) -{ - int ret; - - ret = 0; - if (*str) { - talloc_free(*str); - *str = NULL; - } - regfree(reg); - - if (argc > 0) { - *str = talloc_strdup(ctx, argv[0]); - ret = regcomp(reg, argv[0], 0); - - /* handle compilation failures */ - if (ret != 0) { - talloc_free(*str); - *str = NULL; - } - } - - return ret; -} - -/* Assume there are only 256 possible bts */ -osmo_static_assert(sizeof(((struct gsm_bts *) 0)->nr) == 1, _bts_nr_is_256); -static void depends_calc_index_bit(int bts_nr, int *idx, int *bit) -{ - *idx = bts_nr / (8 * 4); - *bit = bts_nr % (8 * 4); -} - -void bts_depend_mark(struct gsm_bts *bts, int dep) -{ - int idx, bit; - depends_calc_index_bit(dep, &idx, &bit); - - bts->depends_on[idx] |= 1 << bit; -} - -void bts_depend_clear(struct gsm_bts *bts, int dep) -{ - int idx, bit; - depends_calc_index_bit(dep, &idx, &bit); - - bts->depends_on[idx] &= ~(1 << bit); -} - -int bts_depend_is_depedency(struct gsm_bts *base, struct gsm_bts *other) -{ - int idx, bit; - depends_calc_index_bit(other->nr, &idx, &bit); - - /* Check if there is a depends bit */ - return (base->depends_on[idx] & (1 << bit)) > 0; -} - -static int bts_is_online(struct gsm_bts *bts) -{ - /* TODO: support E1 BTS too */ - if (!is_ipaccess_bts(bts)) - return 1; - - if (!bts->oml_link) - return 0; - - return bts->mo.nm_state.operational == NM_OPSTATE_ENABLED; -} - -int bts_depend_check(struct gsm_bts *bts) -{ - struct gsm_bts *other_bts; - - llist_for_each_entry(other_bts, &bts->network->bts_list, list) { - if (!bts_depend_is_depedency(bts, other_bts)) - continue; - if (bts_is_online(other_bts)) - continue; - return 0; - } - return 1; -} - -/* get the radio link timeout (based on SACCH decode errors, according - * to algorithm specified in TS 05.08 section 5.2. A value of -1 - * indicates we should use an infinitely long timeout, which only works - * with OsmoBTS as the BTS implementation */ -int gsm_bts_get_radio_link_timeout(const struct gsm_bts *bts) -{ - const struct gsm48_cell_options *cell_options = &bts->si_common.cell_options; - - if (bts->infinite_radio_link_timeout) - return -1; - else { - /* Encoding as per Table 10.5.21 of TS 04.08 */ - return (cell_options->radio_link_timeout + 1) << 2; - } -} - -/* set the radio link timeout (based on SACCH decode errors, according - * to algorithm specified in TS 05.08 Section 5.2. A value of -1 - * indicates we should use an infinitely long timeout, which only works - * with OsmoBTS as the BTS implementation */ -void gsm_bts_set_radio_link_timeout(struct gsm_bts *bts, int value) -{ - struct gsm48_cell_options *cell_options = &bts->si_common.cell_options; - - if (value < 0) - bts->infinite_radio_link_timeout = true; - else { - bts->infinite_radio_link_timeout = false; - /* Encoding as per Table 10.5.21 of TS 04.08 */ - if (value < 4) - value = 4; - if (value > 64) - value = 64; - cell_options->radio_link_timeout = (value >> 2) - 1; - } -} diff --git a/openbsc/src/libcommon/gsm_data_shared.c b/openbsc/src/libcommon/gsm_data_shared.c deleted file mode 100644 index 89926364..00000000 --- a/openbsc/src/libcommon/gsm_data_shared.c +++ /dev/null @@ -1,849 +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 - -void gsm_abis_mo_reset(struct gsm_abis_mo *mo) -{ - mo->nm_state.operational = NM_OPSTATE_NULL; - mo->nm_state.availability = NM_AVSTATE_POWER_OFF; -} - -static void gsm_mo_init(struct gsm_abis_mo *mo, struct gsm_bts *bts, - uint8_t obj_class, uint8_t p1, uint8_t p2, uint8_t p3) -{ - mo->bts = bts; - mo->obj_class = obj_class; - mo->obj_inst.bts_nr = p1; - mo->obj_inst.trx_nr = p2; - mo->obj_inst.ts_nr = p3; - gsm_abis_mo_reset(mo); -} - -const struct value_string bts_attribute_names[] = { - OSMO_VALUE_STRING(BTS_TYPE_VARIANT), - OSMO_VALUE_STRING(BTS_SUB_MODEL), - OSMO_VALUE_STRING(TRX_PHY_VERSION), - { 0, NULL } -}; - -enum bts_attribute str2btsattr(const char *s) -{ - return get_string_value(bts_attribute_names, s); -} - -const char *btsatttr2str(enum bts_attribute v) -{ - return get_value_string(bts_attribute_names, v); -} - -const struct value_string osmo_bts_variant_names[_NUM_BTS_VARIANT + 1] = { - { BTS_UNKNOWN, "unknown" }, - { BTS_OSMO_LITECELL15, "osmo-bts-lc15" }, - { BTS_OSMO_OCTPHY, "osmo-bts-octphy" }, - { BTS_OSMO_SYSMO, "osmo-bts-sysmo" }, - { BTS_OSMO_TRX, "omso-bts-trx" }, - { 0, NULL } -}; - -enum gsm_bts_type_variant str2btsvariant(const char *arg) -{ - return get_string_value(osmo_bts_variant_names, arg); -} - -const char *btsvariant2str(enum gsm_bts_type_variant v) -{ - return get_value_string(osmo_bts_variant_names, v); -} - -const struct value_string bts_type_names[_NUM_GSM_BTS_TYPE + 1] = { - { GSM_BTS_TYPE_UNKNOWN, "unknown" }, - { GSM_BTS_TYPE_BS11, "bs11" }, - { GSM_BTS_TYPE_NANOBTS, "nanobts" }, - { GSM_BTS_TYPE_RBS2000, "rbs2000" }, - { GSM_BTS_TYPE_NOKIA_SITE, "nokia_site" }, - { GSM_BTS_TYPE_OSMOBTS, "sysmobts" }, - { 0, NULL } -}; - -enum gsm_bts_type str2btstype(const char *arg) -{ - return get_string_value(bts_type_names, arg); -} - -const char *btstype2str(enum gsm_bts_type type) -{ - return get_value_string(bts_type_names, type); -} - -const struct value_string gsm_bts_features_descs[] = { - { BTS_FEAT_HSCSD, "HSCSD" }, - { BTS_FEAT_GPRS, "GPRS" }, - { BTS_FEAT_EGPRS, "EGPRS" }, - { BTS_FEAT_ECSD, "ECSD" }, - { BTS_FEAT_HOPPING, "Frequency Hopping" }, - { BTS_FEAT_MULTI_TSC, "Multi-TSC" }, - { BTS_FEAT_OML_ALERTS, "OML Alerts" }, - { BTS_FEAT_AGCH_PCH_PROP, "AGCH/PCH proportional allocation" }, - { BTS_FEAT_CBCH, "CBCH" }, - { 0, NULL } -}; - -const struct value_string gsm_chreq_descs[] = { - { GSM_CHREQ_REASON_EMERG, "emergency call" }, - { GSM_CHREQ_REASON_PAG, "answer to paging" }, - { GSM_CHREQ_REASON_CALL, "call re-establishment" }, - { GSM_CHREQ_REASON_LOCATION_UPD,"Location updating" }, - { GSM_CHREQ_REASON_PDCH, "one phase packet access" }, - { GSM_CHREQ_REASON_OTHER, "other" }, - { 0, NULL } -}; - -const struct value_string gsm_pchant_names[13] = { - { GSM_PCHAN_NONE, "NONE" }, - { GSM_PCHAN_CCCH, "CCCH" }, - { GSM_PCHAN_CCCH_SDCCH4,"CCCH+SDCCH4" }, - { GSM_PCHAN_TCH_F, "TCH/F" }, - { GSM_PCHAN_TCH_H, "TCH/H" }, - { GSM_PCHAN_SDCCH8_SACCH8C, "SDCCH8" }, - { GSM_PCHAN_PDCH, "PDCH" }, - { GSM_PCHAN_TCH_F_PDCH, "TCH/F_PDCH" }, - { GSM_PCHAN_UNKNOWN, "UNKNOWN" }, - { GSM_PCHAN_CCCH_SDCCH4_CBCH, "CCCH+SDCCH4+CBCH" }, - { GSM_PCHAN_SDCCH8_SACCH8C_CBCH, "SDCCH8+CBCH" }, - { GSM_PCHAN_TCH_F_TCH_H_PDCH, "TCH/F_TCH/H_PDCH" }, - { 0, NULL } -}; - -const struct value_string gsm_pchant_descs[13] = { - { GSM_PCHAN_NONE, "Physical Channel not configured" }, - { GSM_PCHAN_CCCH, "FCCH + SCH + BCCH + CCCH (Comb. IV)" }, - { GSM_PCHAN_CCCH_SDCCH4, - "FCCH + SCH + BCCH + CCCH + 4 SDCCH + 2 SACCH (Comb. V)" }, - { GSM_PCHAN_TCH_F, "TCH/F + FACCH/F + SACCH (Comb. I)" }, - { GSM_PCHAN_TCH_H, "2 TCH/H + 2 FACCH/H + 2 SACCH (Comb. II)" }, - { GSM_PCHAN_SDCCH8_SACCH8C, "8 SDCCH + 4 SACCH (Comb. VII)" }, - { GSM_PCHAN_PDCH, "Packet Data Channel for GPRS/EDGE" }, - { GSM_PCHAN_TCH_F_PDCH, "Dynamic TCH/F or GPRS PDCH" }, - { GSM_PCHAN_UNKNOWN, "Unknown / Unsupported channel combination" }, - { GSM_PCHAN_CCCH_SDCCH4_CBCH, "FCCH + SCH + BCCH + CCCH + CBCH + 3 SDCCH + 2 SACCH (Comb. V)" }, - { GSM_PCHAN_SDCCH8_SACCH8C_CBCH, "7 SDCCH + 4 SACCH + CBCH (Comb. VII)" }, - { GSM_PCHAN_TCH_F_TCH_H_PDCH, "Dynamic TCH/F or TCH/H or GPRS PDCH" }, - { 0, NULL } -}; - -const char *gsm_pchan_name(enum gsm_phys_chan_config c) -{ - return get_value_string(gsm_pchant_names, c); -} - -enum gsm_phys_chan_config gsm_pchan_parse(const char *name) -{ - return get_string_value(gsm_pchant_names, name); -} - -/* TODO: move to libosmocore, next to gsm_chan_t_names? */ -const char *gsm_lchant_name(enum gsm_chan_t c) -{ - return get_value_string(gsm_chan_t_names, c); -} - -static const struct value_string lchan_s_names[] = { - { LCHAN_S_NONE, "NONE" }, - { LCHAN_S_ACT_REQ, "ACTIVATION REQUESTED" }, - { LCHAN_S_ACTIVE, "ACTIVE" }, - { LCHAN_S_INACTIVE, "INACTIVE" }, - { LCHAN_S_REL_REQ, "RELEASE REQUESTED" }, - { LCHAN_S_REL_ERR, "RELEASE DUE ERROR" }, - { LCHAN_S_BROKEN, "BROKEN UNUSABLE" }, - { 0, NULL } -}; - -const char *gsm_lchans_name(enum gsm_lchan_state s) -{ - return get_value_string(lchan_s_names, s); -} - -static const struct value_string chreq_names[] = { - { GSM_CHREQ_REASON_EMERG, "EMERGENCY" }, - { GSM_CHREQ_REASON_PAG, "PAGING" }, - { GSM_CHREQ_REASON_CALL, "CALL" }, - { GSM_CHREQ_REASON_LOCATION_UPD,"LOCATION_UPDATE" }, - { GSM_CHREQ_REASON_OTHER, "OTHER" }, - { 0, NULL } -}; - -const char *gsm_chreq_name(enum gsm_chreq_reason_t c) -{ - return get_value_string(chreq_names, c); -} - -struct gsm_bts *gsm_bts_num(struct gsm_network *net, int num) -{ - struct gsm_bts *bts; - - if (num >= net->num_bts) - return NULL; - - llist_for_each_entry(bts, &net->bts_list, list) { - if (bts->nr == num) - return bts; - } - - return NULL; -} - -struct gsm_bts_trx *gsm_bts_trx_alloc(struct gsm_bts *bts) -{ - struct gsm_bts_trx *trx = talloc_zero(bts, struct gsm_bts_trx); - int k; - - if (!trx) - return NULL; - - trx->bts = bts; - trx->nr = bts->num_trx++; - trx->mo.nm_state.administrative = NM_STATE_UNLOCKED; - - gsm_mo_init(&trx->mo, bts, NM_OC_RADIO_CARRIER, - bts->nr, trx->nr, 0xff); - gsm_mo_init(&trx->bb_transc.mo, bts, NM_OC_BASEB_TRANSC, - bts->nr, trx->nr, 0xff); - - for (k = 0; k < TRX_NR_TS; k++) { - struct gsm_bts_trx_ts *ts = &trx->ts[k]; - int l; - - ts->trx = trx; - ts->nr = k; - ts->pchan = GSM_PCHAN_NONE; - ts->dyn.pchan_is = GSM_PCHAN_NONE; - ts->dyn.pchan_want = GSM_PCHAN_NONE; - ts->tsc = -1; - - gsm_mo_init(&ts->mo, bts, NM_OC_CHANNEL, - bts->nr, trx->nr, ts->nr); - - ts->hopping.arfcns.data_len = sizeof(ts->hopping.arfcns_data); - ts->hopping.arfcns.data = ts->hopping.arfcns_data; - ts->hopping.ma.data_len = sizeof(ts->hopping.ma_data); - ts->hopping.ma.data = ts->hopping.ma_data; - - for (l = 0; l < TS_MAX_LCHAN; l++) { - struct gsm_lchan *lchan; - char *name; - lchan = &ts->lchan[l]; - - lchan->ts = ts; - lchan->nr = l; - lchan->type = GSM_LCHAN_NONE; - - name = gsm_lchan_name_compute(lchan); - lchan->name = talloc_strdup(trx, name); -#ifndef ROLE_BSC - INIT_LLIST_HEAD(&lchan->sapi_cmds); -#endif - } - } - - if (trx->nr != 0) - trx->nominal_power = bts->c0->nominal_power; - - llist_add_tail(&trx->list, &bts->trx_list); - - return trx; -} - - -static const uint8_t bts_nse_timer_default[] = { 3, 3, 3, 3, 30, 3, 10 }; -static const uint8_t bts_cell_timer_default[] = - { 3, 3, 3, 3, 3, 10, 3, 10, 3, 10, 3 }; -static const struct gprs_rlc_cfg rlc_cfg_default = { - .parameter = { - [RLC_T3142] = 20, - [RLC_T3169] = 5, - [RLC_T3191] = 5, - [RLC_T3193] = 160, /* 10ms */ - [RLC_T3195] = 5, - [RLC_N3101] = 10, - [RLC_N3103] = 4, - [RLC_N3105] = 8, - [CV_COUNTDOWN] = 15, - [T_DL_TBF_EXT] = 250 * 10, /* ms */ - [T_UL_TBF_EXT] = 250 * 10, /* ms */ - }, - .paging = { - .repeat_time = 5 * 50, /* ms */ - .repeat_count = 3, - }, - .cs_mask = 0x1fff, - .initial_cs = 2, - .initial_mcs = 6, -}; - -struct gsm_bts *gsm_bts_alloc(void *ctx) -{ - struct gsm_bts *bts = talloc_zero(ctx, struct gsm_bts); - int i; - - if (!bts) - return NULL; - - bts->num_trx = 0; - INIT_LLIST_HEAD(&bts->trx_list); - bts->ms_max_power = 15; /* dBm */ - - gsm_mo_init(&bts->mo, bts, NM_OC_BTS, - bts->nr, 0xff, 0xff); - gsm_mo_init(&bts->site_mgr.mo, bts, NM_OC_SITE_MANAGER, - 0xff, 0xff, 0xff); - - for (i = 0; i < ARRAY_SIZE(bts->gprs.nsvc); i++) { - bts->gprs.nsvc[i].bts = bts; - bts->gprs.nsvc[i].id = i; - gsm_mo_init(&bts->gprs.nsvc[i].mo, bts, NM_OC_GPRS_NSVC, - bts->nr, i, 0xff); - } - memcpy(&bts->gprs.nse.timer, bts_nse_timer_default, - sizeof(bts->gprs.nse.timer)); - gsm_mo_init(&bts->gprs.nse.mo, bts, NM_OC_GPRS_NSE, - bts->nr, 0xff, 0xff); - memcpy(&bts->gprs.cell.timer, bts_cell_timer_default, - sizeof(bts->gprs.cell.timer)); - gsm_mo_init(&bts->gprs.cell.mo, bts, NM_OC_GPRS_CELL, - bts->nr, 0xff, 0xff); - memcpy(&bts->gprs.cell.rlc_cfg, &rlc_cfg_default, - sizeof(bts->gprs.cell.rlc_cfg)); - - /* create our primary TRX */ - bts->c0 = gsm_bts_trx_alloc(bts); - if (!bts->c0) { - talloc_free(bts); - return NULL; - } - bts->c0->ts[0].pchan = GSM_PCHAN_CCCH_SDCCH4; - - bts->rach_b_thresh = -1; - bts->rach_ldavg_slots = -1; - bts->paging.free_chans_need = -1; - bts->features.data = &bts->_features_data[0]; - bts->features.data_len = sizeof(bts->_features_data); - - /* si handling */ - bts->bcch_change_mark = 1; - - return bts; -} - -/* reset the state of all MO in the BTS */ -void gsm_bts_mo_reset(struct gsm_bts *bts) -{ - struct gsm_bts_trx *trx; - unsigned int i; - - gsm_abis_mo_reset(&bts->mo); - gsm_abis_mo_reset(&bts->site_mgr.mo); - for (i = 0; i < ARRAY_SIZE(bts->gprs.nsvc); i++) - gsm_abis_mo_reset(&bts->gprs.nsvc[i].mo); - gsm_abis_mo_reset(&bts->gprs.nse.mo); - gsm_abis_mo_reset(&bts->gprs.cell.mo); - - llist_for_each_entry(trx, &bts->trx_list, list) { - gsm_abis_mo_reset(&trx->mo); - gsm_abis_mo_reset(&trx->bb_transc.mo); - - for (i = 0; i < ARRAY_SIZE(trx->ts); i++) { - struct gsm_bts_trx_ts *ts = &trx->ts[i]; - gsm_abis_mo_reset(&ts->mo); - } - } -} - -struct gsm_bts_trx *gsm_bts_trx_num(const struct gsm_bts *bts, int num) -{ - struct gsm_bts_trx *trx; - - if (num >= bts->num_trx) - return NULL; - - llist_for_each_entry(trx, &bts->trx_list, list) { - if (trx->nr == num) - return trx; - } - - return NULL; -} - -static char ts2str[255]; - -char *gsm_trx_name(const struct gsm_bts_trx *trx) -{ - if (!trx) - snprintf(ts2str, sizeof(ts2str), "(trx=NULL)"); - else - snprintf(ts2str, sizeof(ts2str), "(bts=%d,trx=%d)", - trx->bts->nr, trx->nr); - - return ts2str; -} - - -char *gsm_ts_name(const struct gsm_bts_trx_ts *ts) -{ - snprintf(ts2str, sizeof(ts2str), "(bts=%d,trx=%d,ts=%d)", - ts->trx->bts->nr, ts->trx->nr, ts->nr); - - return ts2str; -} - -/*! Log timeslot number with full pchan information */ -char *gsm_ts_and_pchan_name(const struct gsm_bts_trx_ts *ts) -{ - switch (ts->pchan) { - case GSM_PCHAN_TCH_F_TCH_H_PDCH: - if (ts->dyn.pchan_is == ts->dyn.pchan_want) - snprintf(ts2str, sizeof(ts2str), - "(bts=%d,trx=%d,ts=%d,pchan=%s as %s)", - ts->trx->bts->nr, ts->trx->nr, ts->nr, - gsm_pchan_name(ts->pchan), - gsm_pchan_name(ts->dyn.pchan_is)); - else - snprintf(ts2str, sizeof(ts2str), - "(bts=%d,trx=%d,ts=%d,pchan=%s" - " switching %s -> %s)", - ts->trx->bts->nr, ts->trx->nr, ts->nr, - gsm_pchan_name(ts->pchan), - gsm_pchan_name(ts->dyn.pchan_is), - gsm_pchan_name(ts->dyn.pchan_want)); - break; - case GSM_PCHAN_TCH_F_PDCH: - if ((ts->flags & TS_F_PDCH_PENDING_MASK) == 0) - snprintf(ts2str, sizeof(ts2str), - "(bts=%d,trx=%d,ts=%d,pchan=%s as %s)", - ts->trx->bts->nr, ts->trx->nr, ts->nr, - gsm_pchan_name(ts->pchan), - (ts->flags & TS_F_PDCH_ACTIVE)? "PDCH" - : "TCH/F"); - else - snprintf(ts2str, sizeof(ts2str), - "(bts=%d,trx=%d,ts=%d,pchan=%s" - " switching %s -> %s)", - ts->trx->bts->nr, ts->trx->nr, ts->nr, - gsm_pchan_name(ts->pchan), - (ts->flags & TS_F_PDCH_ACTIVE)? "PDCH" - : "TCH/F", - (ts->flags & TS_F_PDCH_ACT_PENDING)? "PDCH" - : "TCH/F"); - break; - default: - snprintf(ts2str, sizeof(ts2str), "(bts=%d,trx=%d,ts=%d,pchan=%s)", - ts->trx->bts->nr, ts->trx->nr, ts->nr, - gsm_pchan_name(ts->pchan)); - break; - } - - return ts2str; -} - -char *gsm_lchan_name_compute(const struct gsm_lchan *lchan) -{ - struct gsm_bts_trx_ts *ts = lchan->ts; - - snprintf(ts2str, sizeof(ts2str), "(bts=%d,trx=%d,ts=%d,ss=%d)", - ts->trx->bts->nr, ts->trx->nr, ts->nr, lchan->nr); - - return ts2str; -} - -/* obtain the MO structure for a given object instance */ -struct gsm_abis_mo * -gsm_objclass2mo(struct gsm_bts *bts, uint8_t obj_class, - const struct abis_om_obj_inst *obj_inst) -{ - struct gsm_bts_trx *trx; - struct gsm_abis_mo *mo = NULL; - - switch (obj_class) { - case NM_OC_BTS: - mo = &bts->mo; - break; - case NM_OC_RADIO_CARRIER: - if (obj_inst->trx_nr >= bts->num_trx) { - return NULL; - } - trx = gsm_bts_trx_num(bts, obj_inst->trx_nr); - mo = &trx->mo; - break; - case NM_OC_BASEB_TRANSC: - if (obj_inst->trx_nr >= bts->num_trx) { - return NULL; - } - trx = gsm_bts_trx_num(bts, obj_inst->trx_nr); - mo = &trx->bb_transc.mo; - break; - case NM_OC_CHANNEL: - if (obj_inst->trx_nr >= bts->num_trx) { - return NULL; - } - trx = gsm_bts_trx_num(bts, obj_inst->trx_nr); - if (obj_inst->ts_nr >= TRX_NR_TS) - return NULL; - mo = &trx->ts[obj_inst->ts_nr].mo; - break; - case NM_OC_SITE_MANAGER: - mo = &bts->site_mgr.mo; - break; - case NM_OC_BS11: - switch (obj_inst->bts_nr) { - case BS11_OBJ_CCLK: - mo = &bts->bs11.cclk.mo; - break; - case BS11_OBJ_BBSIG: - if (obj_inst->ts_nr > bts->num_trx) - return NULL; - trx = gsm_bts_trx_num(bts, obj_inst->trx_nr); - mo = &trx->bs11.bbsig.mo; - break; - case BS11_OBJ_PA: - if (obj_inst->ts_nr > bts->num_trx) - return NULL; - trx = gsm_bts_trx_num(bts, obj_inst->trx_nr); - mo = &trx->bs11.pa.mo; - break; - default: - return NULL; - } - break; - case NM_OC_BS11_RACK: - mo = &bts->bs11.rack.mo; - break; - case NM_OC_BS11_ENVABTSE: - if (obj_inst->trx_nr >= ARRAY_SIZE(bts->bs11.envabtse)) - return NULL; - mo = &bts->bs11.envabtse[obj_inst->trx_nr].mo; - break; - case NM_OC_GPRS_NSE: - mo = &bts->gprs.nse.mo; - break; - case NM_OC_GPRS_CELL: - mo = &bts->gprs.cell.mo; - break; - case NM_OC_GPRS_NSVC: - if (obj_inst->trx_nr >= ARRAY_SIZE(bts->gprs.nsvc)) - return NULL; - mo = &bts->gprs.nsvc[obj_inst->trx_nr].mo; - break; - } - return mo; -} - -/* obtain the gsm_nm_state data structure for a given object instance */ -struct gsm_nm_state * -gsm_objclass2nmstate(struct gsm_bts *bts, uint8_t obj_class, - const struct abis_om_obj_inst *obj_inst) -{ - struct gsm_abis_mo *mo; - - mo = gsm_objclass2mo(bts, obj_class, obj_inst); - if (!mo) - return NULL; - - return &mo->nm_state; -} - -/* obtain the in-memory data structure of a given object instance */ -void * -gsm_objclass2obj(struct gsm_bts *bts, uint8_t obj_class, - const struct abis_om_obj_inst *obj_inst) -{ - struct gsm_bts_trx *trx; - void *obj = NULL; - - switch (obj_class) { - case NM_OC_BTS: - obj = bts; - break; - case NM_OC_RADIO_CARRIER: - if (obj_inst->trx_nr >= bts->num_trx) { - return NULL; - } - trx = gsm_bts_trx_num(bts, obj_inst->trx_nr); - obj = trx; - break; - case NM_OC_BASEB_TRANSC: - if (obj_inst->trx_nr >= bts->num_trx) { - return NULL; - } - trx = gsm_bts_trx_num(bts, obj_inst->trx_nr); - obj = &trx->bb_transc; - break; - case NM_OC_CHANNEL: - if (obj_inst->trx_nr >= bts->num_trx) { - return NULL; - } - trx = gsm_bts_trx_num(bts, obj_inst->trx_nr); - if (obj_inst->ts_nr >= TRX_NR_TS) - return NULL; - obj = &trx->ts[obj_inst->ts_nr]; - break; - case NM_OC_SITE_MANAGER: - obj = &bts->site_mgr; - break; - case NM_OC_GPRS_NSE: - obj = &bts->gprs.nse; - break; - case NM_OC_GPRS_CELL: - obj = &bts->gprs.cell; - break; - case NM_OC_GPRS_NSVC: - if (obj_inst->trx_nr >= ARRAY_SIZE(bts->gprs.nsvc)) - return NULL; - obj = &bts->gprs.nsvc[obj_inst->trx_nr]; - break; - } - return obj; -} - -/* See Table 10.5.25 of GSM04.08 */ -uint8_t gsm_pchan2chan_nr(enum gsm_phys_chan_config pchan, - uint8_t ts_nr, uint8_t lchan_nr) -{ - uint8_t cbits, chan_nr; - - switch (pchan) { - case GSM_PCHAN_TCH_F: - case GSM_PCHAN_PDCH: - case GSM_PCHAN_TCH_F_PDCH: - OSMO_ASSERT(lchan_nr == 0); - cbits = 0x01; - break; - case GSM_PCHAN_TCH_H: - OSMO_ASSERT(lchan_nr < 2); - cbits = 0x02; - cbits += lchan_nr; - break; - case GSM_PCHAN_CCCH_SDCCH4: - case GSM_PCHAN_CCCH_SDCCH4_CBCH: - /* - * As a special hack for BCCH, lchan_nr == 4 may be passed - * here. This should never be sent in an RSL message. - * See osmo-bts-xxx/oml.c:opstart_compl(). - */ - if (lchan_nr == CCCH_LCHAN) - chan_nr = 0; - else - OSMO_ASSERT(lchan_nr < 4); - cbits = 0x04; - cbits += lchan_nr; - break; - case GSM_PCHAN_SDCCH8_SACCH8C: - case GSM_PCHAN_SDCCH8_SACCH8C_CBCH: - OSMO_ASSERT(lchan_nr < 8); - cbits = 0x08; - cbits += lchan_nr; - break; - default: - case GSM_PCHAN_CCCH: -#ifdef ROLE_BSC - OSMO_ASSERT(lchan_nr == 0); -#else - /* - * FIXME: On octphy and litecell, we hit above assertion (see - * Max's comment at https://gerrit.osmocom.org/589 ); disabled - * for BTS until this is clarified; remove the #ifdef when it - * is fixed. - */ -#warning "fix caller that passes lchan_nr != 0" -#endif - cbits = 0x10; - break; - } - - chan_nr = (cbits << 3) | (ts_nr & 0x7); - - return chan_nr; -} - -uint8_t gsm_lchan2chan_nr(const struct gsm_lchan *lchan) -{ - enum gsm_phys_chan_config pchan = lchan->ts->pchan; - if (pchan == GSM_PCHAN_TCH_F_TCH_H_PDCH) - return gsm_lchan_as_pchan2chan_nr(lchan, - lchan->ts->dyn.pchan_is); - return gsm_pchan2chan_nr(lchan->ts->pchan, lchan->ts->nr, lchan->nr); -} - -uint8_t gsm_lchan_as_pchan2chan_nr(const struct gsm_lchan *lchan, - enum gsm_phys_chan_config as_pchan) -{ - if (lchan->ts->pchan == GSM_PCHAN_TCH_F_TCH_H_PDCH - && as_pchan == GSM_PCHAN_PDCH) - return RSL_CHAN_OSMO_PDCH | (lchan->ts->nr & ~RSL_CHAN_NR_MASK); - return gsm_pchan2chan_nr(as_pchan, lchan->ts->nr, lchan->nr); -} - -/* return the gsm_lchan for the CBCH (if it exists at all) */ -struct gsm_lchan *gsm_bts_get_cbch(struct gsm_bts *bts) -{ - struct gsm_lchan *lchan = NULL; - struct gsm_bts_trx *trx = bts->c0; - - if (trx->ts[0].pchan == GSM_PCHAN_CCCH_SDCCH4_CBCH) - lchan = &trx->ts[0].lchan[2]; - else { - int i; - for (i = 0; i < 8; i++) { - if (trx->ts[i].pchan == GSM_PCHAN_SDCCH8_SACCH8C_CBCH) { - lchan = &trx->ts[i].lchan[2]; - break; - } - } - } - - return lchan; -} - -/* determine logical channel based on TRX and channel number IE */ -struct gsm_lchan *rsl_lchan_lookup(struct gsm_bts_trx *trx, uint8_t chan_nr, - int *rc) -{ - uint8_t ts_nr = chan_nr & 0x07; - uint8_t cbits = chan_nr >> 3; - uint8_t lch_idx; - struct gsm_bts_trx_ts *ts = &trx->ts[ts_nr]; - bool ok = true; - - if (rc) - *rc = -EINVAL; - - if (cbits == 0x01) { - lch_idx = 0; /* TCH/F */ - if (ts->pchan != GSM_PCHAN_TCH_F && - ts->pchan != GSM_PCHAN_PDCH && - ts->pchan != GSM_PCHAN_TCH_F_PDCH - && !(ts->pchan == GSM_PCHAN_TCH_F_TCH_H_PDCH - && (ts->dyn.pchan_is == GSM_PCHAN_TCH_F - || ts->dyn.pchan_want == GSM_PCHAN_TCH_F))) - ok = false; - } else if ((cbits & 0x1e) == 0x02) { - lch_idx = cbits & 0x1; /* TCH/H */ - if (ts->pchan != GSM_PCHAN_TCH_H - && !(ts->pchan == GSM_PCHAN_TCH_F_TCH_H_PDCH - && (ts->dyn.pchan_is == GSM_PCHAN_TCH_H - || ts->dyn.pchan_want == GSM_PCHAN_TCH_H))) - ok = false; - } else if ((cbits & 0x1c) == 0x04) { - lch_idx = cbits & 0x3; /* SDCCH/4 */ - if (ts->pchan != GSM_PCHAN_CCCH_SDCCH4 && - ts->pchan != GSM_PCHAN_CCCH_SDCCH4_CBCH) - ok = false; - } else if ((cbits & 0x18) == 0x08) { - lch_idx = cbits & 0x7; /* SDCCH/8 */ - if (ts->pchan != GSM_PCHAN_SDCCH8_SACCH8C && - ts->pchan != GSM_PCHAN_SDCCH8_SACCH8C_CBCH) - ok = false; - } else if (cbits == 0x10 || cbits == 0x11 || cbits == 0x12) { - lch_idx = 0; - if (ts->pchan != GSM_PCHAN_CCCH && - ts->pchan != GSM_PCHAN_CCCH_SDCCH4 && - ts->pchan != GSM_PCHAN_CCCH_SDCCH4_CBCH) - ok = false; - /* FIXME: we should not return first sdcch4 !!! */ - } else if ((chan_nr & RSL_CHAN_NR_MASK) == RSL_CHAN_OSMO_PDCH) { - lch_idx = 0; - if (ts->pchan != GSM_PCHAN_TCH_F_TCH_H_PDCH) - ok = false; - } else - return NULL; - - if (rc && ok) - *rc = 0; - - return &ts->lchan[lch_idx]; -} - -static const uint8_t subslots_per_pchan[] = { - [GSM_PCHAN_NONE] = 0, - [GSM_PCHAN_CCCH] = 0, - [GSM_PCHAN_PDCH] = 0, - [GSM_PCHAN_CCCH_SDCCH4] = 4, - [GSM_PCHAN_TCH_F] = 1, - [GSM_PCHAN_TCH_H] = 2, - [GSM_PCHAN_SDCCH8_SACCH8C] = 8, - [GSM_PCHAN_CCCH_SDCCH4_CBCH] = 4, - [GSM_PCHAN_SDCCH8_SACCH8C_CBCH] = 8, - /* - * GSM_PCHAN_TCH_F_PDCH and GSM_PCHAN_TCH_F_TCH_H_PDCH should not be - * part of this, those TS are handled according to their dynamic state. - */ -}; - -/*! Return the actual pchan type, also heeding dynamic TS. */ -enum gsm_phys_chan_config ts_pchan(struct gsm_bts_trx_ts *ts) -{ - switch (ts->pchan) { - case GSM_PCHAN_TCH_F_TCH_H_PDCH: - return ts->dyn.pchan_is; - case GSM_PCHAN_TCH_F_PDCH: - if (ts->flags & TS_F_PDCH_ACTIVE) - return GSM_PCHAN_PDCH; - else - return GSM_PCHAN_TCH_F; - default: - return ts->pchan; - } -} - -/*! According to ts->pchan and possibly ts->dyn_pchan, return the number of - * logical channels available in the timeslot. */ -uint8_t ts_subslots(struct gsm_bts_trx_ts *ts) -{ - return subslots_per_pchan[ts_pchan(ts)]; -} - -static bool pchan_is_tch(enum gsm_phys_chan_config pchan) -{ - switch (pchan) { - case GSM_PCHAN_TCH_F: - case GSM_PCHAN_TCH_H: - return true; - default: - return false; - } -} - -bool ts_is_tch(struct gsm_bts_trx_ts *ts) -{ - return pchan_is_tch(ts_pchan(ts)); -} diff --git a/openbsc/src/libcommon/gsm_subscriber_base.c b/openbsc/src/libcommon/gsm_subscriber_base.c deleted file mode 100644 index 1ecdee5a..00000000 --- a/openbsc/src/libcommon/gsm_subscriber_base.c +++ /dev/null @@ -1,163 +0,0 @@ -/* The concept of a subscriber as seen by the BSC */ - -/* (C) 2008 by Harald Welte - * (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 -#include - -LLIST_HEAD(active_subscribers); -void *tall_subscr_ctx; - -/* for the gsm_subscriber.c */ -struct llist_head *subscr_bsc_active_subscribers(void) -{ - return &active_subscribers; -} - - -char *subscr_name(struct gsm_subscriber *subscr) -{ - if (!subscr) - return "unknown"; - - if (strlen(subscr->name)) - return subscr->name; - - return subscr->imsi; -} - -struct gsm_subscriber *subscr_alloc(void) -{ - struct gsm_subscriber *s; - - s = talloc_zero(tall_subscr_ctx, struct gsm_subscriber); - if (!s) - return NULL; - - llist_add_tail(&s->entry, &active_subscribers); - s->use_count = 1; - s->tmsi = GSM_RESERVED_TMSI; - - INIT_LLIST_HEAD(&s->requests); - - return s; -} - -static void subscr_free(struct gsm_subscriber *subscr) -{ - llist_del(&subscr->entry); - talloc_free(subscr); -} - -void subscr_direct_free(struct gsm_subscriber *subscr) -{ - OSMO_ASSERT(subscr->use_count == 1); - subscr_free(subscr); -} - -struct gsm_subscriber *subscr_get(struct gsm_subscriber *subscr) -{ - subscr->use_count++; - DEBUGP(DREF, "subscr %s usage increases usage to: %d\n", - subscr->extension, subscr->use_count); - return subscr; -} - -struct gsm_subscriber *subscr_put(struct gsm_subscriber *subscr) -{ - subscr->use_count--; - DEBUGP(DREF, "subscr %s usage decreased usage to: %d\n", - subscr->extension, subscr->use_count); - if (subscr->use_count <= 0 && - !((subscr->group && subscr->group->keep_subscr) || - subscr->keep_in_ram)) - subscr_free(subscr); - return NULL; -} - -struct gsm_subscriber *subscr_get_or_create(struct gsm_subscriber_group *sgrp, - const char *imsi) -{ - struct gsm_subscriber *subscr; - - llist_for_each_entry(subscr, subscr_bsc_active_subscribers(), entry) { - if (strcmp(subscr->imsi, imsi) == 0 && subscr->group == sgrp) - return subscr_get(subscr); - } - - subscr = subscr_alloc(); - if (!subscr) - return NULL; - - osmo_strlcpy(subscr->imsi, imsi, sizeof(subscr->imsi)); - subscr->group = sgrp; - return subscr; -} - -struct gsm_subscriber *subscr_active_by_tmsi(struct gsm_subscriber_group *sgrp, - uint32_t tmsi) -{ - struct gsm_subscriber *subscr; - - llist_for_each_entry(subscr, subscr_bsc_active_subscribers(), entry) { - if (subscr->tmsi == tmsi && subscr->group == sgrp) - return subscr_get(subscr); - } - - return NULL; -} - -struct gsm_subscriber *subscr_active_by_imsi(struct gsm_subscriber_group *sgrp, - const char *imsi) -{ - struct gsm_subscriber *subscr; - - llist_for_each_entry(subscr, subscr_bsc_active_subscribers(), entry) { - if (strcmp(subscr->imsi, imsi) == 0 && subscr->group == sgrp) - return subscr_get(subscr); - } - - return NULL; -} - -int subscr_purge_inactive(struct gsm_subscriber_group *sgrp) -{ - struct gsm_subscriber *subscr, *tmp; - int purged = 0; - - llist_for_each_entry_safe(subscr, tmp, subscr_bsc_active_subscribers(), entry) { - if (subscr->group == sgrp && subscr->use_count <= 0) { - subscr_free(subscr); - purged += 1; - } - } - - return purged; -} diff --git a/openbsc/src/libcommon/gsup_client.c b/openbsc/src/libcommon/gsup_client.c deleted file mode 100644 index de00d8d4..00000000 --- a/openbsc/src/libcommon/gsup_client.c +++ /dev/null @@ -1,341 +0,0 @@ -/* Generic Subscriber Update Protocol client */ - -/* (C) 2014-2016 by Sysmocom s.f.m.c. GmbH - * All Rights Reserved - * - * Author: Jacob Erlbeck - * Author: Neels Hofmeyr - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation; either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - * - */ - -#include - -#include -#include -#include -#include - -#include - -#include -#include - -extern void *tall_bsc_ctx; - -static void start_test_procedure(struct gsup_client *gsupc); - -static void gsup_client_send_ping(struct gsup_client *gsupc) -{ - struct msgb *msg = gsup_client_msgb_alloc(); - - msg->l2h = msgb_put(msg, 1); - msg->l2h[0] = IPAC_MSGT_PING; - ipa_msg_push_header(msg, IPAC_PROTO_IPACCESS); - ipa_client_conn_send(gsupc->link, msg); -} - -static int gsup_client_connect(struct gsup_client *gsupc) -{ - int rc; - - if (gsupc->is_connected) - return 0; - - if (osmo_timer_pending(&gsupc->connect_timer)) { - LOGP(DLGSUP, LOGL_DEBUG, - "GSUP connect: connect timer already running\n"); - osmo_timer_del(&gsupc->connect_timer); - } - - if (osmo_timer_pending(&gsupc->ping_timer)) { - LOGP(DLGSUP, LOGL_DEBUG, - "GSUP connect: ping timer already running\n"); - osmo_timer_del(&gsupc->ping_timer); - } - - if (ipa_client_conn_clear_queue(gsupc->link) > 0) - LOGP(DLGSUP, LOGL_DEBUG, "GSUP connect: discarded stored messages\n"); - - rc = ipa_client_conn_open(gsupc->link); - - if (rc >= 0) { - LOGP(DLGSUP, LOGL_INFO, "GSUP connecting to %s:%d\n", - gsupc->link->addr, gsupc->link->port); - return 0; - } - - LOGP(DLGSUP, LOGL_INFO, "GSUP failed to connect to %s:%d: %s\n", - gsupc->link->addr, gsupc->link->port, strerror(-rc)); - - if (rc == -EBADF || rc == -ENOTSOCK || rc == -EAFNOSUPPORT || - rc == -EINVAL) - return rc; - - osmo_timer_schedule(&gsupc->connect_timer, - GSUP_CLIENT_RECONNECT_INTERVAL, 0); - - LOGP(DLGSUP, LOGL_INFO, "Scheduled timer to retry GSUP connect to %s:%d\n", - gsupc->link->addr, gsupc->link->port); - - return 0; -} - -static void connect_timer_cb(void *gsupc_) -{ - struct gsup_client *gsupc = gsupc_; - - if (gsupc->is_connected) - return; - - gsup_client_connect(gsupc); -} - -static void client_send(struct gsup_client *gsupc, int proto_ext, - struct msgb *msg_tx) -{ - ipa_prepend_header_ext(msg_tx, proto_ext); - ipa_msg_push_header(msg_tx, IPAC_PROTO_OSMO); - ipa_client_conn_send(gsupc->link, msg_tx); - /* msg_tx is now queued and will be freed. */ -} - -static void gsup_client_oap_register(struct gsup_client *gsupc) -{ - struct msgb *msg_tx; - int rc; - rc = oap_client_register(&gsupc->oap_state, &msg_tx); - - if ((rc < 0) || (!msg_tx)) { - LOGP(DLGSUP, LOGL_ERROR, "GSUP OAP set up, but cannot register.\n"); - return; - } - - client_send(gsupc, IPAC_PROTO_EXT_OAP, msg_tx); -} - -static void gsup_client_updown_cb(struct ipa_client_conn *link, int up) -{ - struct gsup_client *gsupc = link->data; - - LOGP(DLGSUP, LOGL_INFO, "GSUP link to %s:%d %s\n", - link->addr, link->port, up ? "UP" : "DOWN"); - - gsupc->is_connected = up; - - if (up) { - start_test_procedure(gsupc); - - if (gsupc->oap_state.state == OAP_INITIALIZED) - gsup_client_oap_register(gsupc); - - osmo_timer_del(&gsupc->connect_timer); - } else { - osmo_timer_del(&gsupc->ping_timer); - - osmo_timer_schedule(&gsupc->connect_timer, - GSUP_CLIENT_RECONNECT_INTERVAL, 0); - } -} - -static int gsup_client_oap_handle(struct gsup_client *gsupc, struct msgb *msg_rx) -{ - int rc; - struct msgb *msg_tx; - - /* If the oap_state is disabled, this will reject the messages. */ - rc = oap_client_handle(&gsupc->oap_state, msg_rx, &msg_tx); - msgb_free(msg_rx); - if (rc < 0) - return rc; - - if (msg_tx) - client_send(gsupc, IPAC_PROTO_EXT_OAP, msg_tx); - - return 0; -} - -static int gsup_client_read_cb(struct ipa_client_conn *link, struct msgb *msg) -{ - struct ipaccess_head *hh = (struct ipaccess_head *) msg->data; - struct ipaccess_head_ext *he = (struct ipaccess_head_ext *) msgb_l2(msg); - struct gsup_client *gsupc = (struct gsup_client *)link->data; - int rc; - static struct ipaccess_unit ipa_dev = { - .unit_name = "SGSN" - }; - - msg->l2h = &hh->data[0]; - - rc = ipaccess_bts_handle_ccm(link, &ipa_dev, msg); - - if (rc < 0) { - LOGP(DLGSUP, LOGL_NOTICE, - "GSUP received an invalid IPA/CCM message from %s:%d\n", - link->addr, link->port); - /* Link has been closed */ - gsupc->is_connected = 0; - msgb_free(msg); - return -1; - } - - if (rc == 1) { - uint8_t msg_type = *(msg->l2h); - /* CCM message */ - if (msg_type == IPAC_MSGT_PONG) { - LOGP(DLGSUP, LOGL_DEBUG, "GSUP receiving PONG\n"); - gsupc->got_ipa_pong = 1; - } - - msgb_free(msg); - return 0; - } - - if (hh->proto != IPAC_PROTO_OSMO) - goto invalid; - - if (!he || msgb_l2len(msg) < sizeof(*he)) - goto invalid; - - msg->l2h = &he->data[0]; - - if (he->proto == IPAC_PROTO_EXT_GSUP) { - OSMO_ASSERT(gsupc->read_cb != NULL); - gsupc->read_cb(gsupc, msg); - /* expecting read_cb() to free msg */ - } else if (he->proto == IPAC_PROTO_EXT_OAP) { - return gsup_client_oap_handle(gsupc, msg); - /* gsup_client_oap_handle frees msg */ - } else - goto invalid; - - return 0; - -invalid: - LOGP(DLGSUP, LOGL_NOTICE, - "GSUP received an invalid IPA message from %s:%d, size = %d\n", - link->addr, link->port, msgb_length(msg)); - - msgb_free(msg); - return -1; -} - -static void ping_timer_cb(void *gsupc_) -{ - struct gsup_client *gsupc = gsupc_; - - LOGP(DLGSUP, LOGL_INFO, "GSUP ping callback (%s, %s PONG)\n", - gsupc->is_connected ? "connected" : "not connected", - gsupc->got_ipa_pong ? "got" : "didn't get"); - - if (gsupc->got_ipa_pong) { - start_test_procedure(gsupc); - return; - } - - LOGP(DLGSUP, LOGL_NOTICE, "GSUP ping timed out, reconnecting\n"); - ipa_client_conn_close(gsupc->link); - gsupc->is_connected = 0; - - gsup_client_connect(gsupc); -} - -static void start_test_procedure(struct gsup_client *gsupc) -{ - osmo_timer_setup(&gsupc->ping_timer, ping_timer_cb, gsupc); - - gsupc->got_ipa_pong = 0; - osmo_timer_schedule(&gsupc->ping_timer, GSUP_CLIENT_PING_INTERVAL, 0); - LOGP(DLGSUP, LOGL_DEBUG, "GSUP sending PING\n"); - gsup_client_send_ping(gsupc); -} - -struct gsup_client *gsup_client_create(const char *ip_addr, - unsigned int tcp_port, - gsup_client_read_cb_t read_cb, - struct oap_client_config *oapc_config) -{ - struct gsup_client *gsupc; - int rc; - - gsupc = talloc_zero(tall_bsc_ctx, struct gsup_client); - OSMO_ASSERT(gsupc); - - /* a NULL oapc_config will mark oap_state disabled. */ - rc = oap_client_init(oapc_config, &gsupc->oap_state); - if (rc != 0) - goto failed; - - gsupc->link = ipa_client_conn_create(gsupc, - /* no e1inp */ NULL, - 0, - ip_addr, tcp_port, - gsup_client_updown_cb, - gsup_client_read_cb, - /* default write_cb */ NULL, - gsupc); - if (!gsupc->link) - goto failed; - - osmo_timer_setup(&gsupc->connect_timer, connect_timer_cb, gsupc); - - rc = gsup_client_connect(gsupc); - - if (rc < 0) - goto failed; - - gsupc->read_cb = read_cb; - - return gsupc; - -failed: - gsup_client_destroy(gsupc); - return NULL; -} - -void gsup_client_destroy(struct gsup_client *gsupc) -{ - osmo_timer_del(&gsupc->connect_timer); - osmo_timer_del(&gsupc->ping_timer); - - if (gsupc->link) { - ipa_client_conn_close(gsupc->link); - ipa_client_conn_destroy(gsupc->link); - gsupc->link = NULL; - } - talloc_free(gsupc); -} - -int gsup_client_send(struct gsup_client *gsupc, struct msgb *msg) -{ - if (!gsupc) { - msgb_free(msg); - return -ENOTCONN; - } - - if (!gsupc->is_connected) { - msgb_free(msg); - return -EAGAIN; - } - - client_send(gsupc, IPAC_PROTO_EXT_GSUP, msg); - - return 0; -} - -struct msgb *gsup_client_msgb_alloc(void) -{ - return msgb_alloc_headroom(4000, 64, __func__); -} diff --git a/openbsc/src/libcommon/gsup_test_client.c b/openbsc/src/libcommon/gsup_test_client.c deleted file mode 100644 index 8fc38d60..00000000 --- a/openbsc/src/libcommon/gsup_test_client.c +++ /dev/null @@ -1,298 +0,0 @@ -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include - -#include -#include - -static struct gsup_client *g_gc; - - -/*********************************************************************** - * IMSI Operation - ***********************************************************************/ -static LLIST_HEAD(g_imsi_ops); - -struct imsi_op_stats { - uint32_t num_alloc; - uint32_t num_released; - uint32_t num_rx_success; - uint32_t num_rx_error; - uint32_t num_timeout; -}; - -enum imsi_op_type { - IMSI_OP_SAI, - IMSI_OP_LU, - IMSI_OP_ISD, - _NUM_IMSI_OP -}; - -static const struct value_string imsi_op_names[] = { - { IMSI_OP_SAI, "SAI" }, - { IMSI_OP_LU, "LU" }, - { IMSI_OP_ISD, "ISD" }, - { 0, NULL } -}; - -static struct imsi_op_stats imsi_op_stats[_NUM_IMSI_OP]; - -struct imsi_op { - struct llist_head list; - char imsi[17]; - enum imsi_op_type type; - struct osmo_timer_list timer; -}; - -static struct imsi_op *imsi_op_find(const char *imsi, - enum imsi_op_type type) -{ - struct imsi_op *io; - - llist_for_each_entry(io, &g_imsi_ops, list) { - if (!strcmp(io->imsi, imsi) && io->type == type) - return io; - } - return NULL; -} - -static void imsi_op_timer_cb(void *data); - -static struct imsi_op *imsi_op_alloc(void *ctx, const char *imsi, - enum imsi_op_type type) -{ - struct imsi_op *io; - - if (imsi_op_find(imsi, type)) - return NULL; - - io = talloc_zero(ctx, struct imsi_op); - osmo_strlcpy(io->imsi, imsi, sizeof(io->imsi)); - io->type = type; - osmo_timer_setup(&io->timer, imsi_op_timer_cb, io); - llist_add(&io->list, &g_imsi_ops); - imsi_op_stats[type].num_alloc++; - - return io; -} - -static void imsi_op_release(struct imsi_op *io) -{ - osmo_timer_del(&io->timer); - llist_del(&io->list); - imsi_op_stats[io->type].num_released++; - talloc_free(io); -} - -static void imsi_op_timer_cb(void *data) -{ - struct imsi_op *io = data; - printf("%s: Timer expiration\n", io->imsi); - imsi_op_stats[io->type].num_timeout++; - imsi_op_release(io); -} - -/* allocate + generate + send Send-Auth-Info */ -int req_auth_info(const char *imsi) -{ - struct imsi_op *io = imsi_op_alloc(g_gc, imsi, IMSI_OP_SAI); - struct osmo_gsup_message gsup = {0}; - struct msgb *msg = msgb_alloc_headroom(1200, 200, __func__); - - osmo_strlcpy(gsup.imsi, io->imsi, sizeof(gsup.imsi)); - gsup.message_type = OSMO_GSUP_MSGT_SEND_AUTH_INFO_REQUEST; - - osmo_gsup_encode(msg, &gsup); - - return gsup_client_send(g_gc, msg); -} - -/* allocate + generate + send Send-Auth-Info */ -int req_loc_upd(const char *imsi) -{ - struct imsi_op *io = imsi_op_alloc(g_gc, imsi, IMSI_OP_LU); - struct osmo_gsup_message gsup = {0}; - struct msgb *msg = msgb_alloc_headroom(1200, 200, __func__); - - osmo_strlcpy(gsup.imsi, io->imsi, sizeof(gsup.imsi)); - gsup.message_type = OSMO_GSUP_MSGT_UPDATE_LOCATION_REQUEST; - - osmo_gsup_encode(msg, &gsup); - - return gsup_client_send(g_gc, msg); -} - -int resp_isd(struct imsi_op *io) -{ - struct osmo_gsup_message gsup = {0}; - struct msgb *msg = msgb_alloc_headroom(1200, 200, __func__); - - osmo_strlcpy(gsup.imsi, io->imsi, sizeof(gsup.imsi)); - gsup.message_type = OSMO_GSUP_MSGT_INSERT_DATA_RESULT; - - osmo_gsup_encode(msg, &gsup); - - imsi_op_release(io); - - return gsup_client_send(g_gc, msg); -} - -/* receive an incoming GSUP message */ -static void imsi_op_rx_gsup(struct imsi_op *io, const struct osmo_gsup_message *gsup) -{ - int is_error = 0; - - if (OSMO_GSUP_IS_MSGT_ERROR(gsup->message_type)) { - imsi_op_stats[io->type].num_rx_error++; - is_error = 1; - } else - imsi_op_stats[io->type].num_rx_success++; - - switch (io->type) { - case IMSI_OP_SAI: - printf("%s; SAI Response%s\n", io->imsi, is_error ? ": ERROR" : ""); - /* now that we have auth tuples, request LU */ - req_loc_upd(io->imsi); - imsi_op_release(io); - break; - case IMSI_OP_LU: - printf("%s; LU Response%s\n", io->imsi, is_error ? ": ERROR" : ""); - imsi_op_release(io); - break; - case IMSI_OP_ISD: - printf("%s; ISD Request%s\n", io->imsi, is_error ? ": ERROR" : ""); - resp_isd(io); - break; - default: - printf("%s: Unknown\n", io->imsi); - imsi_op_release(io); - break; - } -} - -static int op_type_by_gsup_msgt(enum osmo_gsup_message_type msg_type) -{ - switch (msg_type) { - case OSMO_GSUP_MSGT_SEND_AUTH_INFO_RESULT: - case OSMO_GSUP_MSGT_SEND_AUTH_INFO_ERROR: - return IMSI_OP_SAI; - case OSMO_GSUP_MSGT_UPDATE_LOCATION_RESULT: - case OSMO_GSUP_MSGT_UPDATE_LOCATION_ERROR: - return IMSI_OP_LU; - case OSMO_GSUP_MSGT_INSERT_DATA_REQUEST: - return IMSI_OP_ISD; - default: - printf("Unknown GSUP msg_type %u\n", msg_type); - return -1; - } -} - -static int gsupc_read_cb(struct gsup_client *gsupc, struct msgb *msg) -{ - struct osmo_gsup_message gsup_msg = {0}; - struct imsi_op *io; - int rc; - - DEBUGP(DGPRS, "Rx GSUP %s\n", osmo_hexdump(msgb_l2(msg), msgb_l2len(msg))); - - rc = osmo_gsup_decode(msgb_l2(msg), msgb_l2len(msg), &gsup_msg); - if (rc < 0) - return rc; - - if (!gsup_msg.imsi[0]) - return -1; - - rc = op_type_by_gsup_msgt(gsup_msg.message_type); - if (rc < 0) - return rc; - - switch (rc) { - case IMSI_OP_SAI: - case IMSI_OP_LU: - io = imsi_op_find(gsup_msg.imsi, rc); - if (!io) - return -1; - break; - case IMSI_OP_ISD: - /* ISD is an inbound transaction */ - io = imsi_op_alloc(g_gc, gsup_msg.imsi, IMSI_OP_ISD); - break; - } - - imsi_op_rx_gsup(io, &gsup_msg); - msgb_free(msg); - - return 0; -} - -static void print_report(void) -{ - unsigned int i; - - for (i = 0; i < ARRAY_SIZE(imsi_op_stats); i++) { - struct imsi_op_stats *st = &imsi_op_stats[i]; - const char *name = get_value_string(imsi_op_names, i); - printf("%s: %u alloc, %u released, %u success, %u error , %u tout\n", - name, st->num_alloc, st->num_released, st->num_rx_success, - st->num_rx_error, st->num_timeout); - } -} - -static void sig_cb(int sig) -{ - switch (sig) { - case SIGINT: - print_report(); - exit(0); - break; - } -} - -void *tall_bsc_ctx = NULL; - -/* default categories */ -static struct log_info_cat default_categories[] = { -}; - -static const struct log_info gsup_test_client_log_info = { - .cat = default_categories, - .num_cat = ARRAY_SIZE(default_categories), -}; - -int main(int argc, char **argv) -{ - unsigned long long i; - char *server_host = "127.0.0.1"; - uint16_t server_port = 2222; - - osmo_init_logging(&gsup_test_client_log_info); - - g_gc = gsup_client_create(server_host, server_port, gsupc_read_cb, - NULL); - - - signal(SIGINT, sig_cb); - - for (i = 0; i < 10000; i++) { - unsigned long long imsi = 901790000000000 + i; - char imsi_buf[17]; - snprintf(imsi_buf, sizeof(imsi_buf), "%015llu", imsi); - req_auth_info(imsi_buf); - osmo_select_main(0); - } - - while (1) { - osmo_select_main(0); - } - - print_report(); - exit(0); -} diff --git a/openbsc/src/libcommon/oap_client.c b/openbsc/src/libcommon/oap_client.c deleted file mode 100644 index 5128ac11..00000000 --- a/openbsc/src/libcommon/oap_client.c +++ /dev/null @@ -1,280 +0,0 @@ -/* Osmocom Authentication Protocol API */ - -/* (C) 2015 by Sysmocom s.f.m.c. GmbH - * All Rights Reserved - * - * Author: Neels Hofmeyr - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation; either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - * - */ - -#include -#include - -#include -#include -#include - -#include -#include - -int oap_client_init(struct oap_client_config *config, - struct oap_client_state *state) -{ - OSMO_ASSERT(state->state == OAP_UNINITIALIZED); - - if (!config) - goto disable; - - if (config->client_id == 0) - goto disable; - - if (config->secret_k_present == 0) { - LOGP(DLOAP, LOGL_NOTICE, "OAP: client ID set, but secret K missing.\n"); - goto disable; - } - - if (config->secret_opc_present == 0) { - LOGP(DLOAP, LOGL_NOTICE, "OAP: client ID set, but secret OPC missing.\n"); - goto disable; - } - - state->client_id = config->client_id; - memcpy(state->secret_k, config->secret_k, sizeof(state->secret_k)); - memcpy(state->secret_opc, config->secret_opc, sizeof(state->secret_opc)); - state->state = OAP_INITIALIZED; - return 0; - -disable: - state->state = OAP_DISABLED; - return 0; -} - -/* From the given state and received RAND and AUTN octets, validate the - * server's authenticity and formulate the matching milenage reply octets in - * *tx_xres. The state is not modified. - * On success, and if tx_res is not NULL, exactly 8 octets will be written to - * *tx_res. If not NULL, tx_res must point at allocated memory of at least 8 - * octets. The caller will want to send XRES back to the server in a challenge - * response message and update the state. - * Return 0 on success; -1 if OAP is disabled; -2 if rx_random and rx_autn fail - * the authentication check; -3 for any other errors. */ -static int oap_evaluate_challenge(const struct oap_client_state *state, - const uint8_t *rx_random, - const uint8_t *rx_autn, - uint8_t *tx_xres) -{ - struct osmo_auth_vector vec; - - struct osmo_sub_auth_data auth = { - .type = OSMO_AUTH_TYPE_UMTS, - .algo = OSMO_AUTH_ALG_MILENAGE, - }; - - osmo_static_assert(sizeof(((struct osmo_sub_auth_data*)0)->u.umts.k) - == sizeof(state->secret_k), _secret_k_size_match); - osmo_static_assert(sizeof(((struct osmo_sub_auth_data*)0)->u.umts.opc) - == sizeof(state->secret_opc), _secret_opc_size_match); - - switch (state->state) { - case OAP_UNINITIALIZED: - case OAP_DISABLED: - return -1; - default: - break; - } - - memcpy(auth.u.umts.k, state->secret_k, sizeof(auth.u.umts.k)); - memcpy(auth.u.umts.opc, state->secret_opc, sizeof(auth.u.umts.opc)); - memset(auth.u.umts.amf, '\0', sizeof(auth.u.umts.amf)); - auth.u.umts.sqn = 41; /* TODO use incrementing sequence nr */ - - memset(&vec, 0, sizeof(vec)); - osmo_auth_gen_vec(&vec, &auth, rx_random); - - if (vec.res_len != 8) { - LOGP(DLOAP, LOGL_ERROR, "OAP: Expected XRES to be 8 octets, got %d\n", - vec.res_len); - return -3; - } - - if (osmo_constant_time_cmp(vec.autn, rx_autn, sizeof(vec.autn)) != 0) { - LOGP(DLOAP, LOGL_ERROR, "OAP: AUTN mismatch!\n"); - LOGP(DLOAP, LOGL_INFO, "OAP: AUTN from server: %s\n", - osmo_hexdump_nospc(rx_autn, sizeof(vec.autn))); - LOGP(DLOAP, LOGL_INFO, "OAP: AUTN expected: %s\n", - osmo_hexdump_nospc(vec.autn, sizeof(vec.autn))); - return -2; - } - - if (tx_xres != NULL) - memcpy(tx_xres, vec.res, 8); - return 0; -} - -struct msgb *oap_client_encoded(const struct osmo_oap_message *oap_msg) -{ - struct msgb *msg = msgb_alloc_headroom(1000, 64, __func__); - OSMO_ASSERT(msg); - osmo_oap_encode(msg, oap_msg); - return msg; -} - -/* Create a new msgb containing an OAP registration message. - * On error, return NULL. */ -static struct msgb* oap_msg_register(uint16_t client_id) -{ - struct osmo_oap_message oap_msg = {0}; - - if (client_id < 1) { - LOGP(DLOAP, LOGL_ERROR, "OAP: Invalid client ID: %d\n", client_id); - return NULL; - } - - oap_msg.message_type = OAP_MSGT_REGISTER_REQUEST; - oap_msg.client_id = client_id; - return oap_client_encoded(&oap_msg); -} - -int oap_client_register(struct oap_client_state *state, struct msgb **msg_tx) -{ - *msg_tx = oap_msg_register(state->client_id); - if (!(*msg_tx)) - return -1; - - state->state = OAP_REQUESTED_CHALLENGE; - return 0; -} - -/* Create a new msgb containing an OAP challenge response message. - * xres must point at 8 octets to return as challenge response. - * On error, return NULL. */ -static struct msgb* oap_msg_challenge_response(uint8_t *xres) -{ - struct osmo_oap_message oap_reply = {0}; - - oap_reply.message_type = OAP_MSGT_CHALLENGE_RESULT; - memcpy(oap_reply.xres, xres, sizeof(oap_reply.xres)); - oap_reply.xres_present = 1; - return oap_client_encoded(&oap_reply); -} - -static int handle_challenge(struct oap_client_state *state, - struct osmo_oap_message *oap_rx, - struct msgb **msg_tx) -{ - int rc; - uint8_t xres[8]; - - if (!(oap_rx->rand_present && oap_rx->autn_present)) { - LOGP(DLOAP, LOGL_ERROR, - "OAP challenge incomplete (rand_present: %d, autn_present: %d)\n", - oap_rx->rand_present, oap_rx->autn_present); - rc = -2; - goto failure; - } - - rc = oap_evaluate_challenge(state, - oap_rx->rand, - oap_rx->autn, - xres); - if (rc < 0) - goto failure; - - *msg_tx = oap_msg_challenge_response(xres); - if ((*msg_tx) == NULL) { - rc = -1; - goto failure; - } - - state->state = OAP_SENT_CHALLENGE_RESULT; - return 0; - -failure: - OSMO_ASSERT(rc < 0); - state->state = OAP_INITIALIZED; - return rc; -} - -int oap_client_handle(struct oap_client_state *state, - const struct msgb *msg_rx, struct msgb **msg_tx) -{ - uint8_t *data = msgb_l2(msg_rx); - size_t data_len = msgb_l2len(msg_rx); - struct osmo_oap_message oap_msg = {0}; - int rc = 0; - - *msg_tx = NULL; - - OSMO_ASSERT(data); - - rc = osmo_oap_decode(&oap_msg, data, data_len); - if (rc < 0) { - LOGP(DLOAP, LOGL_ERROR, - "Decoding OAP message failed with error '%s' (%d)\n", - get_value_string(gsm48_gmm_cause_names, -rc), -rc); - return -10; - } - - switch (state->state) { - case OAP_UNINITIALIZED: - LOGP(DLOAP, LOGL_ERROR, - "Received OAP message %d, but the OAP client is" - " not initialized\n", oap_msg.message_type); - return -ENOTCONN; - case OAP_DISABLED: - LOGP(DLOAP, LOGL_ERROR, - "Received OAP message %d, but the OAP client is" - " disabled\n", oap_msg.message_type); - return -ENOTCONN; - default: - break; - } - - switch (oap_msg.message_type) { - case OAP_MSGT_CHALLENGE_REQUEST: - return handle_challenge(state, &oap_msg, msg_tx); - - case OAP_MSGT_REGISTER_RESULT: - /* successfully registered */ - state->state = OAP_REGISTERED; - break; - - case OAP_MSGT_REGISTER_ERROR: - LOGP(DLOAP, LOGL_ERROR, - "OAP registration failed\n"); - state->state = OAP_INITIALIZED; - if (state->registration_failures < 3) { - state->registration_failures ++; - return oap_client_register(state, msg_tx); - } - return -11; - - case OAP_MSGT_REGISTER_REQUEST: - case OAP_MSGT_CHALLENGE_RESULT: - LOGP(DLOAP, LOGL_ERROR, - "Received invalid OAP message type for OAP client side: %d\n", - (int)oap_msg.message_type); - return -12; - - default: - LOGP(DLOAP, LOGL_ERROR, - "Unknown OAP message type: %d\n", - (int)oap_msg.message_type); - return -13; - } - - return 0; -} diff --git a/openbsc/src/libcommon/socket.c b/openbsc/src/libcommon/socket.c deleted file mode 100644 index 2a64767f..00000000 --- a/openbsc/src/libcommon/socket.c +++ /dev/null @@ -1,111 +0,0 @@ -/* OpenBSC sokcet code, taken from Abis input driver for ip.access */ - -/* (C) 2009 by Harald Welte - * (C) 2010 by Holger Hans Peter Freyther - * (C) 2010 by On-Waves - * - * All Rights Reserved - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation; either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - * - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include - -int make_sock(struct osmo_fd *bfd, int proto, - uint32_t ip, uint16_t port, int priv_nr, - int (*cb)(struct osmo_fd *fd, unsigned int what), void *data) -{ - struct sockaddr_in addr; - int ret, on = 1; - int type = SOCK_STREAM; - - switch (proto) { - case IPPROTO_TCP: - type = SOCK_STREAM; - break; - case IPPROTO_UDP: - type = SOCK_DGRAM; - break; -#ifdef IPPROTO_GRE - case IPPROTO_GRE: - type = SOCK_RAW; - break; -#endif - default: - return -EINVAL; - } - - bfd->fd = socket(AF_INET, type, proto); - bfd->cb = cb; - bfd->when = BSC_FD_READ; - bfd->data = data; - bfd->priv_nr = priv_nr; - - if (bfd->fd < 0) { - LOGP(DLINP, LOGL_ERROR, "could not create socket.\n"); - return -EIO; - } - - memset(&addr, 0, sizeof(addr)); - addr.sin_family = AF_INET; - addr.sin_port = htons(port); - if (ip != INADDR_ANY) - addr.sin_addr.s_addr = htonl(ip); - else - addr.sin_addr.s_addr = INADDR_ANY; - - setsockopt(bfd->fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)); - - ret = bind(bfd->fd, (struct sockaddr *) &addr, sizeof(addr)); - if (ret < 0) { - LOGP(DLINP, LOGL_ERROR, "could not bind socket %s\n", - strerror(errno)); - close(bfd->fd); - return -EIO; - } - - if (proto == IPPROTO_TCP) { - ret = listen(bfd->fd, 1); - if (ret < 0) { - perror("listen"); - close(bfd->fd); - return ret; - } - } - - ret = osmo_fd_register(bfd); - if (ret < 0) { - perror("register_listen_fd"); - close(bfd->fd); - return ret; - } - return 0; -} diff --git a/openbsc/src/libcommon/talloc_ctx.c b/openbsc/src/libcommon/talloc_ctx.c deleted file mode 100644 index 5e3d9aeb..00000000 --- a/openbsc/src/libcommon/talloc_ctx.c +++ /dev/null @@ -1,56 +0,0 @@ -/* OpenBSC allocation contexts initialization code */ -/* (C) 2011-2016 by sysmocom s.f.m.c. GmbH - * All Rights Reserved - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation; either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public 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 - -extern void *tall_bsc_ctx; -extern void *tall_fle_ctx; -extern void *tall_locop_ctx; -extern void *tall_authciphop_ctx; -extern void *tall_gsms_ctx; -extern void *tall_subscr_ctx; -extern void *tall_sub_req_ctx; -extern void *tall_call_ctx; -extern void *tall_paging_ctx; -extern void *tall_sigh_ctx; -extern void *tall_tqe_ctx; -extern void *tall_trans_ctx; -extern void *tall_map_ctx; -extern void *tall_upq_ctx; -extern void *tall_ctr_ctx; - -void talloc_ctx_init(void *ctx_root) -{ - msgb_talloc_ctx_init(ctx_root, 0); - tall_fle_ctx = talloc_named_const(ctx_root, 0, "bs11_file_list_entry"); - tall_locop_ctx = talloc_named_const(ctx_root, 0, "loc_updating_oper"); - tall_authciphop_ctx = talloc_named_const(ctx_root, 0, "auth_ciph_oper"); - tall_gsms_ctx = talloc_named_const(ctx_root, 0, "sms"); - tall_subscr_ctx = talloc_named_const(ctx_root, 0, "subscriber"); - tall_sub_req_ctx = talloc_named_const(ctx_root, 0, "subscr_request"); - tall_call_ctx = talloc_named_const(ctx_root, 0, "gsm_call"); - tall_paging_ctx = talloc_named_const(ctx_root, 0, "paging_request"); - tall_sigh_ctx = talloc_named_const(ctx_root, 0, "signal_handler"); - tall_tqe_ctx = talloc_named_const(ctx_root, 0, "subch_txq_entry"); - tall_trans_ctx = talloc_named_const(ctx_root, 0, "transaction"); - tall_map_ctx = talloc_named_const(ctx_root, 0, "trau_map_entry"); - tall_upq_ctx = talloc_named_const(ctx_root, 0, "trau_upq_entry"); - tall_ctr_ctx = talloc_named_const(ctx_root, 0, "counter"); -} diff --git a/openbsc/src/libfilter/Makefile.am b/openbsc/src/libfilter/Makefile.am deleted file mode 100644 index 6d3db0b9..00000000 --- a/openbsc/src/libfilter/Makefile.am +++ /dev/null @@ -1,26 +0,0 @@ -AM_CPPFLAGS = \ - $(all_includes) \ - -I$(top_srcdir)/include \ - -I$(top_builddir) \ - $(NULL) - -AM_CFLAGS = \ - -Wall \ - $(LIBOSMOCORE_CFLAGS) \ - $(LIBOSMOGSM_CFLAGS) \ - $(LIBOSMOVTY_CFLAGS) \ - $(LIBOSMOABIS_CFLAGS) \ - $(LIBOSMOSCCP_CFLAGS) \ - $(COVERAGE_CFLAGS) \ - $(NULL) - -noinst_LIBRARIES = \ - libfilter.a \ - $(NULL) - -libfilter_a_SOURCES = \ - bsc_msg_filter.c \ - bsc_msg_acc.c \ - bsc_msg_vty.c \ - $(NULL) - diff --git a/openbsc/src/libfilter/bsc_msg_acc.c b/openbsc/src/libfilter/bsc_msg_acc.c deleted file mode 100644 index bfc5bdd3..00000000 --- a/openbsc/src/libfilter/bsc_msg_acc.c +++ /dev/null @@ -1,118 +0,0 @@ -/* - * (C) 2010-2015 by Holger Hans Peter Freyther - * (C) 2010-2011 by On-Waves - * All Rights Reserved - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation; either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - * - */ - -#include -#include - -#include -#include - -#include - -static const struct rate_ctr_desc acc_list_ctr_description[] = { - [ACC_LIST_LOCAL_FILTER] = { "access-list.local-filter", "Rejected by rule for local"}, - [ACC_LIST_GLOBAL_FILTER]= { "access-list.global-filter", "Rejected by rule for global"}, -}; - -static const struct rate_ctr_group_desc bsc_cfg_acc_list_desc = { - .group_name_prefix = "nat.filter", - .group_description = "NAT Access-List Statistics", - .num_ctr = ARRAY_SIZE(acc_list_ctr_description), - .ctr_desc = acc_list_ctr_description, - .class_id = OSMO_STATS_CLASS_GLOBAL, -}; - - -int bsc_msg_acc_lst_check_allow(struct bsc_msg_acc_lst *lst, const char *mi_string) -{ - struct bsc_msg_acc_lst_entry *entry; - - llist_for_each_entry(entry, &lst->fltr_list, list) { - if (!entry->imsi_allow) - continue; - if (regexec(&entry->imsi_allow_re, mi_string, 0, NULL, 0) == 0) - return 0; - } - - return 1; -} - -struct bsc_msg_acc_lst *bsc_msg_acc_lst_find(struct llist_head *head, const char *name) -{ - struct bsc_msg_acc_lst *lst; - - if (!name) - return NULL; - - llist_for_each_entry(lst, head, list) - if (strcmp(lst->name, name) == 0) - return lst; - - return NULL; -} - -struct bsc_msg_acc_lst *bsc_msg_acc_lst_get(void *ctx, struct llist_head *head, const char *name) -{ - struct bsc_msg_acc_lst *lst; - - lst = bsc_msg_acc_lst_find(head, name); - if (lst) - return lst; - - lst = talloc_zero(ctx, struct bsc_msg_acc_lst); - if (!lst) { - LOGP(DNAT, LOGL_ERROR, "Failed to allocate access list"); - return NULL; - } - - /* TODO: get the index right */ - lst->stats = rate_ctr_group_alloc(lst, &bsc_cfg_acc_list_desc, 0); - if (!lst->stats) { - talloc_free(lst); - return NULL; - } - - INIT_LLIST_HEAD(&lst->fltr_list); - lst->name = talloc_strdup(lst, name); - llist_add_tail(&lst->list, head); - return lst; -} - -void bsc_msg_acc_lst_delete(struct bsc_msg_acc_lst *lst) -{ - llist_del(&lst->list); - rate_ctr_group_free(lst->stats); - talloc_free(lst); -} - -struct bsc_msg_acc_lst_entry *bsc_msg_acc_lst_entry_create(struct bsc_msg_acc_lst *lst) -{ - struct bsc_msg_acc_lst_entry *entry; - - entry = talloc_zero(lst, struct bsc_msg_acc_lst_entry); - if (!entry) - return NULL; - - entry->cm_reject_cause = GSM48_REJECT_PLMN_NOT_ALLOWED; - entry->lu_reject_cause = GSM48_REJECT_PLMN_NOT_ALLOWED; - llist_add_tail(&entry->list, &lst->fltr_list); - return entry; -} - diff --git a/openbsc/src/libfilter/bsc_msg_filter.c b/openbsc/src/libfilter/bsc_msg_filter.c deleted file mode 100644 index 115d376c..00000000 --- a/openbsc/src/libfilter/bsc_msg_filter.c +++ /dev/null @@ -1,398 +0,0 @@ -/* - * Access filtering - */ -/* - * (C) 2010-2015 by Holger Hans Peter Freyther - * (C) 2010-2012 by On-Waves - * All Rights Reserved - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation; either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - * - */ - -#include - -#include -#include -#include -#include -#include - -#include -#include - -#include -#include - -int bsc_filter_barr_find(struct rb_root *root, const char *imsi, int *cm, int *lu) -{ - struct bsc_filter_barr_entry *n; - n = rb_entry(root->rb_node, struct bsc_filter_barr_entry, node); - - while (n) { - int rc = strcmp(imsi, n->imsi); - if (rc == 0) { - *cm = n->cm_reject_cause; - *lu = n->lu_reject_cause; - return 1; - } - - n = rb_entry( - (rc < 0) ? n->node.rb_left : n->node.rb_right, - struct bsc_filter_barr_entry, node); - }; - - return 0; -} - -static int insert_barr_node(struct bsc_filter_barr_entry *entry, struct rb_root *root) -{ - struct rb_node **new = &root->rb_node, *parent = NULL; - - while (*new) { - int rc; - struct bsc_filter_barr_entry *this; - this = rb_entry(*new, struct bsc_filter_barr_entry, node); - parent = *new; - - rc = strcmp(entry->imsi, this->imsi); - if (rc < 0) - new = &((*new)->rb_left); - else if (rc > 0) - new = &((*new)->rb_right); - else { - LOGP(DFILTER, LOGL_ERROR, - "Duplicate entry for IMSI(%s)\n", entry->imsi); - talloc_free(entry); - return -1; - } - } - - rb_link_node(&entry->node, parent, new); - rb_insert_color(&entry->node, root); - return 0; -} - -int bsc_filter_barr_adapt(void *ctx, struct rb_root *root, - const struct osmo_config_list *list) -{ - struct osmo_config_entry *cfg_entry; - int err = 0; - - /* free the old data */ - while (!RB_EMPTY_ROOT(root)) { - struct rb_node *node = rb_first(root); - rb_erase(node, root); - talloc_free(node); - } - - if (!list) - return 0; - - /* now adapt the new list */ - llist_for_each_entry(cfg_entry, &list->entry, list) { - struct bsc_filter_barr_entry *entry; - entry = talloc_zero(ctx, struct bsc_filter_barr_entry); - if (!entry) { - LOGP(DFILTER, LOGL_ERROR, - "Allocation of the barr entry failed.\n"); - continue; - } - - entry->imsi = talloc_strdup(entry, cfg_entry->mcc); - entry->cm_reject_cause = atoi(cfg_entry->mnc); - entry->lu_reject_cause = atoi(cfg_entry->option); - err |= insert_barr_node(entry, root); - } - - return err; -} - - -static int lst_check_deny(struct bsc_msg_acc_lst *lst, const char *mi_string, - int *cm_cause, int *lu_cause) -{ - struct bsc_msg_acc_lst_entry *entry; - - llist_for_each_entry(entry, &lst->fltr_list, list) { - if (!entry->imsi_deny) - continue; - if (regexec(&entry->imsi_deny_re, mi_string, 0, NULL, 0) == 0) { - *cm_cause = entry->cm_reject_cause; - *lu_cause = entry->lu_reject_cause; - return 0; - } - } - - return 1; -} - -/* apply white/black list */ -static int auth_imsi(struct bsc_filter_request *req, - const char *imsi, - struct bsc_filter_reject_cause *cause) -{ - /* - * Now apply blacklist/whitelist of the BSC and the NAT. - * 1.) Check the global IMSI barr list - * 2.) Allow directly if the IMSI is allowed at the BSC - * 3.) Reject if the IMSI is not allowed at the BSC - * 4.) Reject if the IMSI not allowed at the global level. - * 5.) Allow directly if the IMSI is allowed at the global level - */ - int cm, lu; - struct bsc_msg_acc_lst *nat_lst = NULL; - struct bsc_msg_acc_lst *bsc_lst = NULL; - - /* 1. global check for barred imsis */ - if (req->black_list && bsc_filter_barr_find(req->black_list, imsi, &cm, &lu)) { - cause->cm_reject_cause = cm; - cause->lu_reject_cause = lu; - LOGP(DFILTER, LOGL_DEBUG, - "Blocking subscriber IMSI %s with CM: %d LU: %d\n", - imsi, cm, lu); - return -4; - } - - - bsc_lst = bsc_msg_acc_lst_find(req->access_lists, req->local_lst_name); - nat_lst = bsc_msg_acc_lst_find(req->access_lists, req->global_lst_name); - - - if (bsc_lst) { - /* 2. BSC allow */ - if (bsc_msg_acc_lst_check_allow(bsc_lst, imsi) == 0) - return 1; - - /* 3. BSC deny */ - if (lst_check_deny(bsc_lst, imsi, &cm, &lu) == 0) { - LOGP(DFILTER, LOGL_ERROR, - "Filtering %s by imsi_deny on config nr: %d.\n", imsi, req->bsc_nr); - rate_ctr_inc(&bsc_lst->stats->ctr[ACC_LIST_LOCAL_FILTER]); - cause->cm_reject_cause = cm; - cause->lu_reject_cause = lu; - return -2; - } - - } - - /* 4. NAT deny */ - if (nat_lst) { - if (lst_check_deny(nat_lst, imsi, &cm, &lu) == 0) { - LOGP(DFILTER, LOGL_ERROR, - "Filtering %s global imsi_deny on bsc nr: %d.\n", imsi, req->bsc_nr); - rate_ctr_inc(&nat_lst->stats->ctr[ACC_LIST_GLOBAL_FILTER]); - cause->cm_reject_cause = cm; - cause->lu_reject_cause = lu; - return -3; - } - } - - return 1; -} - -static int _cr_check_loc_upd(void *ctx, - uint8_t *data, unsigned int length, - char **imsi) -{ - uint8_t mi_type; - struct gsm48_loc_upd_req *lu; - char mi_string[GSM48_MI_SIZE]; - - if (length < sizeof(*lu)) { - LOGP(DFILTER, LOGL_ERROR, - "LU does not fit. Length is %d \n", length); - return -1; - } - - lu = (struct gsm48_loc_upd_req *) data; - mi_type = lu->mi[0] & GSM_MI_TYPE_MASK; - - /* - * We can only deal with the IMSI. This will fail for a phone that - * will send the TMSI of a previous network to us. - */ - if (mi_type != GSM_MI_TYPE_IMSI) - return 0; - - gsm48_mi_to_string(mi_string, sizeof(mi_string), lu->mi, lu->mi_len); - *imsi = talloc_strdup(ctx, mi_string); - return 1; -} - -static int _cr_check_cm_serv_req(void *ctx, - uint8_t *data, unsigned int length, - int *con_type, char **imsi) -{ - static const uint32_t classmark_offset = - offsetof(struct gsm48_service_request, classmark); - - char mi_string[GSM48_MI_SIZE]; - uint8_t mi_type; - int rc; - struct gsm48_service_request *req; - - /* unfortunately in Phase1 the classmark2 length is variable */ - - if (length < sizeof(*req)) { - LOGP(DFILTER, LOGL_ERROR, - "CM Serv Req does not fit. Length is %d\n", length); - return -1; - } - - req = (struct gsm48_service_request *) data; - if (req->cm_service_type == 0x8) - *con_type = FLT_CON_TYPE_SSA; - rc = gsm48_extract_mi((uint8_t *) &req->classmark, - length - classmark_offset, mi_string, &mi_type); - if (rc < 0) { - LOGP(DFILTER, LOGL_ERROR, "Failed to parse the classmark2/mi. error: %d\n", rc); - return -1; - } - - /* we have to let the TMSI or such pass */ - if (mi_type != GSM_MI_TYPE_IMSI) - return 0; - - *imsi = talloc_strdup(ctx, mi_string); - return 1; -} - -static int _cr_check_pag_resp(void *ctx, - uint8_t *data, unsigned int length, char **imsi) -{ - struct gsm48_pag_resp *resp; - char mi_string[GSM48_MI_SIZE]; - uint8_t mi_type; - - if (length < sizeof(*resp)) { - LOGP(DFILTER, LOGL_ERROR, "PAG RESP does not fit. Length was %d.\n", length); - return -1; - } - - resp = (struct gsm48_pag_resp *) data; - if (gsm48_paging_extract_mi(resp, length, mi_string, &mi_type) < 0) { - LOGP(DFILTER, LOGL_ERROR, "Failed to extract the MI.\n"); - return -1; - } - - /* we need to let it pass for now */ - if (mi_type != GSM_MI_TYPE_IMSI) - return 0; - - *imsi = talloc_strdup(ctx, mi_string); - return 1; -} - -static int _dt_check_id_resp(struct bsc_filter_request *req, - uint8_t *data, unsigned int length, - struct bsc_filter_state *state, - struct bsc_filter_reject_cause *cause) -{ - char mi_string[GSM48_MI_SIZE]; - uint8_t mi_type; - - if (length < 2) { - LOGP(DFILTER, LOGL_ERROR, "mi does not fit.\n"); - return -1; - } - - if (data[0] < length - 1) { - LOGP(DFILTER, LOGL_ERROR, "mi length too big.\n"); - return -2; - } - - mi_type = data[1] & GSM_MI_TYPE_MASK; - gsm48_mi_to_string(mi_string, sizeof(mi_string), &data[1], data[0]); - - if (mi_type != GSM_MI_TYPE_IMSI) - return 0; - - state->imsi_checked = 1; - state->imsi = talloc_strdup(req->ctx, mi_string); - return auth_imsi(req, mi_string, cause); -} - - -/* Filter out CR data... */ -int bsc_msg_filter_initial(struct gsm48_hdr *hdr48, size_t hdr48_len, - struct bsc_filter_request *req, - int *con_type, - char **imsi, struct bsc_filter_reject_cause *cause) -{ - int ret = 0; - uint8_t msg_type, proto; - - *con_type = FLT_CON_TYPE_NONE; - cause->cm_reject_cause = GSM48_REJECT_PLMN_NOT_ALLOWED; - cause->lu_reject_cause = GSM48_REJECT_PLMN_NOT_ALLOWED; - *imsi = NULL; - - proto = gsm48_hdr_pdisc(hdr48); - msg_type = gsm48_hdr_msg_type(hdr48); - if (proto == GSM48_PDISC_MM && - msg_type == GSM48_MT_MM_LOC_UPD_REQUEST) { - *con_type = FLT_CON_TYPE_LU; - ret = _cr_check_loc_upd(req->ctx, &hdr48->data[0], - hdr48_len - sizeof(*hdr48), imsi); - } else if (proto == GSM48_PDISC_MM && - msg_type == GSM48_MT_MM_CM_SERV_REQ) { - *con_type = FLT_CON_TYPE_CM_SERV_REQ; - ret = _cr_check_cm_serv_req(req->ctx, &hdr48->data[0], - hdr48_len - sizeof(*hdr48), - con_type, imsi); - } else if (proto == GSM48_PDISC_RR && - msg_type == GSM48_MT_RR_PAG_RESP) { - *con_type = FLT_CON_TYPE_PAG_RESP; - ret = _cr_check_pag_resp(req->ctx, &hdr48->data[0], - hdr48_len - sizeof(*hdr48), imsi); - } else { - /* We only want to filter the above, let other things pass */ - *con_type = FLT_CON_TYPE_OTHER; - return 0; - } - - /* check if we are done */ - if (ret != 1) - return ret; - - /* the memory allocation failed */ - if (!*imsi) - return -1; - - /* now check the imsi */ - return auth_imsi(req, *imsi, cause); -} - -int bsc_msg_filter_data(struct gsm48_hdr *hdr48, size_t len, - struct bsc_filter_request *req, - struct bsc_filter_state *state, - struct bsc_filter_reject_cause *cause) -{ - uint8_t msg_type, proto; - - cause->cm_reject_cause = GSM48_REJECT_PLMN_NOT_ALLOWED; - cause->lu_reject_cause = GSM48_REJECT_PLMN_NOT_ALLOWED; - - if (state->imsi_checked) - return 0; - - proto = gsm48_hdr_pdisc(hdr48); - msg_type = gsm48_hdr_msg_type(hdr48); - if (proto != GSM48_PDISC_MM || msg_type != GSM48_MT_MM_ID_RESP) - return 0; - - return _dt_check_id_resp(req, &hdr48->data[0], - len - sizeof(*hdr48), state, cause); -} diff --git a/openbsc/src/libfilter/bsc_msg_vty.c b/openbsc/src/libfilter/bsc_msg_vty.c deleted file mode 100644 index c342fdca..00000000 --- a/openbsc/src/libfilter/bsc_msg_vty.c +++ /dev/null @@ -1,140 +0,0 @@ -/* (C) 2010-2015 by Holger Hans Peter Freyther - * (C) 2010-2013 by On-Waves - * All Rights Reserved - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation; either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - * - */ - -#include -#include -#include - -#include - -static struct llist_head *_acc_lst; -static void *_ctx; - -DEFUN(cfg_lst_no, - cfg_lst_no_cmd, - "no access-list NAME", - NO_STR "Remove an access-list by name\n" - "The access-list to remove\n") -{ - struct bsc_msg_acc_lst *acc; - acc = bsc_msg_acc_lst_find(_acc_lst, argv[0]); - if (!acc) - return CMD_WARNING; - - bsc_msg_acc_lst_delete(acc); - return CMD_SUCCESS; -} - -DEFUN(show_acc_lst, - show_acc_lst_cmd, - "show access-list NAME", - SHOW_STR "IMSI access list\n" "Name of the access list\n") -{ - struct bsc_msg_acc_lst *acc; - acc = bsc_msg_acc_lst_find(_acc_lst, argv[0]); - if (!acc) - return CMD_WARNING; - - vty_out(vty, "access-list %s%s", acc->name, VTY_NEWLINE); - vty_out_rate_ctr_group(vty, " ", acc->stats); - - return CMD_SUCCESS; -} - -DEFUN(cfg_lst_imsi_allow, - cfg_lst_imsi_allow_cmd, - "access-list NAME imsi-allow [REGEXP]", - "Access list commands\n" - "Name of the access list\n" - "Add allowed IMSI to the list\n" - "Regexp for IMSIs\n") -{ - struct bsc_msg_acc_lst *acc; - struct bsc_msg_acc_lst_entry *entry; - - acc = bsc_msg_acc_lst_get(_ctx, _acc_lst, argv[0]); - if (!acc) - return CMD_WARNING; - - entry = bsc_msg_acc_lst_entry_create(acc); - if (!entry) - return CMD_WARNING; - - if (gsm_parse_reg(acc, &entry->imsi_allow_re, &entry->imsi_allow, argc - 1, &argv[1]) != 0) - return CMD_WARNING; - return CMD_SUCCESS; -} - -DEFUN(cfg_lst_imsi_deny, - cfg_lst_imsi_deny_cmd, - "access-list NAME imsi-deny [REGEXP] (<0-256>) (<0-256>)", - "Access list commands\n" - "Name of the access list\n" - "Add denied IMSI to the list\n" - "Regexp for IMSIs\n" - "CM Service Reject reason\n" - "LU Reject reason\n") -{ - struct bsc_msg_acc_lst *acc; - struct bsc_msg_acc_lst_entry *entry; - - acc = bsc_msg_acc_lst_get(_ctx, _acc_lst, argv[0]); - if (!acc) - return CMD_WARNING; - - entry = bsc_msg_acc_lst_entry_create(acc); - if (!entry) - return CMD_WARNING; - - if (gsm_parse_reg(acc, &entry->imsi_deny_re, &entry->imsi_deny, argc - 1, &argv[1]) != 0) - return CMD_WARNING; - if (argc >= 3) - entry->cm_reject_cause = atoi(argv[2]); - if (argc >= 4) - entry->lu_reject_cause = atoi(argv[3]); - return CMD_SUCCESS; -} - -void bsc_msg_acc_lst_write(struct vty *vty, struct bsc_msg_acc_lst *lst) -{ - struct bsc_msg_acc_lst_entry *entry; - - llist_for_each_entry(entry, &lst->fltr_list, list) { - if (entry->imsi_allow) - vty_out(vty, " access-list %s imsi-allow %s%s", - lst->name, entry->imsi_allow, VTY_NEWLINE); - if (entry->imsi_deny) - vty_out(vty, " access-list %s imsi-deny %s %d %d%s", - lst->name, entry->imsi_deny, - entry->cm_reject_cause, entry->lu_reject_cause, - VTY_NEWLINE); - } -} - -void bsc_msg_lst_vty_init(void *ctx, struct llist_head *lst, int node) -{ - _ctx = ctx; - _acc_lst = lst; - install_element_ve(&show_acc_lst_cmd); - - /* access-list */ - install_element(node, &cfg_lst_imsi_allow_cmd); - install_element(node, &cfg_lst_imsi_deny_cmd); - install_element(node, &cfg_lst_no_cmd); -} diff --git a/openbsc/src/libiu/Makefile.am b/openbsc/src/libiu/Makefile.am deleted file mode 100644 index e5f9e275..00000000 --- a/openbsc/src/libiu/Makefile.am +++ /dev/null @@ -1,28 +0,0 @@ -AM_CPPFLAGS = \ - $(all_includes) \ - -I$(top_srcdir)/include \ - -I$(top_builddir) \ - $(NULL) - -AM_CFLAGS = \ - -Wall \ - $(COVERAGE_CFLAGS) \ - $(LIBCRYPTO_CFLAGS) \ - $(LIBASN1C_CFLAGS) \ - $(LIBOSMOCORE_CFLAGS) \ - $(LIBOSMOVTY_CFLAGS) \ - $(LIBOSMOGSM_CFLAGS) \ - $(LIBOSMOABIS_CFLAGS) \ - $(LIBOSMOSIGTRAN_CFLAGS) \ - $(LIBOSMORANAP_CFLAGS) \ - $(NULL) - -noinst_LIBRARIES = \ - libiu.a \ - $(NULL) - -libiu_a_SOURCES = \ - iu.c \ - iu_vty.c \ - $(NULL) - diff --git a/openbsc/src/libiu/iu.c b/openbsc/src/libiu/iu.c deleted file mode 100644 index 8ba6fa49..00000000 --- a/openbsc/src/libiu/iu.c +++ /dev/null @@ -1,759 +0,0 @@ -/* Common parts of IuCS and IuPS interfaces implementation */ - -/* (C) 2016 by sysmocom s.f.m.c. GmbH - * All Rights Reserved - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation; either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public 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 - -/* Parsed global RNC id. See also struct RANAP_GlobalRNC_ID, and note that the - * PLMN identity is a BCD representation of the MCC and MNC. - * See iu_grnc_id_parse(). */ -struct iu_grnc_id { - uint16_t mcc; - uint16_t mnc; - uint16_t rnc_id; -}; - -/* A remote RNC (Radio Network Controller, like BSC but for UMTS) that has - * called us and is currently reachable at the given osmo_sccp_link. So, when we - * know a LAC for a subscriber, we can page it at the RNC matching that LAC or - * RAC. An HNB-GW typically presents itself as if it were a single RNC, even - * though it may have several RNCs in hNodeBs connected to it. Those will then - * share the same RNC id, which they actually receive and adopt from the HNB-GW - * in the HNBAP HNB REGISTER ACCEPT message. */ -struct iu_rnc { - struct llist_head entry; - - uint16_t rnc_id; - uint16_t lac; /* Location Area Code (used for CS and PS) */ - uint8_t rac; /* Routing Area Code (used for PS only) */ - struct osmo_sccp_link *link; -}; - -void *talloc_iu_ctx; - -int asn1_xer_print = 1; -void *talloc_asn1_ctx; - -iu_recv_cb_t global_iu_recv_cb = NULL; -iu_event_cb_t global_iu_event_cb = NULL; - -static LLIST_HEAD(ue_conn_ctx_list); -static LLIST_HEAD(rnc_list); - -const struct value_string iu_event_type_names[] = { - OSMO_VALUE_STRING(IU_EVENT_RAB_ASSIGN), - OSMO_VALUE_STRING(IU_EVENT_SECURITY_MODE_COMPLETE), - OSMO_VALUE_STRING(IU_EVENT_IU_RELEASE), - OSMO_VALUE_STRING(IU_EVENT_LINK_INVALIDATED), - { 0, NULL } -}; - -struct ue_conn_ctx *ue_conn_ctx_alloc(struct osmo_sccp_link *link, uint32_t conn_id) -{ - struct ue_conn_ctx *ctx = talloc_zero(talloc_iu_ctx, struct ue_conn_ctx); - - ctx->link = link; - ctx->conn_id = conn_id; - llist_add(&ctx->list, &ue_conn_ctx_list); - - return ctx; -} - -struct ue_conn_ctx *ue_conn_ctx_find(struct osmo_sccp_link *link, - uint32_t conn_id) -{ - struct ue_conn_ctx *ctx; - - llist_for_each_entry(ctx, &ue_conn_ctx_list, list) { - if (ctx->link == link && ctx->conn_id == conn_id) - return ctx; - } - return NULL; -} - -static struct iu_rnc *iu_rnc_alloc(uint16_t rnc_id, uint16_t lac, uint8_t rac, - struct osmo_sccp_link *link) -{ - struct iu_rnc *rnc = talloc_zero(talloc_iu_ctx, struct iu_rnc); - - rnc->rnc_id = rnc_id; - rnc->lac = lac; - rnc->rac = rac; - rnc->link = link; - llist_add(&rnc->entry, &rnc_list); - - LOGP(DRANAP, LOGL_NOTICE, "New RNC %d (LAC=%d RAC=%d)\n", - rnc->rnc_id, rnc->lac, rnc->rac); - - return rnc; -} - -static struct iu_rnc *iu_rnc_register(uint16_t rnc_id, uint16_t lac, - uint8_t rac, struct osmo_sccp_link *link) -{ - struct iu_rnc *rnc; - llist_for_each_entry(rnc, &rnc_list, entry) { - if (rnc->rnc_id != rnc_id) - continue; - - /* We have this RNC Id registered already. Make sure that the - * details match. */ - - /* TODO should a mismatch be an error? */ - if (rnc->lac != lac || rnc->rac != rac) - LOGP(DRANAP, LOGL_NOTICE, "RNC %d changes its details:" - " LAC=%d RAC=%d --> LAC=%d RAC=%d\n", - rnc->rnc_id, rnc->lac, rnc->rac, - lac, rac); - rnc->lac = lac; - rnc->rac = rac; - - if (link && rnc->link != link) - LOGP(DRANAP, LOGL_NOTICE, "RNC %d on new link" - " (LAC=%d RAC=%d)\n", - rnc->rnc_id, rnc->lac, rnc->rac); - rnc->link = link; - return rnc; - } - - /* Not found, make a new one. */ - return iu_rnc_alloc(rnc_id, lac, rac, link); -} - -/* Discard/invalidate all ue_conn_ctx and iu_rnc entries that reference the - * given link, since this link is invalid and about to be deallocated. For - * each ue_conn_ctx, invoke the iu_event_cb_t with IU_EVENT_LINK_INVALIDATED. - */ -void iu_link_del(struct osmo_sccp_link *link) -{ - struct iu_rnc *rnc, *rnc_next; - llist_for_each_entry_safe(rnc, rnc_next, &rnc_list, entry) { - if (!rnc->link) - continue; - if (rnc->link != link) - continue; - rnc->link = NULL; - llist_del(&rnc->entry); - talloc_free(rnc); - } - - struct ue_conn_ctx *uec, *uec_next; - llist_for_each_entry_safe(uec, uec_next, &ue_conn_ctx_list, list) { - if (uec->link != link) - continue; - uec->link = NULL; - global_iu_event_cb(uec, IU_EVENT_LINK_INVALIDATED, NULL); - } -} - -/*********************************************************************** - * RANAP handling - ***********************************************************************/ - -int iu_rab_act(struct ue_conn_ctx *ue_ctx, struct msgb *msg) -{ - struct osmo_scu_prim *prim; - - /* wrap RANAP message in SCCP N-DATA.req */ - prim = (struct osmo_scu_prim *) msgb_push(msg, sizeof(*prim)); - prim->u.data.conn_id = ue_ctx->conn_id; - osmo_prim_init(&prim->oph, - SCCP_SAP_USER, - OSMO_SCU_PRIM_N_DATA, - PRIM_OP_REQUEST, - msg); - return osmo_sua_user_link_down(ue_ctx->link, &prim->oph); -} - -int iu_rab_deact(struct ue_conn_ctx *ue_ctx, uint8_t rab_id) -{ - /* FIXME */ - return -1; -} - -int iu_tx_sec_mode_cmd(struct ue_conn_ctx *uectx, struct gsm_auth_tuple *tp, - int send_ck, int new_key) -{ - struct osmo_scu_prim *prim; - struct msgb *msg; - uint8_t ik[16]; - uint8_t ck[16]; - unsigned int i; - - /* C5 function to derive IK from Kc */ - for (i = 0; i < 4; i++) - ik[i] = tp->vec.kc[i] ^ tp->vec.kc[i+4]; - memcpy(ik+4, tp->vec.kc, 8); - for (i = 12; i < 16; i++) - ik[i] = ik[i-12]; - - if (send_ck) { - /* C4 function to derive CK from Kc */ - memcpy(ck, tp->vec.kc, 8); - memcpy(ck+8, tp->vec.kc, 8); - } - - /* create RANAP message */ - msg = ranap_new_msg_sec_mod_cmd(ik, send_ck? ck : NULL, new_key ? RANAP_KeyStatus_new : RANAP_KeyStatus_old); - msg->l2h = msg->data; - /* wrap RANAP message in SCCP N-DATA.req */ - prim = (struct osmo_scu_prim *) msgb_push(msg, sizeof(*prim)); - prim->u.data.conn_id = uectx->conn_id; - osmo_prim_init(&prim->oph, SCCP_SAP_USER, - OSMO_SCU_PRIM_N_DATA, - PRIM_OP_REQUEST, msg); - osmo_sua_user_link_down(uectx->link, &prim->oph); - - return 0; -} - -static int iu_grnc_id_parse(struct iu_grnc_id *dst, - struct RANAP_GlobalRNC_ID *src) -{ - /* The size is coming from arbitrary sender, check it gracefully */ - if (src->pLMNidentity.size != 3) { - LOGP(DRANAP, LOGL_ERROR, "Invalid PLMN Identity size:" - " should be 3, is %d\n", src->pLMNidentity.size); - return -1; - } - gsm48_mcc_mnc_from_bcd(&src->pLMNidentity.buf[0], - &dst->mcc, &dst->mnc); - dst->rnc_id = (uint16_t)src->rNC_ID; - return 0; -} - -#if 0 - -- not used at present -- -static int iu_grnc_id_compose(struct iu_grnc_id *src, - struct RANAP_GlobalRNC_ID *dst) -{ - /* The caller must ensure proper size */ - OSMO_ASSERT(dst->pLMNidentity.size == 3); - gsm48_mcc_mnc_to_bcd(&dst->pLMNidentity.buf[0], - src->mcc, src->mnc); - dst->rNC_ID = src->rnc_id; - return 0; -} -#endif - -static int ranap_handle_co_initial_ue(void *ctx, RANAP_InitialUE_MessageIEs_t *ies) -{ - struct ue_conn_ctx *ue_conn = ctx; - struct gprs_ra_id ra_id; - struct iu_grnc_id grnc_id; - uint16_t sai; - struct msgb *msg = msgb_alloc(256, "RANAP->NAS"); - - if (ranap_parse_lai(&ra_id, &ies->lai) != 0) { - LOGP(DRANAP, LOGL_ERROR, "Failed to parse RANAP LAI IE\n"); - return -1; - } - - if (ies->presenceMask & INITIALUE_MESSAGEIES_RANAP_RAC_PRESENT) { - ra_id.rac = asn1str_to_u8(&ies->rac); - } - - if (iu_grnc_id_parse(&grnc_id, &ies->globalRNC_ID) != 0) { - LOGP(DRANAP, LOGL_ERROR, - "Failed to parse RANAP Global-RNC-ID IE\n"); - return -1; - } - - sai = asn1str_to_u16(&ies->sai.sAC); - msgb_gmmh(msg) = msgb_put(msg, ies->nas_pdu.size); - memcpy(msgb_gmmh(msg), ies->nas_pdu.buf, ies->nas_pdu.size); - - /* Make sure we know the RNC Id and LAC+RAC coming in on this connection. */ - iu_rnc_register(grnc_id.rnc_id, ra_id.lac, ra_id.rac, ue_conn->link); - ue_conn->ra_id = ra_id; - - /* Feed into the MM layer */ - msg->dst = ctx; - global_iu_recv_cb(msg, &ra_id, &sai); - - msgb_free(msg); - - return 0; -} - -static int ranap_handle_co_dt(void *ctx, RANAP_DirectTransferIEs_t *ies) -{ - struct gprs_ra_id _ra_id, *ra_id = NULL; - uint16_t _sai, *sai = NULL; - struct msgb *msg = msgb_alloc(256, "RANAP->NAS"); - - if (ies->presenceMask & DIRECTTRANSFERIES_RANAP_LAI_PRESENT) { - if (ranap_parse_lai(&_ra_id, &ies->lai) != 0) { - LOGP(DRANAP, LOGL_ERROR, "Failed to parse RANAP LAI IE\n"); - return -1; - } - ra_id = &_ra_id; - if (ies->presenceMask & DIRECTTRANSFERIES_RANAP_RAC_PRESENT) { - _ra_id.rac = asn1str_to_u8(&ies->rac); - } - if (ies->presenceMask & DIRECTTRANSFERIES_RANAP_SAI_PRESENT) { - _sai = asn1str_to_u16(&ies->sai.sAC); - sai = &_sai; - } - } - - msgb_gmmh(msg) = msgb_put(msg, ies->nas_pdu.size); - memcpy(msgb_gmmh(msg), ies->nas_pdu.buf, ies->nas_pdu.size); - - /* Feed into the MM/CC/SMS-CP layer */ - msg->dst = ctx; - global_iu_recv_cb(msg, ra_id, sai); - - msgb_free(msg); - - return 0; -} - -static int ranap_handle_co_err_ind(void *ctx, RANAP_ErrorIndicationIEs_t *ies) -{ - if (ies->presenceMask & ERRORINDICATIONIES_RANAP_CAUSE_PRESENT) - LOGP(DRANAP, LOGL_ERROR, "Rx Error Indication (%s)\n", - ranap_cause_str(&ies->cause)); - else - LOGP(DRANAP, LOGL_ERROR, "Rx Error Indication\n"); - - return 0; -} - -int iu_tx(struct msgb *msg_nas, uint8_t sapi) -{ - struct ue_conn_ctx *uectx = msg_nas->dst; - struct msgb *msg; - struct osmo_scu_prim *prim; - - LOGP(DRANAP, LOGL_INFO, "Transmitting L3 Message as RANAP DT (SUA link %p conn_id %u)\n", - uectx->link, uectx->conn_id); - - msg = ranap_new_msg_dt(sapi, msg_nas->data, msgb_length(msg_nas)); - msgb_free(msg_nas); - msg->l2h = msg->data; - prim = (struct osmo_scu_prim *) msgb_push(msg, sizeof(*prim)); - prim->u.data.conn_id = uectx->conn_id; - osmo_prim_init(&prim->oph, SCCP_SAP_USER, - OSMO_SCU_PRIM_N_DATA, - PRIM_OP_REQUEST, msg); - osmo_sua_user_link_down(uectx->link, &prim->oph); - return 0; -} - -static int ranap_handle_co_iu_rel_req(struct ue_conn_ctx *ctx, RANAP_Iu_ReleaseRequestIEs_t *ies) -{ - struct msgb *msg; - struct osmo_scu_prim *prim; - - LOGP(DRANAP, LOGL_INFO, "Received Iu Release Request, Sending Release Command\n"); - msg = ranap_new_msg_iu_rel_cmd(&ies->cause); - msg->l2h = msg->data; - prim = (struct osmo_scu_prim *) msgb_push(msg, sizeof(*prim)); - prim->u.data.conn_id = ctx->conn_id; - osmo_prim_init(&prim->oph, SCCP_SAP_USER, - OSMO_SCU_PRIM_N_DATA, - PRIM_OP_REQUEST, msg); - osmo_sua_user_link_down(ctx->link, &prim->oph); - return 0; -} - -static int ranap_handle_co_rab_ass_resp(struct ue_conn_ctx *ctx, RANAP_RAB_AssignmentResponseIEs_t *ies) -{ - int rc = -1; - - LOGP(DRANAP, LOGL_INFO, - "Rx RAB Assignment Response for UE conn_id %u\n", ctx->conn_id); - if (ies->presenceMask & RAB_ASSIGNMENTRESPONSEIES_RANAP_RAB_SETUPORMODIFIEDLIST_PRESENT) { - /* TODO: Iterate over list of SetupOrModifiedList IEs and handle each one */ - RANAP_IE_t *ranap_ie = ies->raB_SetupOrModifiedList.raB_SetupOrModifiedList_ies.list.array[0]; - RANAP_RAB_SetupOrModifiedItemIEs_t setup_ies; - - rc = ranap_decode_rab_setupormodifieditemies_fromlist(&setup_ies, &ranap_ie->value); - if (rc) { - LOGP(DRANAP, LOGL_ERROR, "Error in ranap_decode_rab_setupormodifieditemies()\n"); - return rc; - } - - rc = global_iu_event_cb(ctx, IU_EVENT_RAB_ASSIGN, &setup_ies); - - ranap_free_rab_setupormodifieditemies(&setup_ies); - } - - return rc; -} - -/* Entry point for connection-oriented RANAP message */ -static void cn_ranap_handle_co(void *ctx, ranap_message *message) -{ - int rc; - - LOGP(DRANAP, LOGL_NOTICE, "handle_co(dir=%u, proc=%u)\n", message->direction, message->procedureCode); - - switch (message->direction) { - case RANAP_RANAP_PDU_PR_initiatingMessage: - switch (message->procedureCode) { - case RANAP_ProcedureCode_id_InitialUE_Message: - rc = ranap_handle_co_initial_ue(ctx, &message->msg.initialUE_MessageIEs); - break; - case RANAP_ProcedureCode_id_DirectTransfer: - rc = ranap_handle_co_dt(ctx, &message->msg.directTransferIEs); - break; - case RANAP_ProcedureCode_id_ErrorIndication: - rc = ranap_handle_co_err_ind(ctx, &message->msg.errorIndicationIEs); - break; - case RANAP_ProcedureCode_id_Iu_ReleaseRequest: - /* Iu Release Request */ - rc = ranap_handle_co_iu_rel_req(ctx, &message->msg.iu_ReleaseRequestIEs); - break; - default: - LOGP(DRANAP, LOGL_ERROR, "Received Initiating Message: unknown Procedure Code %d\n", - message->procedureCode); - rc = -1; - break; - } - break; - case RANAP_RANAP_PDU_PR_successfulOutcome: - switch (message->procedureCode) { - case RANAP_ProcedureCode_id_SecurityModeControl: - /* Security Mode Complete */ - rc = global_iu_event_cb(ctx, IU_EVENT_SECURITY_MODE_COMPLETE, NULL); - break; - case RANAP_ProcedureCode_id_Iu_Release: - /* Iu Release Complete */ - rc = global_iu_event_cb(ctx, IU_EVENT_IU_RELEASE, NULL); - if (rc) { - LOGP(DRANAP, LOGL_ERROR, "Iu Release event: Iu Event callback returned %d\n", - rc); - } - break; - default: - LOGP(DRANAP, LOGL_ERROR, "Received Successful Outcome: unknown Procedure Code %d\n", - message->procedureCode); - rc = -1; - break; - } - break; - case RANAP_RANAP_PDU_PR_outcome: - switch (message->procedureCode) { - case RANAP_ProcedureCode_id_RAB_Assignment: - /* RAB Assignment Response */ - rc = ranap_handle_co_rab_ass_resp(ctx, &message->msg.raB_AssignmentResponseIEs); - break; - default: - LOGP(DRANAP, LOGL_ERROR, "Received Outcome: unknown Procedure Code %d\n", - message->procedureCode); - rc = -1; - break; - } - break; - case RANAP_RANAP_PDU_PR_unsuccessfulOutcome: - default: - LOGP(DRANAP, LOGL_ERROR, "Received Unsuccessful Outcome: Procedure Code %d\n", - message->procedureCode); - rc = -1; - break; - } - - if (rc) { - LOGP(DRANAP, LOGL_ERROR, "Error in cn_ranap_handle_co (%d)\n", - rc); - /* TODO handling of the error? */ - } -} - -static int ranap_handle_cl_reset_req(void *ctx, RANAP_ResetIEs_t *ies) -{ - /* FIXME: send reset response */ - return -1; -} - -static int ranap_handle_cl_err_ind(void *ctx, RANAP_ErrorIndicationIEs_t *ies) -{ - if (ies->presenceMask & ERRORINDICATIONIES_RANAP_CAUSE_PRESENT) - LOGP(DRANAP, LOGL_ERROR, "Rx Error Indication (%s)\n", - ranap_cause_str(&ies->cause)); - else - LOGP(DRANAP, LOGL_ERROR, "Rx Error Indication\n"); - - return 0; -} - -/* Entry point for connection-less RANAP message */ -static void cn_ranap_handle_cl(void *ctx, ranap_message *message) -{ - int rc; - - switch (message->direction) { - case RANAP_RANAP_PDU_PR_initiatingMessage: - switch (message->procedureCode) { - case RANAP_ProcedureCode_id_Reset: - /* received reset.req, send reset.resp */ - rc = ranap_handle_cl_reset_req(ctx, &message->msg.resetIEs); - break; - case RANAP_ProcedureCode_id_ErrorIndication: - rc = ranap_handle_cl_err_ind(ctx, &message->msg.errorIndicationIEs); - break; - default: - rc = -1; - break; - } - break; - case RANAP_RANAP_PDU_PR_successfulOutcome: - case RANAP_RANAP_PDU_PR_unsuccessfulOutcome: - case RANAP_RANAP_PDU_PR_outcome: - default: - rc = -1; - break; - } - - if (rc) { - LOGP(DRANAP, LOGL_ERROR, "Error in cn_ranap_handle_cl (%d)\n", - rc); - /* TODO handling of the error? */ - } -} - -/*********************************************************************** - * Paging - ***********************************************************************/ - -/* Send a paging command down a given SUA link. tmsi and paging_cause are - * optional and may be passed NULL and 0, respectively, to disable their use. - * See enum RANAP_PagingCause. - * - * If TMSI is given, the IMSI is not sent over the air interface. Nevertheless, - * the IMSI is still required for resolution in the HNB-GW and/or(?) RNC. */ -static int iu_tx_paging_cmd(struct osmo_sccp_link *link, - const char *imsi, const uint32_t *tmsi, - bool is_ps, uint32_t paging_cause) -{ - struct msgb *msg; - msg = ranap_new_msg_paging_cmd(imsi, tmsi, is_ps? 1 : 0, paging_cause); - msg->l2h = msg->data; - return osmo_sccp_tx_unitdata_ranap(link, 1, 2, msg->data, - msgb_length(msg)); -} - -static int iu_page(const char *imsi, const uint32_t *tmsi_or_ptimsi, - uint16_t lac, uint8_t rac, bool is_ps) -{ - struct iu_rnc *rnc; - int pagings_sent = 0; - - if (tmsi_or_ptimsi) { - LOGP(DRANAP, LOGL_DEBUG, "%s: Looking for RNCs to page for IMSI %s" - " (paging will use %s %x)\n", - is_ps? "IuPS" : "IuCS", - imsi, - is_ps? "PTMSI" : "TMSI", - *tmsi_or_ptimsi); - } else { - LOGP(DRANAP, LOGL_DEBUG, "%s: Looking for RNCs to page for IMSI %s" - " (paging will use IMSI)\n", - is_ps? "IuPS" : "IuCS", - imsi - ); - } - - llist_for_each_entry(rnc, &rnc_list, entry) { - if (!rnc->link) { - /* Not actually connected, don't count it. */ - continue; - } - if (rnc->lac != lac) - continue; - if (is_ps && rnc->rac != rac) - continue; - - /* Found a match! */ - if (iu_tx_paging_cmd(rnc->link, imsi, tmsi_or_ptimsi, is_ps, 0) - == 0) { - LOGP(DRANAP, LOGL_DEBUG, - "%s: Paged for IMSI %s on RNC %d, on SUA link %p\n", - is_ps? "IuPS" : "IuCS", - imsi, rnc->rnc_id, rnc->link); - pagings_sent ++; - } - } - - /* Some logging... */ - if (pagings_sent > 0) { - LOGP(DRANAP, LOGL_DEBUG, - "%s: %d RNCs were paged for IMSI %s.\n", - is_ps? "IuPS" : "IuCS", - pagings_sent, imsi); - } - else { - if (is_ps) { - LOGP(DRANAP, LOGL_ERROR, "IuPS: Found no RNC to page for" - " LAC %d RAC %d (would have paged IMSI %s)\n", - lac, rac, imsi); - } - else { - LOGP(DRANAP, LOGL_ERROR, "IuCS: Found no RNC to page for" - " LAC %d (would have paged IMSI %s)\n", - lac, imsi); - } - } - - return pagings_sent; -} - -int iu_page_cs(const char *imsi, const uint32_t *tmsi, uint16_t lac) -{ - return iu_page(imsi, tmsi, lac, 0, false); -} - -int iu_page_ps(const char *imsi, const uint32_t *ptmsi, uint16_t lac, uint8_t rac) -{ - return iu_page(imsi, ptmsi, lac, rac, true); -} - - -/*********************************************************************** - * - ***********************************************************************/ - -int tx_unitdata(struct osmo_sccp_link *link); -int tx_conn_req(struct osmo_sccp_link *link, uint32_t conn_id); - -struct osmo_prim_hdr *make_conn_req(uint32_t conn_id); -struct osmo_prim_hdr *make_dt1_req(uint32_t conn_id, const uint8_t *data, unsigned int len); - -struct osmo_prim_hdr *make_conn_resp(struct osmo_scu_connect_param *param) -{ - struct msgb *msg = msgb_alloc(1024, "conn_resp"); - struct osmo_scu_prim *prim; - - prim = (struct osmo_scu_prim *) msgb_put(msg, sizeof(*prim)); - osmo_prim_init(&prim->oph, SCCP_SAP_USER, - OSMO_SCU_PRIM_N_CONNECT, - PRIM_OP_RESPONSE, msg); - memcpy(&prim->u.connect, param, sizeof(prim->u.connect)); - return &prim->oph; -} - -static int sccp_sap_up(struct osmo_prim_hdr *oph, void *link) -{ - struct osmo_scu_prim *prim = (struct osmo_scu_prim *) oph; - struct osmo_prim_hdr *resp = NULL; - int rc; - struct ue_conn_ctx *ue; - - DEBUGP(DRANAP, "sccp_sap_up(%s)\n", osmo_scu_prim_name(oph)); - - switch (OSMO_PRIM_HDR(oph)) { - case OSMO_PRIM(OSMO_SCU_PRIM_N_CONNECT, PRIM_OP_CONFIRM): - /* confirmation of outbound connection */ - rc = -1; - break; - case OSMO_PRIM(OSMO_SCU_PRIM_N_CONNECT, PRIM_OP_INDICATION): - /* indication of new inbound connection request*/ - DEBUGP(DRANAP, "N-CONNECT.ind(X->%u)\n", prim->u.connect.conn_id); - if (/* prim->u.connect.called_addr.ssn != OSMO_SCCP_SSN_RANAP || */ - !msgb_l2(oph->msg) || msgb_l2len(oph->msg) == 0) { - LOGP(DRANAP, LOGL_NOTICE, - "Received invalid N-CONNECT.ind\n"); - return 0; - } - ue = ue_conn_ctx_alloc(link, prim->u.connect.conn_id); - /* first ensure the local SUA/SCCP socket is ACTIVE */ - resp = make_conn_resp(&prim->u.connect); - osmo_sua_user_link_down(link, resp); - /* then handle the RANAP payload */ - rc = ranap_cn_rx_co(cn_ranap_handle_co, ue, msgb_l2(oph->msg), msgb_l2len(oph->msg)); - break; - case OSMO_PRIM(OSMO_SCU_PRIM_N_DISCONNECT, PRIM_OP_INDICATION): - /* indication of disconnect */ - DEBUGP(DRANAP, "N-DISCONNECT.ind(%u)\n", - prim->u.disconnect.conn_id); - ue = ue_conn_ctx_find(link, prim->u.disconnect.conn_id); - rc = ranap_cn_rx_co(cn_ranap_handle_co, ue, msgb_l2(oph->msg), msgb_l2len(oph->msg)); - break; - case OSMO_PRIM(OSMO_SCU_PRIM_N_DATA, PRIM_OP_INDICATION): - /* connection-oriented data received */ - DEBUGP(DRANAP, "N-DATA.ind(%u, %s)\n", prim->u.data.conn_id, - osmo_hexdump(msgb_l2(oph->msg), msgb_l2len(oph->msg))); - /* resolve UE context */ - ue = ue_conn_ctx_find(link, prim->u.data.conn_id); - rc = ranap_cn_rx_co(cn_ranap_handle_co, ue, msgb_l2(oph->msg), msgb_l2len(oph->msg)); - break; - case OSMO_PRIM(OSMO_SCU_PRIM_N_UNITDATA, PRIM_OP_INDICATION): - /* connection-less data received */ - DEBUGP(DRANAP, "N-UNITDATA.ind(%s)\n", - osmo_hexdump(msgb_l2(oph->msg), msgb_l2len(oph->msg))); - rc = ranap_cn_rx_cl(cn_ranap_handle_cl, link, msgb_l2(oph->msg), msgb_l2len(oph->msg)); - break; - default: - rc = -1; - break; - } - - msgb_free(oph->msg); - return rc; -} - -int iu_init(void *ctx, const char *listen_addr, uint16_t listen_port, - iu_recv_cb_t iu_recv_cb, iu_event_cb_t iu_event_cb) -{ - struct osmo_sccp_user *user; - talloc_iu_ctx = talloc_named_const(ctx, 1, "iu"); - talloc_asn1_ctx = talloc_named_const(talloc_iu_ctx, 1, "asn1"); - - global_iu_recv_cb = iu_recv_cb; - global_iu_event_cb = iu_event_cb; - osmo_sua_set_log_area(DSUA); - user = osmo_sua_user_create(talloc_iu_ctx, sccp_sap_up, talloc_iu_ctx); - return osmo_sua_server_listen(user, listen_addr, listen_port); -} - diff --git a/openbsc/src/libiu/iu_vty.c b/openbsc/src/libiu/iu_vty.c deleted file mode 100644 index 91eed96b..00000000 --- a/openbsc/src/libiu/iu_vty.c +++ /dev/null @@ -1,50 +0,0 @@ -/* OpenBSC Iu related interface to quagga VTY */ -/* (C) 2016 by sysmocom s.m.f.c. GmbH - * All Rights Reserved - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation; either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public 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 - -/* Pointer to the actual asn_debug value as passed from main scopes. */ -static int *g_asn_debug_p = NULL; - -DEFUN(logging_asn_debug, - logging_asn_debug_cmd, - "logging asn1-debug (1|0)", - LOGGING_STR - "Log human readable representations of all ASN.1 messages to stderr\n" - "Log decoded ASN.1 messages to stderr\n" - "Do not log decoded ASN.1 messages to stderr\n") -{ - if (!g_asn_debug_p) { - vty_out(vty, "%%ASN.1 debugging not available%s", VTY_NEWLINE); - return CMD_WARNING; - } - - *g_asn_debug_p = atoi(argv[0]); - return CMD_SUCCESS; -} - -void iu_vty_init(int *asn_debug_p) -{ - g_asn_debug_p = asn_debug_p; - - install_element(CFG_LOG_NODE, &logging_asn_debug_cmd); -} diff --git a/openbsc/src/libmgcp/Makefile.am b/openbsc/src/libmgcp/Makefile.am deleted file mode 100644 index 5faf6027..00000000 --- a/openbsc/src/libmgcp/Makefile.am +++ /dev/null @@ -1,43 +0,0 @@ -AM_CPPFLAGS = \ - $(all_includes) \ - -I$(top_srcdir)/include \ - -I$(top_builddir) \ - $(NULL) - -AM_CFLAGS = \ - -Wall \ - $(LIBOSMOCORE_CFLAGS) \ - $(LIBOSMOVTY_CFLAGS) \ - $(LIBOSMONETIF_CFLAGS) \ - $(COVERAGE_CFLAGS) \ - $(LIBBCG729_CFLAGS) \ - $(NULL) - -AM_LDFLAGS = \ - $(LIBOSMOCORE_LIBS) \ - $(LIBOSMOGSM_LIBS) \ - $(LIBOSMONETIF_LIBS) \ - $(COVERAGE_LDFLAGS) \ - $(LIBBCG729_LIBS) \ - $(NULL) - -noinst_LIBRARIES = \ - libmgcp.a \ - $(NULL) - -noinst_HEADERS = \ - g711common.h \ - $(NULL) - -libmgcp_a_SOURCES = \ - mgcp_protocol.c \ - mgcp_network.c \ - mgcp_vty.c \ - mgcp_osmux.c \ - mgcp_sdp.c \ - $(NULL) -if BUILD_MGCP_TRANSCODING -libmgcp_a_SOURCES += \ - mgcp_transcode.c \ - $(NULL) -endif diff --git a/openbsc/src/libmgcp/g711common.h b/openbsc/src/libmgcp/g711common.h deleted file mode 100644 index cb35fc65..00000000 --- a/openbsc/src/libmgcp/g711common.h +++ /dev/null @@ -1,187 +0,0 @@ -/* - * PCM - A-Law conversion - * Copyright (c) 2000 by Abramo Bagnara - * - * Wrapper for linphone Codec class by Simon Morlat - * - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -static inline int val_seg(int val) -{ - int r = 0; - val >>= 7; /*7 = 4 + 3*/ - if (val & 0xf0) { - val >>= 4; - r += 4; - } - if (val & 0x0c) { - val >>= 2; - r += 2; - } - if (val & 0x02) - r += 1; - return r; -} - -/* - * s16_to_alaw() - Convert a 16-bit linear PCM value to 8-bit A-law - * - * s16_to_alaw() accepts an 16-bit integer and encodes it as A-law data. - * - * Linear Input Code Compressed Code - * ------------------------ --------------- - * 0000000wxyza 000wxyz - * 0000001wxyza 001wxyz - * 000001wxyzab 010wxyz - * 00001wxyzabc 011wxyz - * 0001wxyzabcd 100wxyz - * 001wxyzabcde 101wxyz - * 01wxyzabcdef 110wxyz - * 1wxyzabcdefg 111wxyz - * - * For further information see John C. Bellamy's Digital Telephony, 1982, - * John Wiley & Sons, pps 98-111 and 472-476. - * G711 is designed for 13 bits input signal, this function add extra shifting to take this into account. - */ - -static inline unsigned char s16_to_alaw(int pcm_val) -{ - int mask; - int seg; - unsigned char aval; - - if (pcm_val >= 0) { - mask = 0xD5; - } else { - mask = 0x55; - pcm_val = -pcm_val; - if (pcm_val > 0x7fff) - pcm_val = 0x7fff; - } - - if (pcm_val < 256) /*256 = 32 << 3*/ - aval = pcm_val >> 4; /*4 = 1 + 3*/ - else { - /* Convert the scaled magnitude to segment number. */ - seg = val_seg(pcm_val); - aval = (seg << 4) | ((pcm_val >> (seg + 3)) & 0x0f); - } - return aval ^ mask; -} - -/* - * alaw_to_s16() - Convert an A-law value to 16-bit linear PCM - * - */ -static inline int alaw_to_s16(unsigned char a_val) -{ - int t; - int seg; - - a_val ^= 0x55; - t = a_val & 0x7f; - if (t < 16) - t = (t << 4) + 8; - else { - seg = (t >> 4) & 0x07; - t = ((t & 0x0f) << 4) + 0x108; - t <<= seg -1; - } - return ((a_val & 0x80) ? t : -t); -} -/* - * s16_to_ulaw() - Convert a linear PCM value to u-law - * - * In order to simplify the encoding process, the original linear magnitude - * is biased by adding 33 which shifts the encoding range from (0 - 8158) to - * (33 - 8191). The result can be seen in the following encoding table: - * - * Biased Linear Input Code Compressed Code - * ------------------------ --------------- - * 00000001wxyza 000wxyz - * 0000001wxyzab 001wxyz - * 000001wxyzabc 010wxyz - * 00001wxyzabcd 011wxyz - * 0001wxyzabcde 100wxyz - * 001wxyzabcdef 101wxyz - * 01wxyzabcdefg 110wxyz - * 1wxyzabcdefgh 111wxyz - * - * Each biased linear code has a leading 1 which identifies the segment - * number. The value of the segment number is equal to 7 minus the number - * of leading 0's. The quantization interval is directly available as the - * four bits wxyz. * The trailing bits (a - h) are ignored. - * - * Ordinarily the complement of the resulting code word is used for - * transmission, and so the code word is complemented before it is returned. - * - * For further information see John C. Bellamy's Digital Telephony, 1982, - * John Wiley & Sons, pps 98-111 and 472-476. - */ - -static inline unsigned char s16_to_ulaw(int pcm_val) /* 2's complement (16-bit range) */ -{ - int mask; - int seg; - unsigned char uval; - - if (pcm_val < 0) { - pcm_val = 0x84 - pcm_val; - mask = 0x7f; - } else { - pcm_val += 0x84; - mask = 0xff; - } - if (pcm_val > 0x7fff) - pcm_val = 0x7fff; - - /* Convert the scaled magnitude to segment number. */ - seg = val_seg(pcm_val); - - /* - * Combine the sign, segment, quantization bits; - * and complement the code word. - */ - uval = (seg << 4) | ((pcm_val >> (seg + 3)) & 0x0f); - return uval ^ mask; -} - -/* - * ulaw_to_s16() - Convert a u-law value to 16-bit linear PCM - * - * First, a biased linear code is derived from the code word. An unbiased - * output can then be obtained by subtracting 33 from the biased code. - * - * Note that this function expects to be passed the complement of the - * original code word. This is in keeping with ISDN conventions. - */ -static inline int ulaw_to_s16(unsigned char u_val) -{ - int t; - - /* Complement to obtain normal u-law value. */ - u_val = ~u_val; - - /* - * Extract and bias the quantization bits. Then - * shift up by the segment number and subtract out the bias. - */ - t = ((u_val & 0x0f) << 3) + 0x84; - t <<= (u_val & 0x70) >> 4; - - return ((u_val & 0x80) ? (0x84 - t) : (t - 0x84)); -} diff --git a/openbsc/src/libmgcp/mgcp_network.c b/openbsc/src/libmgcp/mgcp_network.c deleted file mode 100644 index abce6e49..00000000 --- a/openbsc/src/libmgcp/mgcp_network.c +++ /dev/null @@ -1,1064 +0,0 @@ -/* A Media Gateway Control Protocol Media Gateway: RFC 3435 */ -/* The protocol implementation */ - -/* - * (C) 2009-2012 by Holger Hans Peter Freyther - * (C) 2009-2012 by On-Waves - * All Rights Reserved - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation; either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - * - */ - -#include -#include -#include -#include -#include -#include - -#include -#include - -#include -#include - -#include - -#include -#include - -#include - -#warning "Make use of the rtp proxy code" - - -#define RTP_SEQ_MOD (1 << 16) -#define RTP_MAX_DROPOUT 3000 -#define RTP_MAX_MISORDER 100 -#define RTP_BUF_SIZE 4096 - -enum { - MGCP_PROTO_RTP, - MGCP_PROTO_RTCP, -}; - -/** - * This does not need to be a precision timestamp and - * is allowed to wrap quite fast. The returned value is - * 1/unit seconds. - */ -static uint32_t get_current_ts(unsigned unit) -{ - struct timespec tp; - uint64_t ret; - - if (!unit) - return 0; - - memset(&tp, 0, sizeof(tp)); - if (clock_gettime(CLOCK_MONOTONIC, &tp) != 0) - LOGP(DMGCP, LOGL_NOTICE, - "Getting the clock failed.\n"); - - /* convert it to 1/unit seconds */ - ret = tp.tv_sec; - ret *= unit; - ret += (int64_t)tp.tv_nsec * unit / 1000 / 1000 / 1000; - - return ret; -} - -int mgcp_udp_send(int fd, struct in_addr *addr, int port, char *buf, int len) -{ - struct sockaddr_in out; - out.sin_family = AF_INET; - out.sin_port = port; - memcpy(&out.sin_addr, addr, sizeof(*addr)); - - return sendto(fd, buf, len, 0, (struct sockaddr *)&out, sizeof(out)); -} - -int mgcp_send_dummy(struct mgcp_endpoint *endp) -{ - static char buf[] = { MGCP_DUMMY_LOAD }; - int rc; - int was_rtcp = 0; - - rc = mgcp_udp_send(endp->net_end.rtp.fd, &endp->net_end.addr, - endp->net_end.rtp_port, buf, 1); - - if (rc == -1) - goto failed; - - if (endp->tcfg->omit_rtcp) - return rc; - - was_rtcp = 1; - rc = mgcp_udp_send(endp->net_end.rtcp.fd, &endp->net_end.addr, - endp->net_end.rtcp_port, buf, 1); - - if (rc >= 0) - return rc; - -failed: - LOGP(DMGCP, LOGL_ERROR, - "Failed to send dummy %s packet: %s on: 0x%x to %s:%d\n", - was_rtcp ? "RTCP" : "RTP", - strerror(errno), ENDPOINT_NUMBER(endp), inet_ntoa(endp->net_end.addr), - was_rtcp ? endp->net_end.rtcp_port : endp->net_end.rtp_port); - - return -1; -} - -static int32_t compute_timestamp_aligment_error(struct mgcp_rtp_stream_state *sstate, - int ptime, uint32_t timestamp) -{ - int32_t timestamp_delta; - - if (ptime == 0) - return 0; - - /* Align according to: T - Tlast = k * Tptime */ - timestamp_delta = timestamp - sstate->last_timestamp; - - return timestamp_delta % ptime; -} - -static int check_rtp_timestamp(struct mgcp_endpoint *endp, - struct mgcp_rtp_state *state, - struct mgcp_rtp_stream_state *sstate, - struct mgcp_rtp_end *rtp_end, - struct sockaddr_in *addr, - uint16_t seq, uint32_t timestamp, - const char *text, int32_t *tsdelta_out) -{ - int32_t tsdelta; - int32_t timestamp_error; - - /* Not fully intialized, skip */ - if (sstate->last_tsdelta == 0 && timestamp == sstate->last_timestamp) - return 0; - - if (seq == sstate->last_seq) { - if (timestamp != sstate->last_timestamp) { - sstate->err_ts_counter += 1; - LOGP(DMGCP, LOGL_ERROR, - "The %s timestamp delta is != 0 but the sequence " - "number %d is the same, " - "TS offset: %d, SeqNo offset: %d " - "on 0x%x SSRC: %u timestamp: %u " - "from %s:%d in %d\n", - text, seq, - state->timestamp_offset, state->seq_offset, - ENDPOINT_NUMBER(endp), sstate->ssrc, timestamp, - inet_ntoa(addr->sin_addr), ntohs(addr->sin_port), - endp->conn_mode); - } - return 0; - } - - tsdelta = - (int32_t)(timestamp - sstate->last_timestamp) / - (int16_t)(seq - sstate->last_seq); - - if (tsdelta == 0) { - /* Don't update *tsdelta_out */ - LOGP(DMGCP, LOGL_NOTICE, - "The %s timestamp delta is %d " - "on 0x%x SSRC: %u timestamp: %u " - "from %s:%d in %d\n", - text, tsdelta, - ENDPOINT_NUMBER(endp), sstate->ssrc, timestamp, - inet_ntoa(addr->sin_addr), ntohs(addr->sin_port), - endp->conn_mode); - - return 0; - } - - if (sstate->last_tsdelta != tsdelta) { - if (sstate->last_tsdelta) { - LOGP(DMGCP, LOGL_INFO, - "The %s timestamp delta changes from %d to %d " - "on 0x%x SSRC: %u timestamp: %u from %s:%d in %d\n", - text, sstate->last_tsdelta, tsdelta, - ENDPOINT_NUMBER(endp), sstate->ssrc, timestamp, - inet_ntoa(addr->sin_addr), ntohs(addr->sin_port), - endp->conn_mode); - } - } - - if (tsdelta_out) - *tsdelta_out = tsdelta; - - timestamp_error = - compute_timestamp_aligment_error(sstate, state->packet_duration, - timestamp); - - if (timestamp_error) { - sstate->err_ts_counter += 1; - LOGP(DMGCP, LOGL_NOTICE, - "The %s timestamp has an alignment error of %d " - "on 0x%x SSRC: %u " - "SeqNo delta: %d, TS delta: %d, dTS/dSeq: %d " - "from %s:%d in mode %d. ptime: %d\n", - text, timestamp_error, - ENDPOINT_NUMBER(endp), sstate->ssrc, - (int16_t)(seq - sstate->last_seq), - (int32_t)(timestamp - sstate->last_timestamp), - tsdelta, - inet_ntoa(addr->sin_addr), ntohs(addr->sin_port), - endp->conn_mode, state->packet_duration); - } - return 1; -} - -/* Set the timestamp offset according to the packet duration. */ -static int adjust_rtp_timestamp_offset(struct mgcp_endpoint *endp, - struct mgcp_rtp_state *state, - struct mgcp_rtp_end *rtp_end, - struct sockaddr_in *addr, - int16_t delta_seq, uint32_t in_timestamp) -{ - int32_t tsdelta = state->packet_duration; - int timestamp_offset; - uint32_t out_timestamp; - - if (tsdelta == 0) { - tsdelta = state->out_stream.last_tsdelta; - if (tsdelta != 0) { - LOGP(DMGCP, LOGL_NOTICE, - "A fixed packet duration is not available on 0x%x, " - "using last output timestamp delta instead: %d " - "from %s:%d in %d\n", - ENDPOINT_NUMBER(endp), tsdelta, - inet_ntoa(addr->sin_addr), ntohs(addr->sin_port), - endp->conn_mode); - } else { - tsdelta = rtp_end->codec.rate * 20 / 1000; - LOGP(DMGCP, LOGL_NOTICE, - "Fixed packet duration and last timestamp delta " - "are not available on 0x%x, " - "using fixed 20ms instead: %d " - "from %s:%d in %d\n", - ENDPOINT_NUMBER(endp), tsdelta, - inet_ntoa(addr->sin_addr), ntohs(addr->sin_port), - endp->conn_mode); - } - } - - out_timestamp = state->out_stream.last_timestamp + delta_seq * tsdelta; - timestamp_offset = out_timestamp - in_timestamp; - - if (state->timestamp_offset != timestamp_offset) { - state->timestamp_offset = timestamp_offset; - - LOGP(DMGCP, LOGL_NOTICE, - "Timestamp offset change on 0x%x SSRC: %u " - "SeqNo delta: %d, TS offset: %d, " - "from %s:%d in %d\n", - ENDPOINT_NUMBER(endp), state->in_stream.ssrc, - delta_seq, state->timestamp_offset, - inet_ntoa(addr->sin_addr), ntohs(addr->sin_port), - endp->conn_mode); - } - - return timestamp_offset; -} - -/* Set the timestamp offset according to the packet duration. */ -static int align_rtp_timestamp_offset(struct mgcp_endpoint *endp, - struct mgcp_rtp_state *state, - struct mgcp_rtp_end *rtp_end, - struct sockaddr_in *addr, - uint32_t timestamp) -{ - int timestamp_error = 0; - int ptime = state->packet_duration; - - /* Align according to: T + Toffs - Tlast = k * Tptime */ - - timestamp_error = compute_timestamp_aligment_error( - &state->out_stream, ptime, - timestamp + state->timestamp_offset); - - if (timestamp_error) { - state->timestamp_offset += ptime - timestamp_error; - - LOGP(DMGCP, LOGL_NOTICE, - "Corrected timestamp alignment error of %d on 0x%x SSRC: %u " - "new TS offset: %d, " - "from %s:%d in %d\n", - timestamp_error, - ENDPOINT_NUMBER(endp), state->in_stream.ssrc, - state->timestamp_offset, inet_ntoa(addr->sin_addr), - ntohs(addr->sin_port), endp->conn_mode); - } - - OSMO_ASSERT(compute_timestamp_aligment_error(&state->out_stream, ptime, - timestamp + state->timestamp_offset) == 0); - - return timestamp_error; -} - -int mgcp_rtp_processing_default(struct mgcp_endpoint *endp, struct mgcp_rtp_end *dst_end, - char *data, int *len, int buf_size) -{ - return 0; -} - -int mgcp_setup_rtp_processing_default(struct mgcp_endpoint *endp, - struct mgcp_rtp_end *dst_end, - struct mgcp_rtp_end *src_end) -{ - return 0; -} - -void mgcp_get_net_downlink_format_default(struct mgcp_endpoint *endp, - int *payload_type, - const char**audio_name, - const char**fmtp_extra) -{ - /* Use the BTS side parameters when passing the SDP data (for - * downlink) to the net peer. - */ - *payload_type = endp->bts_end.codec.payload_type; - *audio_name = endp->bts_end.codec.audio_name; - *fmtp_extra = endp->bts_end.fmtp_extra; -} - - -void mgcp_rtp_annex_count(struct mgcp_endpoint *endp, struct mgcp_rtp_state *state, - const uint16_t seq, const int32_t transit, - const uint32_t ssrc) -{ - int32_t d; - - /* initialize or re-initialize */ - if (!state->stats_initialized || state->stats_ssrc != ssrc) { - state->stats_initialized = 1; - state->stats_base_seq = seq; - state->stats_max_seq = seq - 1; - state->stats_ssrc = ssrc; - state->stats_jitter = 0; - state->stats_transit = transit; - state->stats_cycles = 0; - } else { - uint16_t udelta; - - /* - * The below takes the shape of the validation of - * Appendix A. Check if there is something weird with - * the sequence number, otherwise check for a wrap - * around in the sequence number. - * It can't wrap during the initialization so let's - * skip it here. The Appendix A probably doesn't have - * this issue because of the probation. - */ - udelta = seq - state->stats_max_seq; - if (udelta < RTP_MAX_DROPOUT) { - if (seq < state->stats_max_seq) - state->stats_cycles += RTP_SEQ_MOD; - } else if (udelta <= RTP_SEQ_MOD - RTP_MAX_MISORDER) { - LOGP(DMGCP, LOGL_NOTICE, - "RTP seqno made a very large jump on 0x%x delta: %u\n", - ENDPOINT_NUMBER(endp), udelta); - } - } - - /* - * Calculate the jitter between the two packages. The TS should be - * taken closer to the read function. This was taken from the - * Appendix A of RFC 3550. Timestamp and arrival_time have a 1/rate - * resolution. - */ - d = transit - state->stats_transit; - state->stats_transit = transit; - if (d < 0) - d = -d; - state->stats_jitter += d - ((state->stats_jitter + 8) >> 4); - state->stats_max_seq = seq; -} - - - -/** - * The RFC 3550 Appendix A assumes there are multiple sources but - * some of the supported endpoints (e.g. the nanoBTS) can only handle - * one source and this code will patch RTP header to appear as if there - * is only one source. - * There is also no probation period for new sources. Every RTP header - * we receive will be seen as a switch in streams. - */ -void mgcp_patch_and_count(struct mgcp_endpoint *endp, struct mgcp_rtp_state *state, - struct mgcp_rtp_end *rtp_end, struct sockaddr_in *addr, - char *data, int len) -{ - uint32_t arrival_time; - int32_t transit; - uint16_t seq; - uint32_t timestamp, ssrc; - struct rtp_hdr *rtp_hdr; - int payload = rtp_end->codec.payload_type; - - if (len < sizeof(*rtp_hdr)) - return; - - rtp_hdr = (struct rtp_hdr *) data; - seq = ntohs(rtp_hdr->sequence); - timestamp = ntohl(rtp_hdr->timestamp); - arrival_time = get_current_ts(rtp_end->codec.rate); - ssrc = ntohl(rtp_hdr->ssrc); - transit = arrival_time - timestamp; - - mgcp_rtp_annex_count(endp, state, seq, transit, ssrc); - - if (!state->initialized) { - state->initialized = 1; - state->in_stream.last_seq = seq - 1; - state->in_stream.ssrc = state->orig_ssrc = ssrc; - state->in_stream.last_tsdelta = 0; - state->packet_duration = mgcp_rtp_packet_duration(endp, rtp_end); - state->out_stream = state->in_stream; - state->out_stream.last_timestamp = timestamp; - state->out_stream.ssrc = ssrc - 1; /* force output SSRC change */ - LOGP(DMGCP, LOGL_INFO, - "Initializing stream on 0x%x SSRC: %u timestamp: %u " - "pkt-duration: %d, from %s:%d in %d\n", - ENDPOINT_NUMBER(endp), state->in_stream.ssrc, - state->seq_offset, state->packet_duration, - inet_ntoa(addr->sin_addr), ntohs(addr->sin_port), - endp->conn_mode); - if (state->packet_duration == 0) { - state->packet_duration = rtp_end->codec.rate * 20 / 1000; - LOGP(DMGCP, LOGL_NOTICE, - "Fixed packet duration is not available on 0x%x, " - "using fixed 20ms instead: %d from %s:%d in %d\n", - ENDPOINT_NUMBER(endp), state->packet_duration, - inet_ntoa(addr->sin_addr), ntohs(addr->sin_port), - endp->conn_mode); - } - } else if (state->in_stream.ssrc != ssrc) { - LOGP(DMGCP, LOGL_NOTICE, - "The SSRC changed on 0x%x: %u -> %u " - "from %s:%d in %d\n", - ENDPOINT_NUMBER(endp), - state->in_stream.ssrc, rtp_hdr->ssrc, - inet_ntoa(addr->sin_addr), ntohs(addr->sin_port), - endp->conn_mode); - - state->in_stream.ssrc = ssrc; - if (rtp_end->force_constant_ssrc) { - int16_t delta_seq; - - /* Always increment seqno by 1 */ - state->seq_offset = - (state->out_stream.last_seq + 1) - seq; - - /* Estimate number of packets that would have been sent */ - delta_seq = - (arrival_time - state->in_stream.last_arrival_time - + state->packet_duration/2) / - state->packet_duration; - - adjust_rtp_timestamp_offset(endp, state, rtp_end, addr, - delta_seq, timestamp); - - state->patch_ssrc = 1; - ssrc = state->orig_ssrc; - if (rtp_end->force_constant_ssrc != -1) - rtp_end->force_constant_ssrc -= 1; - - LOGP(DMGCP, LOGL_NOTICE, - "SSRC patching enabled on 0x%x SSRC: %u " - "SeqNo offset: %d, TS offset: %d " - "from %s:%d in %d\n", - ENDPOINT_NUMBER(endp), state->in_stream.ssrc, - state->seq_offset, state->timestamp_offset, - inet_ntoa(addr->sin_addr), ntohs(addr->sin_port), - endp->conn_mode); - } - - state->in_stream.last_tsdelta = 0; - } else { - /* Compute current per-packet timestamp delta */ - check_rtp_timestamp(endp, state, &state->in_stream, rtp_end, addr, - seq, timestamp, "input", - &state->in_stream.last_tsdelta); - - if (state->patch_ssrc) - ssrc = state->orig_ssrc; - } - - /* Save before patching */ - state->in_stream.last_timestamp = timestamp; - state->in_stream.last_seq = seq; - state->in_stream.last_arrival_time = arrival_time; - - if (rtp_end->force_aligned_timing && - state->out_stream.ssrc == ssrc && state->packet_duration) - /* Align the timestamp offset */ - align_rtp_timestamp_offset(endp, state, rtp_end, addr, timestamp); - - /* Store the updated SSRC back to the packet */ - if (state->patch_ssrc) - rtp_hdr->ssrc = htonl(ssrc); - - /* Apply the offset and store it back to the packet. - * This won't change anything if the offset is 0, so the conditional is - * omitted. */ - seq += state->seq_offset; - rtp_hdr->sequence = htons(seq); - timestamp += state->timestamp_offset; - rtp_hdr->timestamp = htonl(timestamp); - - /* Check again, whether the timestamps are still valid */ - if (state->out_stream.ssrc == ssrc) - check_rtp_timestamp(endp, state, &state->out_stream, rtp_end, - addr, seq, timestamp, "output", - &state->out_stream.last_tsdelta); - - /* Save output values */ - state->out_stream.last_seq = seq; - state->out_stream.last_timestamp = timestamp; - state->out_stream.ssrc = ssrc; - - if (payload < 0) - return; - - rtp_hdr->payload_type = payload; -} - -/* - * The below code is for dispatching. We have a dedicated port for - * the data coming from the net and one to discover the BTS. - */ -static int forward_data(int fd, struct mgcp_rtp_tap *tap, const char *buf, int len) -{ - if (!tap->enabled) - return 0; - - return sendto(fd, buf, len, 0, - (struct sockaddr *)&tap->forward, sizeof(tap->forward)); -} - -static int mgcp_send_transcoder(struct mgcp_rtp_end *end, - struct mgcp_config *cfg, int is_rtp, - const char *buf, int len) -{ - int rc; - int port; - struct sockaddr_in addr; - - port = is_rtp ? end->rtp_port : end->rtcp_port; - - addr.sin_family = AF_INET; - addr.sin_addr = cfg->transcoder_in; - addr.sin_port = port; - - rc = sendto(is_rtp ? - end->rtp.fd : - end->rtcp.fd, buf, len, 0, - (struct sockaddr *) &addr, sizeof(addr)); - - if (rc != len) - LOGP(DMGCP, LOGL_ERROR, - "Failed to send data to the transcoder: %s\n", - strerror(errno)); - - return rc; -} - -int mgcp_send(struct mgcp_endpoint *endp, int dest, int is_rtp, - struct sockaddr_in *addr, char *buf, int rc) -{ - struct mgcp_trunk_config *tcfg = endp->tcfg; - struct mgcp_rtp_end *rtp_end; - struct mgcp_rtp_state *rtp_state; - int tap_idx; - - /* For loop toggle the destination and then dispatch. */ - if (tcfg->audio_loop) - dest = !dest; - - /* Loop based on the conn_mode, maybe undoing the above */ - if (endp->conn_mode == MGCP_CONN_LOOPBACK) - dest = !dest; - - if (dest == MGCP_DEST_NET) { - rtp_end = &endp->net_end; - rtp_state = &endp->bts_state; - tap_idx = MGCP_TAP_NET_OUT; - } else { - rtp_end = &endp->bts_end; - rtp_state = &endp->net_state; - tap_idx = MGCP_TAP_BTS_OUT; - } - - if (!rtp_end->output_enabled) - rtp_end->dropped_packets += 1; - else if (is_rtp) { - int cont; - int nbytes = 0; - int len = rc; - do { - cont = endp->cfg->rtp_processing_cb(endp, rtp_end, - buf, &len, RTP_BUF_SIZE); - if (cont < 0) - break; - - mgcp_patch_and_count(endp, rtp_state, rtp_end, addr, buf, len); - forward_data(rtp_end->rtp.fd, &endp->taps[tap_idx], - buf, len); - rc = mgcp_udp_send(rtp_end->rtp.fd, - &rtp_end->addr, - rtp_end->rtp_port, buf, len); - - if (rc <= 0) - return rc; - nbytes += rc; - len = cont; - } while (len > 0); - return nbytes; - } else if (!tcfg->omit_rtcp) { - return mgcp_udp_send(rtp_end->rtcp.fd, - &rtp_end->addr, - rtp_end->rtcp_port, buf, rc); - } - - return 0; -} - -static int receive_from(struct mgcp_endpoint *endp, int fd, struct sockaddr_in *addr, - char *buf, int bufsize) -{ - int rc; - socklen_t slen = sizeof(*addr); - - rc = recvfrom(fd, buf, bufsize, 0, - (struct sockaddr *) addr, &slen); - if (rc < 0) { - LOGP(DMGCP, LOGL_ERROR, "Failed to receive message on: 0x%x errno: %d/%s\n", - ENDPOINT_NUMBER(endp), errno, strerror(errno)); - return -1; - } - - /* do not forward aynthing... maybe there is a packet from the bts */ - if (!endp->allocated) - return -1; - - #warning "Slight spec violation. With connection mode recvonly we should attempt to forward." - - return rc; -} - -static int rtp_data_net(struct osmo_fd *fd, unsigned int what) -{ - char buf[RTP_BUF_SIZE]; - struct sockaddr_in addr; - struct mgcp_endpoint *endp; - int rc, proto; - - endp = (struct mgcp_endpoint *) fd->data; - - rc = receive_from(endp, fd->fd, &addr, buf, sizeof(buf)); - if (rc <= 0) - return -1; - - if (memcmp(&addr.sin_addr, &endp->net_end.addr, sizeof(addr.sin_addr)) != 0) { - LOGP(DMGCP, LOGL_ERROR, - "Endpoint 0x%x data from wrong address %s vs. ", - ENDPOINT_NUMBER(endp), inet_ntoa(addr.sin_addr)); - LOGPC(DMGCP, LOGL_ERROR, - "%s\n", inet_ntoa(endp->net_end.addr)); - return -1; - } - - switch(endp->type) { - case MGCP_RTP_DEFAULT: - case MGCP_RTP_TRANSCODED: - if (endp->net_end.rtp_port != addr.sin_port && - endp->net_end.rtcp_port != addr.sin_port) { - LOGP(DMGCP, LOGL_ERROR, - "Data from wrong source port %d on 0x%x\n", - ntohs(addr.sin_port), ENDPOINT_NUMBER(endp)); - return -1; - } - break; - case MGCP_OSMUX_BSC: - case MGCP_OSMUX_BSC_NAT: - break; - } - - /* throw away the dummy message */ - if (rc == 1 && buf[0] == MGCP_DUMMY_LOAD) { - LOGP(DMGCP, LOGL_NOTICE, "Filtered dummy from network on 0x%x\n", - ENDPOINT_NUMBER(endp)); - return 0; - } - - proto = fd == &endp->net_end.rtp ? MGCP_PROTO_RTP : MGCP_PROTO_RTCP; - endp->net_end.packets += 1; - endp->net_end.octets += rc; - - forward_data(fd->fd, &endp->taps[MGCP_TAP_NET_IN], buf, rc); - - switch (endp->type) { - case MGCP_RTP_DEFAULT: - return mgcp_send(endp, MGCP_DEST_BTS, proto == MGCP_PROTO_RTP, - &addr, buf, rc); - case MGCP_RTP_TRANSCODED: - return mgcp_send_transcoder(&endp->trans_net, endp->cfg, - proto == MGCP_PROTO_RTP, buf, rc); - case MGCP_OSMUX_BSC_NAT: - return osmux_xfrm_to_osmux(MGCP_DEST_BTS, buf, rc, endp); - case MGCP_OSMUX_BSC: /* Should not happen */ - break; - } - - LOGP(DMGCP, LOGL_ERROR, "Bad MGCP type %u on endpoint %u\n", - endp->type, ENDPOINT_NUMBER(endp)); - return 0; -} - -static void discover_bts(struct mgcp_endpoint *endp, int proto, struct sockaddr_in *addr) -{ - struct mgcp_config *cfg = endp->cfg; - - if (proto == MGCP_PROTO_RTP && endp->bts_end.rtp_port == 0) { - if (!cfg->bts_ip || - memcmp(&addr->sin_addr, - &cfg->bts_in, sizeof(cfg->bts_in)) == 0 || - memcmp(&addr->sin_addr, - &endp->bts_end.addr, sizeof(endp->bts_end.addr)) == 0) { - - endp->bts_end.rtp_port = addr->sin_port; - endp->bts_end.addr = addr->sin_addr; - - LOGP(DMGCP, LOGL_NOTICE, - "Found BTS for endpoint: 0x%x on port: %d/%d of %s\n", - ENDPOINT_NUMBER(endp), ntohs(endp->bts_end.rtp_port), - ntohs(endp->bts_end.rtcp_port), inet_ntoa(addr->sin_addr)); - } - } else if (proto == MGCP_PROTO_RTCP && endp->bts_end.rtcp_port == 0) { - if (memcmp(&endp->bts_end.addr, &addr->sin_addr, - sizeof(endp->bts_end.addr)) == 0) { - endp->bts_end.rtcp_port = addr->sin_port; - } - } -} - -static int rtp_data_bts(struct osmo_fd *fd, unsigned int what) -{ - char buf[RTP_BUF_SIZE]; - struct sockaddr_in addr; - struct mgcp_endpoint *endp; - int rc, proto; - - endp = (struct mgcp_endpoint *) fd->data; - - rc = receive_from(endp, fd->fd, &addr, buf, sizeof(buf)); - if (rc <= 0) - return -1; - - proto = fd == &endp->bts_end.rtp ? MGCP_PROTO_RTP : MGCP_PROTO_RTCP; - - /* We have no idea who called us, maybe it is the BTS. */ - /* it was the BTS... */ - discover_bts(endp, proto, &addr); - - if (memcmp(&endp->bts_end.addr, &addr.sin_addr, sizeof(addr.sin_addr)) != 0) { - LOGP(DMGCP, LOGL_ERROR, - "Data from wrong bts %s on 0x%x\n", - inet_ntoa(addr.sin_addr), ENDPOINT_NUMBER(endp)); - return -1; - } - - if (endp->bts_end.rtp_port != addr.sin_port && - endp->bts_end.rtcp_port != addr.sin_port) { - LOGP(DMGCP, LOGL_ERROR, - "Data from wrong bts source port %d on 0x%x\n", - ntohs(addr.sin_port), ENDPOINT_NUMBER(endp)); - return -1; - } - - /* throw away the dummy message */ - if (rc == 1 && buf[0] == MGCP_DUMMY_LOAD) { - LOGP(DMGCP, LOGL_NOTICE, "Filtered dummy from bts on 0x%x\n", - ENDPOINT_NUMBER(endp)); - return 0; - } - - /* do this before the loop handling */ - endp->bts_end.packets += 1; - endp->bts_end.octets += rc; - - forward_data(fd->fd, &endp->taps[MGCP_TAP_BTS_IN], buf, rc); - - switch (endp->type) { - case MGCP_RTP_DEFAULT: - return mgcp_send(endp, MGCP_DEST_NET, proto == MGCP_PROTO_RTP, - &addr, buf, rc); - case MGCP_RTP_TRANSCODED: - return mgcp_send_transcoder(&endp->trans_bts, endp->cfg, - proto == MGCP_PROTO_RTP, buf, rc); - case MGCP_OSMUX_BSC: - /* OSMUX translation: BTS -> BSC */ - return osmux_xfrm_to_osmux(MGCP_DEST_NET, buf, rc, endp); - case MGCP_OSMUX_BSC_NAT: - break; /* Should not happen */ - } - - LOGP(DMGCP, LOGL_ERROR, "Bad MGCP type %u on endpoint %u\n", - endp->type, ENDPOINT_NUMBER(endp)); - return 0; -} - -static int rtp_data_transcoder(struct mgcp_rtp_end *end, struct mgcp_endpoint *_endp, - int dest, struct osmo_fd *fd) -{ - char buf[RTP_BUF_SIZE]; - struct sockaddr_in addr; - struct mgcp_config *cfg; - int rc, proto; - - cfg = _endp->cfg; - rc = receive_from(_endp, fd->fd, &addr, buf, sizeof(buf)); - if (rc <= 0) - return -1; - - proto = fd == &end->rtp ? MGCP_PROTO_RTP : MGCP_PROTO_RTCP; - - if (memcmp(&addr.sin_addr, &cfg->transcoder_in, sizeof(addr.sin_addr)) != 0) { - LOGP(DMGCP, LOGL_ERROR, - "Data not coming from transcoder dest: %d %s on 0x%x\n", - dest, inet_ntoa(addr.sin_addr), ENDPOINT_NUMBER(_endp)); - return -1; - } - - if (end->rtp_port != addr.sin_port && - end->rtcp_port != addr.sin_port) { - LOGP(DMGCP, LOGL_ERROR, - "Data from wrong transcoder dest %d source port %d on 0x%x\n", - dest, ntohs(addr.sin_port), ENDPOINT_NUMBER(_endp)); - return -1; - } - - /* throw away the dummy message */ - if (rc == 1 && buf[0] == MGCP_DUMMY_LOAD) { - LOGP(DMGCP, LOGL_NOTICE, "Filtered dummy from transcoder dest %d on 0x%x\n", - dest, ENDPOINT_NUMBER(_endp)); - return 0; - } - - end->packets += 1; - return mgcp_send(_endp, dest, proto == MGCP_PROTO_RTP, &addr, buf, rc); -} - -static int rtp_data_trans_net(struct osmo_fd *fd, unsigned int what) -{ - struct mgcp_endpoint *endp; - endp = (struct mgcp_endpoint *) fd->data; - - return rtp_data_transcoder(&endp->trans_net, endp, MGCP_DEST_NET, fd); -} - -static int rtp_data_trans_bts(struct osmo_fd *fd, unsigned int what) -{ - struct mgcp_endpoint *endp; - endp = (struct mgcp_endpoint *) fd->data; - - return rtp_data_transcoder(&endp->trans_bts, endp, MGCP_DEST_BTS, fd); -} - -int mgcp_create_bind(const char *source_addr, struct osmo_fd *fd, int port) -{ - struct sockaddr_in addr; - int on = 1; - - fd->fd = socket(AF_INET, SOCK_DGRAM, 0); - if (fd->fd < 0) { - LOGP(DMGCP, LOGL_ERROR, "Failed to create UDP port.\n"); - return -1; - } - - setsockopt(fd->fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)); - memset(&addr, 0, sizeof(addr)); - addr.sin_family = AF_INET; - addr.sin_port = htons(port); - inet_aton(source_addr, &addr.sin_addr); - - if (bind(fd->fd, (struct sockaddr *) &addr, sizeof(addr)) < 0) { - close(fd->fd); - fd->fd = -1; - return -1; - } - - return 0; -} - -int mgcp_set_ip_tos(int fd, int tos) -{ - int ret; - ret = setsockopt(fd, IPPROTO_IP, IP_TOS, - &tos, sizeof(tos)); - return ret != 0; -} - -static int bind_rtp(struct mgcp_config *cfg, const char *source_addr, - struct mgcp_rtp_end *rtp_end, int endpno) -{ - if (mgcp_create_bind(source_addr, &rtp_end->rtp, - rtp_end->local_port) != 0) { - LOGP(DMGCP, LOGL_ERROR, "Failed to create RTP port: %s:%d on 0x%x\n", - source_addr, rtp_end->local_port, endpno); - goto cleanup0; - } - - if (mgcp_create_bind(source_addr, &rtp_end->rtcp, - rtp_end->local_port + 1) != 0) { - LOGP(DMGCP, LOGL_ERROR, "Failed to create RTCP port: %s:%d on 0x%x\n", - source_addr, rtp_end->local_port + 1, endpno); - goto cleanup1; - } - - mgcp_set_ip_tos(rtp_end->rtp.fd, cfg->endp_dscp); - mgcp_set_ip_tos(rtp_end->rtcp.fd, cfg->endp_dscp); - - rtp_end->rtp.when = BSC_FD_READ; - if (osmo_fd_register(&rtp_end->rtp) != 0) { - LOGP(DMGCP, LOGL_ERROR, "Failed to register RTP port %d on 0x%x\n", - rtp_end->local_port, endpno); - goto cleanup2; - } - - rtp_end->rtcp.when = BSC_FD_READ; - if (osmo_fd_register(&rtp_end->rtcp) != 0) { - LOGP(DMGCP, LOGL_ERROR, "Failed to register RTCP port %d on 0x%x\n", - rtp_end->local_port + 1, endpno); - goto cleanup3; - } - - return 0; - -cleanup3: - osmo_fd_unregister(&rtp_end->rtp); -cleanup2: - close(rtp_end->rtcp.fd); - rtp_end->rtcp.fd = -1; -cleanup1: - close(rtp_end->rtp.fd); - rtp_end->rtp.fd = -1; -cleanup0: - return -1; -} - -static int int_bind(const char *port, - struct mgcp_rtp_end *end, int (*cb)(struct osmo_fd *, unsigned), - struct mgcp_endpoint *_endp, - const char *source_addr, int rtp_port) -{ - if (end->rtp.fd != -1 || end->rtcp.fd != -1) { - LOGP(DMGCP, LOGL_ERROR, "Previous %s was still bound on %d\n", - port, ENDPOINT_NUMBER(_endp)); - mgcp_free_rtp_port(end); - } - - end->local_port = rtp_port; - end->rtp.cb = cb; - end->rtp.data = _endp; - end->rtcp.data = _endp; - end->rtcp.cb = cb; - return bind_rtp(_endp->cfg, source_addr, end, ENDPOINT_NUMBER(_endp)); -} - -int mgcp_bind_bts_rtp_port(struct mgcp_endpoint *endp, int rtp_port) -{ - return int_bind("bts-port", &endp->bts_end, - rtp_data_bts, endp, - mgcp_bts_src_addr(endp), rtp_port); -} - -int mgcp_bind_net_rtp_port(struct mgcp_endpoint *endp, int rtp_port) -{ - return int_bind("net-port", &endp->net_end, - rtp_data_net, endp, - mgcp_net_src_addr(endp), rtp_port); -} - -int mgcp_bind_trans_net_rtp_port(struct mgcp_endpoint *endp, int rtp_port) -{ - return int_bind("trans-net", &endp->trans_net, - rtp_data_trans_net, endp, - endp->cfg->source_addr, rtp_port); -} - -int mgcp_bind_trans_bts_rtp_port(struct mgcp_endpoint *endp, int rtp_port) -{ - return int_bind("trans-bts", &endp->trans_bts, - rtp_data_trans_bts, endp, - endp->cfg->source_addr, rtp_port); -} - -int mgcp_free_rtp_port(struct mgcp_rtp_end *end) -{ - if (end->rtp.fd != -1) { - close(end->rtp.fd); - end->rtp.fd = -1; - osmo_fd_unregister(&end->rtp); - } - - if (end->rtcp.fd != -1) { - close(end->rtcp.fd); - end->rtcp.fd = -1; - osmo_fd_unregister(&end->rtcp); - } - - return 0; -} - - -void mgcp_state_calc_loss(struct mgcp_rtp_state *state, - struct mgcp_rtp_end *end, uint32_t *expected, - int *loss) -{ - *expected = state->stats_cycles + state->stats_max_seq; - *expected = *expected - state->stats_base_seq + 1; - - if (!state->stats_initialized) { - *expected = 0; - *loss = 0; - return; - } - - /* - * Make sure the sign is correct and use the biggest - * positive/negative number that fits. - */ - *loss = *expected - end->packets; - if (*expected < end->packets) { - if (*loss > 0) - *loss = INT_MIN; - } else { - if (*loss < 0) - *loss = INT_MAX; - } -} - -uint32_t mgcp_state_calc_jitter(struct mgcp_rtp_state *state) -{ - if (!state->stats_initialized) - return 0; - return state->stats_jitter >> 4; -} diff --git a/openbsc/src/libmgcp/mgcp_osmux.c b/openbsc/src/libmgcp/mgcp_osmux.c deleted file mode 100644 index b46a80e7..00000000 --- a/openbsc/src/libmgcp/mgcp_osmux.c +++ /dev/null @@ -1,586 +0,0 @@ -/* - * (C) 2012-2013 by Pablo Neira Ayuso - * (C) 2012-2013 by On Waves ehf - * All rights not specifically granted under this license are reserved. - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU Affero General Public License as published by the - * Free Software Foundation; either version 3 of the License, or (at your - * option) any later version. - */ - -#include /* for printf */ -#include /* for memcpy */ -#include /* for abs */ -#include /* for PRIu64 */ -#include -#include -#include - -#include -#include - -#include -#include -#include - -static struct osmo_fd osmux_fd; - -static LLIST_HEAD(osmux_handle_list); - -struct osmux_handle { - struct llist_head head; - struct osmux_in_handle *in; - struct in_addr rem_addr; - int rem_port; - int refcnt; -}; - -static void *osmux; - -static void osmux_deliver(struct msgb *batch_msg, void *data) -{ - struct osmux_handle *handle = data; - struct sockaddr_in out = { - .sin_family = AF_INET, - .sin_port = handle->rem_port, - }; - - memcpy(&out.sin_addr, &handle->rem_addr, sizeof(handle->rem_addr)); - sendto(osmux_fd.fd, batch_msg->data, batch_msg->len, 0, - (struct sockaddr *)&out, sizeof(out)); - msgb_free(batch_msg); -} - -static struct osmux_handle * -osmux_handle_find_get(struct in_addr *addr, int rem_port) -{ - struct osmux_handle *h; - - /* Lookup for existing OSMUX handle for this destination address. */ - llist_for_each_entry(h, &osmux_handle_list, head) { - if (memcmp(&h->rem_addr, addr, sizeof(struct in_addr)) == 0 && - h->rem_port == rem_port) { - LOGP(DMGCP, LOGL_DEBUG, "using existing OSMUX handle " - "for addr=%s:%d\n", - inet_ntoa(*addr), ntohs(rem_port)); - h->refcnt++; - return h; - } - } - - return NULL; -} - -static void osmux_handle_put(struct osmux_in_handle *in) -{ - struct osmux_handle *h; - - /* Lookup for existing OSMUX handle for this destination address. */ - llist_for_each_entry(h, &osmux_handle_list, head) { - if (h->in == in) { - if (--h->refcnt == 0) { - LOGP(DMGCP, LOGL_INFO, - "Releasing unused osmux handle for %s:%d\n", - inet_ntoa(h->rem_addr), - ntohs(h->rem_port)); - LOGP(DMGCP, LOGL_INFO, "Stats: " - "input RTP msgs: %u bytes: %"PRIu64" " - "output osmux msgs: %u bytes: %"PRIu64"\n", - in->stats.input_rtp_msgs, - in->stats.input_rtp_bytes, - in->stats.output_osmux_msgs, - in->stats.output_osmux_bytes); - llist_del(&h->head); - osmux_xfrm_input_fini(h->in); - talloc_free(h); - } - return; - } - } - LOGP(DMGCP, LOGL_ERROR, "cannot find Osmux input handle %p\n", in); -} - -static struct osmux_handle * -osmux_handle_alloc(struct mgcp_config *cfg, struct in_addr *addr, int rem_port) -{ - struct osmux_handle *h; - - h = talloc_zero(osmux, struct osmux_handle); - if (!h) - return NULL; - h->rem_addr = *addr; - h->rem_port = rem_port; - h->refcnt++; - - h->in = talloc_zero(h, struct osmux_in_handle); - if (!h->in) { - talloc_free(h); - return NULL; - } - - h->in->osmux_seq = 0; /* sequence number to start OSmux message from */ - h->in->batch_factor = cfg->osmux_batch; - /* If batch size is zero, the library defaults to 1470 bytes. */ - h->in->batch_size = cfg->osmux_batch_size; - h->in->deliver = osmux_deliver; - osmux_xfrm_input_init(h->in); - h->in->data = h; - - llist_add(&h->head, &osmux_handle_list); - - LOGP(DMGCP, LOGL_DEBUG, "created new OSMUX handle for addr=%s:%d\n", - inet_ntoa(*addr), ntohs(rem_port)); - - return h; -} - -static struct osmux_in_handle * -osmux_handle_lookup(struct mgcp_config *cfg, struct in_addr *addr, int rem_port) -{ - struct osmux_handle *h; - - h = osmux_handle_find_get(addr, rem_port); - if (h != NULL) - return h->in; - - h = osmux_handle_alloc(cfg, addr, rem_port); - if (h == NULL) - return NULL; - - return h->in; -} - -int osmux_xfrm_to_osmux(int type, char *buf, int rc, struct mgcp_endpoint *endp) -{ - int ret; - struct msgb *msg; - - msg = msgb_alloc(4096, "RTP"); - if (!msg) - return 0; - - memcpy(msg->data, buf, rc); - msgb_put(msg, rc); - - while ((ret = osmux_xfrm_input(endp->osmux.in, msg, endp->osmux.cid)) > 0) { - /* batch full, build and deliver it */ - osmux_xfrm_input_deliver(endp->osmux.in); - } - return 0; -} - -static struct mgcp_endpoint * -endpoint_lookup(struct mgcp_config *cfg, int cid, - struct in_addr *from_addr, int type) -{ - struct mgcp_endpoint *tmp = NULL; - int i; - - /* Lookup for the endpoint that corresponds to this port */ - for (i=0; itrunk.number_endpoints; i++) { - struct in_addr *this; - - tmp = &cfg->trunk.endpoints[i]; - - if (!tmp->allocated) - continue; - - switch(type) { - case MGCP_DEST_NET: - this = &tmp->net_end.addr; - break; - case MGCP_DEST_BTS: - this = &tmp->bts_end.addr; - break; - default: - /* Should not ever happen */ - LOGP(DMGCP, LOGL_ERROR, "Bad type %d. Fix your code.\n", type); - return NULL; - } - - if (tmp->osmux.cid == cid && this->s_addr == from_addr->s_addr) - return tmp; - } - - LOGP(DMGCP, LOGL_ERROR, "Cannot find endpoint with cid=%d\n", cid); - - return NULL; -} - -static void scheduled_tx_net_cb(struct msgb *msg, void *data) -{ - struct mgcp_endpoint *endp = data; - struct sockaddr_in addr = { - .sin_addr = endp->net_end.addr, - .sin_port = endp->net_end.rtp_port, - }; - - endp->bts_end.octets += msg->len; - endp->bts_end.packets++; - - mgcp_send(endp, MGCP_DEST_NET, 1, &addr, (char *)msg->data, msg->len); - msgb_free(msg); -} - -static void scheduled_tx_bts_cb(struct msgb *msg, void *data) -{ - struct mgcp_endpoint *endp = data; - struct sockaddr_in addr = { - .sin_addr = endp->bts_end.addr, - .sin_port = endp->bts_end.rtp_port, - }; - - endp->net_end.octets += msg->len; - endp->net_end.packets++; - - mgcp_send(endp, MGCP_DEST_BTS, 1, &addr, (char *)msg->data, msg->len); - msgb_free(msg); -} - -static struct msgb *osmux_recv(struct osmo_fd *ofd, struct sockaddr_in *addr) -{ - struct msgb *msg; - socklen_t slen = sizeof(*addr); - int ret; - - msg = msgb_alloc(4096, "OSMUX"); - if (!msg) { - LOGP(DMGCP, LOGL_ERROR, "cannot allocate message\n"); - return NULL; - } - ret = recvfrom(ofd->fd, msg->data, msg->data_len, 0, - (struct sockaddr *)addr, &slen); - if (ret <= 0) { - msgb_free(msg); - LOGP(DMGCP, LOGL_ERROR, "cannot receive message\n"); - return NULL; - } - msgb_put(msg, ret); - - return msg; -} - -#define osmux_chunk_length(msg, rem) (rem - msg->len); - -int osmux_read_from_bsc_nat_cb(struct osmo_fd *ofd, unsigned int what) -{ - struct msgb *msg; - struct osmux_hdr *osmuxh; - struct llist_head list; - struct sockaddr_in addr; - struct mgcp_config *cfg = ofd->data; - uint32_t rem; - - msg = osmux_recv(ofd, &addr); - if (!msg) - return -1; - - /* not any further processing dummy messages */ - if (msg->data[0] == MGCP_DUMMY_LOAD) - goto out; - - rem = msg->len; - while((osmuxh = osmux_xfrm_output_pull(msg)) != NULL) { - struct mgcp_endpoint *endp; - - /* Yes, we use MGCP_DEST_NET to locate the origin */ - endp = endpoint_lookup(cfg, osmuxh->circuit_id, - &addr.sin_addr, MGCP_DEST_NET); - if (!endp) { - LOGP(DMGCP, LOGL_ERROR, - "Cannot find an endpoint for circuit_id=%d\n", - osmuxh->circuit_id); - goto out; - } - endp->osmux.stats.octets += osmux_chunk_length(msg, rem); - endp->osmux.stats.chunks++; - rem = msg->len; - - osmux_xfrm_output(osmuxh, &endp->osmux.out, &list); - osmux_tx_sched(&list, scheduled_tx_bts_cb, endp); - } -out: - msgb_free(msg); - return 0; -} - -/* This is called from the bsc-nat */ -static int osmux_handle_dummy(struct mgcp_config *cfg, struct sockaddr_in *addr, - struct msgb *msg) -{ - struct mgcp_endpoint *endp; - uint8_t osmux_cid; - - if (msg->len < 1 + sizeof(osmux_cid)) { - LOGP(DMGCP, LOGL_ERROR, - "Discarding truncated Osmux dummy load\n"); - goto out; - } - - LOGP(DMGCP, LOGL_DEBUG, "Received Osmux dummy load from %s\n", - inet_ntoa(addr->sin_addr)); - - if (!cfg->osmux) { - LOGP(DMGCP, LOGL_ERROR, - "bsc wants to use Osmux but bsc-nat did not request it\n"); - goto out; - } - - /* extract the osmux CID from the dummy message */ - memcpy(&osmux_cid, &msg->data[1], sizeof(osmux_cid)); - - endp = endpoint_lookup(cfg, osmux_cid, &addr->sin_addr, MGCP_DEST_BTS); - if (!endp) { - LOGP(DMGCP, LOGL_ERROR, - "Cannot find endpoint for Osmux CID %d\n", osmux_cid); - goto out; - } - - if (endp->osmux.state == OSMUX_STATE_ENABLED) - goto out; - - if (osmux_enable_endpoint(endp, OSMUX_ROLE_BSC_NAT, - &addr->sin_addr, addr->sin_port) < 0 ){ - LOGP(DMGCP, LOGL_ERROR, - "Could not enable osmux in endpoint %d\n", - ENDPOINT_NUMBER(endp)); - goto out; - } - - LOGP(DMGCP, LOGL_INFO, "Enabling osmux in endpoint %d for %s:%u\n", - ENDPOINT_NUMBER(endp), inet_ntoa(addr->sin_addr), - ntohs(addr->sin_port)); -out: - msgb_free(msg); - return 0; -} - -int osmux_read_from_bsc_cb(struct osmo_fd *ofd, unsigned int what) -{ - struct msgb *msg; - struct osmux_hdr *osmuxh; - struct llist_head list; - struct sockaddr_in addr; - struct mgcp_config *cfg = ofd->data; - uint32_t rem; - - msg = osmux_recv(ofd, &addr); - if (!msg) - return -1; - - /* not any further processing dummy messages */ - if (msg->data[0] == MGCP_DUMMY_LOAD) - return osmux_handle_dummy(cfg, &addr, msg); - - rem = msg->len; - while((osmuxh = osmux_xfrm_output_pull(msg)) != NULL) { - struct mgcp_endpoint *endp; - - /* Yes, we use MGCP_DEST_BTS to locate the origin */ - endp = endpoint_lookup(cfg, osmuxh->circuit_id, - &addr.sin_addr, MGCP_DEST_BTS); - if (!endp) { - LOGP(DMGCP, LOGL_ERROR, - "Cannot find an endpoint for circuit_id=%d\n", - osmuxh->circuit_id); - goto out; - } - endp->osmux.stats.octets += osmux_chunk_length(msg, rem); - endp->osmux.stats.chunks++; - rem = msg->len; - - osmux_xfrm_output(osmuxh, &endp->osmux.out, &list); - osmux_tx_sched(&list, scheduled_tx_net_cb, endp); - } -out: - msgb_free(msg); - return 0; -} - -int osmux_init(int role, struct mgcp_config *cfg) -{ - int ret; - - switch(role) { - case OSMUX_ROLE_BSC: - osmux_fd.cb = osmux_read_from_bsc_nat_cb; - break; - case OSMUX_ROLE_BSC_NAT: - osmux_fd.cb = osmux_read_from_bsc_cb; - break; - default: - LOGP(DMGCP, LOGL_ERROR, "wrong role for OSMUX\n"); - return -1; - } - osmux_fd.data = cfg; - - ret = mgcp_create_bind(cfg->osmux_addr, &osmux_fd, cfg->osmux_port); - if (ret < 0) { - LOGP(DMGCP, LOGL_ERROR, "cannot bind OSMUX socket\n"); - return ret; - } - mgcp_set_ip_tos(osmux_fd.fd, cfg->endp_dscp); - osmux_fd.when |= BSC_FD_READ; - - ret = osmo_fd_register(&osmux_fd); - if (ret < 0) { - LOGP(DMGCP, LOGL_ERROR, "cannot register OSMUX socket\n"); - return ret; - } - cfg->osmux_init = 1; - - return 0; -} - -int osmux_enable_endpoint(struct mgcp_endpoint *endp, int role, - struct in_addr *addr, uint16_t port) -{ - /* If osmux is enabled, initialize the output handler. This handler is - * used to reconstruct the RTP flow from osmux. The RTP SSRC is - * allocated based on the circuit ID (endp->osmux.cid), which is unique - * in the local scope to the BSC/BSC-NAT. We use it to divide the RTP - * SSRC space (2^32) by the 256 possible circuit IDs, then randomly - * select one value from that window. Thus, we have no chance to have - * overlapping RTP SSRC traveling to the BTSes behind the BSC, - * similarly, for flows traveling to the MSC. - */ - static const uint32_t rtp_ssrc_winlen = UINT32_MAX / 256; - - if (endp->osmux.state == OSMUX_STATE_DISABLED) { - LOGP(DMGCP, LOGL_ERROR, "Endpoint %u didn't request Osmux\n", - ENDPOINT_NUMBER(endp)); - return -1; - } - - osmux_xfrm_output_init(&endp->osmux.out, - (endp->osmux.cid * rtp_ssrc_winlen) + - (random() % rtp_ssrc_winlen)); - - endp->osmux.in = osmux_handle_lookup(endp->cfg, addr, port); - if (!endp->osmux.in) { - LOGP(DMGCP, LOGL_ERROR, "Cannot allocate input osmux handle\n"); - return -1; - } - if (!osmux_xfrm_input_open_circuit(endp->osmux.in, endp->osmux.cid, - endp->cfg->osmux_dummy)) { - LOGP(DMGCP, LOGL_ERROR, "Cannot open osmux circuit %u\n", - endp->osmux.cid); - return -1; - } - - switch (endp->cfg->role) { - case MGCP_BSC_NAT: - endp->type = MGCP_OSMUX_BSC_NAT; - break; - case MGCP_BSC: - endp->type = MGCP_OSMUX_BSC; - break; - } - endp->osmux.state = OSMUX_STATE_ENABLED; - - return 0; -} - -void osmux_disable_endpoint(struct mgcp_endpoint *endp) -{ - LOGP(DMGCP, LOGL_INFO, "Releasing endpoint %u using Osmux CID %u\n", - ENDPOINT_NUMBER(endp), endp->osmux.cid); - osmux_xfrm_input_close_circuit(endp->osmux.in, endp->osmux.cid); - endp->osmux.state = OSMUX_STATE_DISABLED; - endp->osmux.cid = -1; - osmux_handle_put(endp->osmux.in); -} - -void osmux_release_cid(struct mgcp_endpoint *endp) -{ - if (endp->osmux.allocated_cid >= 0) - osmux_put_cid(endp->osmux.allocated_cid); - endp->osmux.allocated_cid = -1; -} - -void osmux_allocate_cid(struct mgcp_endpoint *endp) -{ - osmux_release_cid(endp); - endp->osmux.allocated_cid = osmux_get_cid(); -} - -/* We don't need to send the dummy load for osmux so often as another endpoint - * may have already punched the hole in the firewall. This approach is simple - * though. - */ -int osmux_send_dummy(struct mgcp_endpoint *endp) -{ - char buf[1 + sizeof(uint8_t)]; - struct in_addr addr_unset = {}; - - buf[0] = MGCP_DUMMY_LOAD; - memcpy(&buf[1], &endp->osmux.cid, sizeof(endp->osmux.cid)); - - /* Wait until we have the connection information from MDCX */ - if (memcmp(&endp->net_end.addr, &addr_unset, sizeof(addr_unset)) == 0) - return 0; - - if (endp->osmux.state == OSMUX_STATE_ACTIVATING) { - if (osmux_enable_endpoint(endp, OSMUX_ROLE_BSC, - &endp->net_end.addr, - htons(endp->cfg->osmux_port)) < 0) { - LOGP(DMGCP, LOGL_ERROR, - "Could not activate osmux in endpoint %d\n", - ENDPOINT_NUMBER(endp)); - } - LOGP(DMGCP, LOGL_ERROR, - "Osmux CID %u for %s:%u is now enabled\n", - endp->osmux.cid, inet_ntoa(endp->net_end.addr), - endp->cfg->osmux_port); - } - LOGP(DMGCP, LOGL_DEBUG, - "sending OSMUX dummy load to %s CID %u\n", - inet_ntoa(endp->net_end.addr), endp->osmux.cid); - - return mgcp_udp_send(osmux_fd.fd, &endp->net_end.addr, - htons(endp->cfg->osmux_port), buf, sizeof(buf)); -} - -/* bsc-nat allocates/releases the Osmux circuit ID */ -static uint8_t osmux_cid_bitmap[(OSMUX_CID_MAX + 1) / 8]; - -int osmux_used_cid(void) -{ - int i, j, used = 0; - - for (i = 0; i < sizeof(osmux_cid_bitmap); i++) { - for (j = 0; j < 8; j++) { - if (osmux_cid_bitmap[i] & (1 << j)) - used += 1; - } - } - - return used; -} - -int osmux_get_cid(void) -{ - int i, j; - - for (i = 0; i < sizeof(osmux_cid_bitmap); i++) { - for (j = 0; j < 8; j++) { - if (osmux_cid_bitmap[i] & (1 << j)) - continue; - - osmux_cid_bitmap[i] |= (1 << j); - LOGP(DMGCP, LOGL_DEBUG, - "Allocating Osmux CID %u from pool\n", (i * 8) + j); - return (i * 8) + j; - } - } - - LOGP(DMGCP, LOGL_ERROR, "All Osmux circuits are in use!\n"); - return -1; -} - -void osmux_put_cid(uint8_t osmux_cid) -{ - LOGP(DMGCP, LOGL_DEBUG, "Osmux CID %u is back to the pool\n", osmux_cid); - osmux_cid_bitmap[osmux_cid / 8] &= ~(1 << (osmux_cid % 8)); -} diff --git a/openbsc/src/libmgcp/mgcp_protocol.c b/openbsc/src/libmgcp/mgcp_protocol.c deleted file mode 100644 index 4fcadd94..00000000 --- a/openbsc/src/libmgcp/mgcp_protocol.c +++ /dev/null @@ -1,1598 +0,0 @@ -/* A Media Gateway Control Protocol Media Gateway: RFC 3435 */ -/* The protocol implementation */ - -/* - * (C) 2009-2012 by Holger Hans Peter Freyther - * (C) 2009-2012 by On-Waves - * All Rights Reserved - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation; either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - * - */ - -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include - -#include -#include - -#define for_each_non_empty_line(line, save) \ - for (line = strtok_r(NULL, "\r\n", &save); line;\ - line = strtok_r(NULL, "\r\n", &save)) - - -static void mgcp_rtp_end_reset(struct mgcp_rtp_end *end); - -struct mgcp_request { - char *name; - struct msgb *(*handle_request) (struct mgcp_parse_data *data); - char *debug_name; -}; - -#define MGCP_REQUEST(NAME, REQ, DEBUG_NAME) \ - { .name = NAME, .handle_request = REQ, .debug_name = DEBUG_NAME }, - -static struct msgb *handle_audit_endpoint(struct mgcp_parse_data *data); -static struct msgb *handle_create_con(struct mgcp_parse_data *data); -static struct msgb *handle_delete_con(struct mgcp_parse_data *data); -static struct msgb *handle_modify_con(struct mgcp_parse_data *data); -static struct msgb *handle_rsip(struct mgcp_parse_data *data); -static struct msgb *handle_noti_req(struct mgcp_parse_data *data); - -static void create_transcoder(struct mgcp_endpoint *endp); -static void delete_transcoder(struct mgcp_endpoint *endp); - -static int setup_rtp_processing(struct mgcp_endpoint *endp); - -static int mgcp_analyze_header(struct mgcp_parse_data *parse, char *data); - -static int mgcp_check_param(const struct mgcp_endpoint *endp, const char *line) -{ - const size_t line_len = strlen(line); - if (line[0] != '\0' && line_len < 2) { - LOGP(DMGCP, LOGL_ERROR, - "Wrong MGCP option format: '%s' on 0x%x\n", - line, ENDPOINT_NUMBER(endp)); - return 0; - } - - return 1; -} - -static uint32_t generate_call_id(struct mgcp_config *cfg) -{ - int i; - - /* use the call id */ - ++cfg->last_call_id; - - /* handle wrap around */ - if (cfg->last_call_id == CI_UNUSED) - ++cfg->last_call_id; - - /* callstack can only be of size number_of_endpoints */ - /* verify that the call id is free, e.g. in case of overrun */ - for (i = 1; i < cfg->trunk.number_endpoints; ++i) - if (cfg->trunk.endpoints[i].ci == cfg->last_call_id) - return generate_call_id(cfg); - - return cfg->last_call_id; -} - -/* - * array of function pointers for handling various - * messages. In the future this might be binary sorted - * for performance reasons. - */ -static const struct mgcp_request mgcp_requests [] = { - MGCP_REQUEST("AUEP", handle_audit_endpoint, "AuditEndpoint") - MGCP_REQUEST("CRCX", handle_create_con, "CreateConnection") - MGCP_REQUEST("DLCX", handle_delete_con, "DeleteConnection") - MGCP_REQUEST("MDCX", handle_modify_con, "ModifiyConnection") - MGCP_REQUEST("RQNT", handle_noti_req, "NotificationRequest") - - /* SPEC extension */ - MGCP_REQUEST("RSIP", handle_rsip, "ReSetInProgress") -}; - -static struct msgb *mgcp_msgb_alloc(void) -{ - struct msgb *msg; - msg = msgb_alloc_headroom(4096, 128, "MGCP msg"); - if (!msg) - LOGP(DMGCP, LOGL_ERROR, "Failed to msgb for MGCP data.\n"); - - return msg; -} - -static struct msgb *do_retransmission(const struct mgcp_endpoint *endp) -{ - struct msgb *msg = mgcp_msgb_alloc(); - if (!msg) - return NULL; - - msg->l2h = msgb_put(msg, strlen(endp->last_response)); - memcpy(msg->l2h, endp->last_response, msgb_l2len(msg)); - return msg; -} - -static struct msgb *create_resp(struct mgcp_endpoint *endp, int code, - const char *txt, const char *msg, - const char *trans, const char *param, - const char *sdp) -{ - int len; - struct msgb *res; - - res = mgcp_msgb_alloc(); - if (!res) - return NULL; - - len = snprintf((char *) res->data, 2048, "%d %s%s%s\r\n%s", - code, trans, txt, param ? param : "", sdp ? sdp : ""); - if (len < 0) { - LOGP(DMGCP, LOGL_ERROR, "Failed to sprintf MGCP response.\n"); - msgb_free(res); - return NULL; - } - - res->l2h = msgb_put(res, len); - LOGP(DMGCP, LOGL_DEBUG, "Generated response: code: %d for '%s'\n", code, res->l2h); - - /* - * Remember the last transmission per endpoint. - */ - if (endp) { - struct mgcp_trunk_config *tcfg = endp->tcfg; - talloc_free(endp->last_response); - talloc_free(endp->last_trans); - endp->last_trans = talloc_strdup(tcfg->endpoints, trans); - endp->last_response = talloc_strndup(tcfg->endpoints, - (const char *) res->l2h, - msgb_l2len(res)); - } - - return res; -} - -static struct msgb *create_ok_resp_with_param(struct mgcp_endpoint *endp, - int code, const char *msg, - const char *trans, const char *param) -{ - return create_resp(endp, code, " OK", msg, trans, param, NULL); -} - -static struct msgb *create_ok_response(struct mgcp_endpoint *endp, - int code, const char *msg, const char *trans) -{ - return create_ok_resp_with_param(endp, code, msg, trans, NULL); -} - -static struct msgb *create_err_response(struct mgcp_endpoint *endp, - int code, const char *msg, const char *trans) -{ - return create_resp(endp, code, " FAIL", msg, trans, NULL, NULL); -} - -static int write_response_sdp(struct mgcp_endpoint *endp, - char *sdp_record, size_t size, const char *addr) -{ - const char *fmtp_extra; - const char *audio_name; - int payload_type; - int len; - int nchars; - - endp->cfg->get_net_downlink_format_cb(endp, &payload_type, - &audio_name, &fmtp_extra); - - len = snprintf(sdp_record, size, - "v=0\r\n" - "o=- %u 23 IN IP4 %s\r\n" - "s=-\r\n" - "c=IN IP4 %s\r\n" - "t=0 0\r\n", - endp->ci, addr, addr); - - if (len < 0 || len >= size) - goto buffer_too_small; - - if (payload_type >= 0) { - nchars = snprintf(sdp_record + len, size - len, - "m=audio %d RTP/AVP %d\r\n", - endp->net_end.local_port, payload_type); - if (nchars < 0 || nchars >= size - len) - goto buffer_too_small; - - len += nchars; - - if (audio_name && endp->tcfg->audio_send_name) { - nchars = snprintf(sdp_record + len, size - len, - "a=rtpmap:%d %s\r\n", - payload_type, audio_name); - - if (nchars < 0 || nchars >= size - len) - goto buffer_too_small; - - len += nchars; - } - - if (fmtp_extra) { - nchars = snprintf(sdp_record + len, size - len, - "%s\r\n", fmtp_extra); - - if (nchars < 0 || nchars >= size - len) - goto buffer_too_small; - - len += nchars; - } - } - if (endp->bts_end.packet_duration_ms > 0 && endp->tcfg->audio_send_ptime) { - nchars = snprintf(sdp_record + len, size - len, - "a=ptime:%d\r\n", - endp->bts_end.packet_duration_ms); - if (nchars < 0 || nchars >= size - len) - goto buffer_too_small; - - len += nchars; - } - - return len; - -buffer_too_small: - LOGP(DMGCP, LOGL_ERROR, "SDP buffer too small: %zu (needed %d)\n", - size, len); - return -1; -} - -static struct msgb *create_response_with_sdp(struct mgcp_endpoint *endp, - const char *msg, const char *trans_id) -{ - const char *addr = endp->cfg->local_ip; - char sdp_record[4096]; - int len; - int nchars; - char osmux_extension[strlen("\nX-Osmux: 255") + 1]; - - if (!addr) - addr = mgcp_net_src_addr(endp); - - if (endp->osmux.state == OSMUX_STATE_NEGOTIATING) - sprintf(osmux_extension, "\nX-Osmux: %u", endp->osmux.cid); - else - osmux_extension[0] = '\0'; - - len = snprintf(sdp_record, sizeof(sdp_record), - "I: %u%s\n\n", endp->ci, osmux_extension); - if (len < 0) - return NULL; - - nchars = write_response_sdp(endp, sdp_record + len, - sizeof(sdp_record) - len - 1, addr); - if (nchars < 0) - return NULL; - - len += nchars; - - sdp_record[sizeof(sdp_record) - 1] = '\0'; - - return create_resp(endp, 200, " OK", msg, trans_id, NULL, sdp_record); -} - -static void send_dummy(struct mgcp_endpoint *endp) -{ - if (endp->osmux.state != OSMUX_STATE_DISABLED) - osmux_send_dummy(endp); - else - mgcp_send_dummy(endp); -} - -/* - * handle incoming messages: - * - this can be a command (four letters, space, transaction id) - * - or a response (three numbers, space, transaction id) - */ -struct msgb *mgcp_handle_message(struct mgcp_config *cfg, struct msgb *msg) -{ - struct mgcp_parse_data pdata; - int i, code, handled = 0; - struct msgb *resp = NULL; - char *data; - unsigned char *tail = msg->l2h + msgb_l2len(msg); /* char after l2 data */ - - if (msgb_l2len(msg) < 4) { - LOGP(DMGCP, LOGL_ERROR, "msg too short: %d\n", msg->len); - return NULL; - } - - /* Ensure that the msg->l2h is NUL terminated. */ - if (tail[-1] == '\0') - /* nothing to do */; - else if (msgb_tailroom(msg) > 0) - tail[0] = '\0'; - else if (tail[-1] == '\r' || tail[-1] == '\n') - tail[-1] = '\0'; - else { - LOGP(DMGCP, LOGL_ERROR, "Cannot NUL terminate MGCP message: " - "Length: %d, Buffer size: %d\n", - msgb_l2len(msg), msg->data_len); - return NULL; - } - - /* attempt to treat it as a response */ - if (sscanf((const char *)&msg->l2h[0], "%3d %*s", &code) == 1) { - LOGP(DMGCP, LOGL_DEBUG, "Response: Code: %d\n", code); - return NULL; - } - - msg->l3h = &msg->l2h[4]; - - - /* - * Check for a duplicate message and respond. - */ - memset(&pdata, 0, sizeof(pdata)); - pdata.cfg = cfg; - data = strline_r((char *) msg->l3h, &pdata.save); - pdata.found = mgcp_analyze_header(&pdata, data); - if (pdata.endp && pdata.trans - && pdata.endp->last_trans - && strcmp(pdata.endp->last_trans, pdata.trans) == 0) { - return do_retransmission(pdata.endp); - } - - for (i = 0; i < ARRAY_SIZE(mgcp_requests); ++i) { - if (strncmp(mgcp_requests[i].name, (const char *) &msg->l2h[0], 4) == 0) { - handled = 1; - resp = mgcp_requests[i].handle_request(&pdata); - break; - } - } - - if (!handled) - LOGP(DMGCP, LOGL_NOTICE, "MSG with type: '%.4s' not handled\n", &msg->l2h[0]); - - return resp; -} - -/** - * We have a null terminated string with the endpoint name here. We only - * support two kinds. Simple ones as seen on the BSC level and the ones - * seen on the trunk side. - */ -static struct mgcp_endpoint *find_e1_endpoint(struct mgcp_config *cfg, - const char *mgcp) -{ - char *rest = NULL; - struct mgcp_trunk_config *tcfg; - int trunk, endp; - - trunk = strtoul(mgcp + 6, &rest, 10); - if (rest == NULL || rest[0] != '/' || trunk < 1) { - LOGP(DMGCP, LOGL_ERROR, "Wrong trunk name '%s'\n", mgcp); - return NULL; - } - - endp = strtoul(rest + 1, &rest, 10); - if (rest == NULL || rest[0] != '@') { - LOGP(DMGCP, LOGL_ERROR, "Wrong endpoint name '%s'\n", mgcp); - return NULL; - } - - /* signalling is on timeslot 1 */ - if (endp == 1) - return NULL; - - tcfg = mgcp_trunk_num(cfg, trunk); - if (!tcfg) { - LOGP(DMGCP, LOGL_ERROR, "The trunk %d is not declared.\n", trunk); - return NULL; - } - - if (!tcfg->endpoints) { - LOGP(DMGCP, LOGL_ERROR, "Endpoints of trunk %d not allocated.\n", trunk); - return NULL; - } - - if (endp < 1 || endp >= tcfg->number_endpoints) { - LOGP(DMGCP, LOGL_ERROR, "Failed to find endpoint '%s'\n", mgcp); - return NULL; - } - - return &tcfg->endpoints[endp]; -} - -static struct mgcp_endpoint *find_endpoint(struct mgcp_config *cfg, const char *mgcp) -{ - char *endptr = NULL; - unsigned int gw = INT_MAX; - - if (strncmp(mgcp, "ds/e1", 5) == 0) - return find_e1_endpoint(cfg, mgcp); - - gw = strtoul(mgcp, &endptr, 16); - if (gw > 0 && gw < cfg->trunk.number_endpoints && endptr[0] == '@') - return &cfg->trunk.endpoints[gw]; - - LOGP(DMGCP, LOGL_ERROR, "Not able to find the endpoint: '%s'\n", mgcp); - return NULL; -} - -/** - * @returns 0 when the status line was complete and transaction_id and - * endp out parameters are set. - */ -static int mgcp_analyze_header(struct mgcp_parse_data *pdata, char *data) -{ - int i = 0; - char *elem, *save = NULL; - - OSMO_ASSERT(data); - pdata->trans = "000000"; - - for (elem = strtok_r(data, " ", &save); elem; - elem = strtok_r(NULL, " ", &save)) { - switch (i) { - case 0: - pdata->trans = elem; - break; - case 1: - pdata->endp = find_endpoint(pdata->cfg, elem); - if (!pdata->endp) { - LOGP(DMGCP, LOGL_ERROR, - "Unable to find Endpoint `%s'\n", elem); - return -1; - } - break; - case 2: - if (strcmp("MGCP", elem)) { - LOGP(DMGCP, LOGL_ERROR, - "MGCP header parsing error\n"); - return -1; - } - break; - case 3: - if (strcmp("1.0", elem)) { - LOGP(DMGCP, LOGL_ERROR, "MGCP version `%s' " - "not supported\n", elem); - return -1; - } - break; - } - i++; - } - - if (i != 4) { - LOGP(DMGCP, LOGL_ERROR, "MGCP status line too short.\n"); - pdata->trans = "000000"; - pdata->endp = NULL; - return -1; - } - - return 0; -} - -static int verify_call_id(const struct mgcp_endpoint *endp, - const char *callid) -{ - if (strcmp(endp->callid, callid) != 0) { - LOGP(DMGCP, LOGL_ERROR, "CallIDs does not match on 0x%x. '%s' != '%s'\n", - ENDPOINT_NUMBER(endp), endp->callid, callid); - return -1; - } - - return 0; -} - -static int verify_ci(const struct mgcp_endpoint *endp, - const char *_ci) -{ - uint32_t ci = strtoul(_ci, NULL, 10); - - if (ci != endp->ci) { - LOGP(DMGCP, LOGL_ERROR, "ConnectionIdentifiers do not match on 0x%x. %u != %s\n", - ENDPOINT_NUMBER(endp), endp->ci, _ci); - return -1; - } - - return 0; -} - -static struct msgb *handle_audit_endpoint(struct mgcp_parse_data *p) -{ - if (p->found != 0) - return create_err_response(NULL, 500, "AUEP", p->trans); - else - return create_ok_response(p->endp, 200, "AUEP", p->trans); -} - -static int parse_conn_mode(const char *msg, struct mgcp_endpoint *endp) -{ - int ret = 0; - if (strcmp(msg, "recvonly") == 0) - endp->conn_mode = MGCP_CONN_RECV_ONLY; - else if (strcmp(msg, "sendrecv") == 0) - endp->conn_mode = MGCP_CONN_RECV_SEND; - else if (strcmp(msg, "sendonly") == 0) - endp->conn_mode = MGCP_CONN_SEND_ONLY; - else if (strcmp(msg, "loopback") == 0) - endp->conn_mode = MGCP_CONN_LOOPBACK; - else { - LOGP(DMGCP, LOGL_ERROR, "Unknown connection mode: '%s'\n", msg); - ret = -1; - } - - endp->net_end.output_enabled = - endp->conn_mode & MGCP_CONN_SEND_ONLY ? 1 : 0; - endp->bts_end.output_enabled = - endp->conn_mode & MGCP_CONN_RECV_ONLY ? 1 : 0; - - return ret; -} - -static int allocate_port(struct mgcp_endpoint *endp, struct mgcp_rtp_end *end, - struct mgcp_port_range *range, - int (*alloc)(struct mgcp_endpoint *endp, int port)) -{ - int i; - - if (range->mode == PORT_ALLOC_STATIC) { - end->local_alloc = PORT_ALLOC_STATIC; - return 0; - } - - /* attempt to find a port */ - for (i = 0; i < 200; ++i) { - int rc; - - if (range->last_port >= range->range_end) - range->last_port = range->range_start; - - rc = alloc(endp, range->last_port); - - range->last_port += 2; - if (rc == 0) { - end->local_alloc = PORT_ALLOC_DYNAMIC; - return 0; - } - - } - - LOGP(DMGCP, LOGL_ERROR, "Allocating a RTP/RTCP port failed 200 times 0x%x.\n", - ENDPOINT_NUMBER(endp)); - return -1; -} - -static int allocate_ports(struct mgcp_endpoint *endp) -{ - if (allocate_port(endp, &endp->net_end, &endp->cfg->net_ports, - mgcp_bind_net_rtp_port) != 0) - return -1; - - if (allocate_port(endp, &endp->bts_end, &endp->cfg->bts_ports, - mgcp_bind_bts_rtp_port) != 0) { - mgcp_rtp_end_reset(&endp->net_end); - return -1; - } - - if (endp->cfg->transcoder_ip && endp->tcfg->trunk_type == MGCP_TRUNK_VIRTUAL) { - if (allocate_port(endp, &endp->trans_net, - &endp->cfg->transcoder_ports, - mgcp_bind_trans_net_rtp_port) != 0) { - mgcp_rtp_end_reset(&endp->net_end); - mgcp_rtp_end_reset(&endp->bts_end); - return -1; - } - - if (allocate_port(endp, &endp->trans_bts, - &endp->cfg->transcoder_ports, - mgcp_bind_trans_bts_rtp_port) != 0) { - mgcp_rtp_end_reset(&endp->net_end); - mgcp_rtp_end_reset(&endp->bts_end); - mgcp_rtp_end_reset(&endp->trans_net); - return -1; - } - - /* remember that we have set up transcoding */ - endp->type = MGCP_RTP_TRANSCODED; - } - - return 0; -} - -/* Set the LCO from a string (see RFC 3435). - * The string is stored in the 'string' field. A NULL string is handled excatly - * like an empty string, the 'string' field is never NULL after this function - * has been called. */ -static void set_local_cx_options(void *ctx, struct mgcp_lco *lco, - const char *options) -{ - char *p_opt, *a_opt; - char codec[9]; - - talloc_free(lco->string); - talloc_free(lco->codec); - lco->codec = NULL; - lco->pkt_period_min = lco->pkt_period_max = 0; - lco->string = talloc_strdup(ctx, options ? options : ""); - - p_opt = strstr(lco->string, "p:"); - if (p_opt && sscanf(p_opt, "p:%d-%d", - &lco->pkt_period_min, &lco->pkt_period_max) == 1) - lco->pkt_period_max = lco->pkt_period_min; - - a_opt = strstr(lco->string, "a:"); - if (a_opt && sscanf(a_opt, "a:%8[^,]", codec) == 1) - lco->codec = talloc_strdup(ctx, codec); -} - -void mgcp_rtp_end_config(struct mgcp_endpoint *endp, int expect_ssrc_change, - struct mgcp_rtp_end *rtp) -{ - struct mgcp_trunk_config *tcfg = endp->tcfg; - - int patch_ssrc = expect_ssrc_change && tcfg->force_constant_ssrc; - - rtp->force_aligned_timing = tcfg->force_aligned_timing; - rtp->force_constant_ssrc = patch_ssrc ? 1 : 0; - - LOGP(DMGCP, LOGL_DEBUG, - "Configuring RTP endpoint: local port %d%s%s\n", - ntohs(rtp->rtp_port), - rtp->force_aligned_timing ? ", force constant timing" : "", - rtp->force_constant_ssrc ? ", force constant ssrc" : ""); -} - -uint32_t mgcp_rtp_packet_duration(struct mgcp_endpoint *endp, - struct mgcp_rtp_end *rtp) -{ - int f = 0; - - /* Get the number of frames per channel and packet */ - if (rtp->frames_per_packet) - f = rtp->frames_per_packet; - else if (rtp->packet_duration_ms && rtp->codec.frame_duration_num) { - int den = 1000 * rtp->codec.frame_duration_num; - f = (rtp->packet_duration_ms * rtp->codec.frame_duration_den + den/2) - / den; - } - - return rtp->codec.rate * f * rtp->codec.frame_duration_num / rtp->codec.frame_duration_den; -} - -static int mgcp_parse_osmux_cid(const char *line) -{ - int osmux_cid; - - if (sscanf(line + 2, "Osmux: %u", &osmux_cid) != 1) - return -1; - - if (osmux_cid > OSMUX_CID_MAX) { - LOGP(DMGCP, LOGL_ERROR, "Osmux ID too large: %u > %u\n", - osmux_cid, OSMUX_CID_MAX); - return -1; - } - LOGP(DMGCP, LOGL_DEBUG, "bsc-nat offered Osmux CID %u\n", osmux_cid); - - return osmux_cid; -} - -static int mgcp_osmux_setup(struct mgcp_endpoint *endp, const char *line) -{ - if (!endp->cfg->osmux_init) { - if (osmux_init(OSMUX_ROLE_BSC, endp->cfg) < 0) { - LOGP(DMGCP, LOGL_ERROR, "Cannot init OSMUX\n"); - return -1; - } - LOGP(DMGCP, LOGL_NOTICE, "OSMUX socket has been set up\n"); - } - - return mgcp_parse_osmux_cid(line); -} - -static struct msgb *handle_create_con(struct mgcp_parse_data *p) -{ - struct mgcp_trunk_config *tcfg; - struct mgcp_endpoint *endp = p->endp; - int error_code = 400; - - const char *local_options = NULL; - const char *callid = NULL; - const char *mode = NULL; - char *line; - int have_sdp = 0, osmux_cid = -1; - - if (p->found != 0) - return create_err_response(NULL, 510, "CRCX", p->trans); - - /* parse CallID C: and LocalParameters L: */ - for_each_line(line, p->save) { - if (!mgcp_check_param(endp, line)) - continue; - - switch (line[0]) { - case 'L': - local_options = (const char *) line + 3; - break; - case 'C': - callid = (const char *) line + 3; - break; - case 'M': - mode = (const char *) line + 3; - break; - case 'X': - /* Osmux is not enabled in this bsc, ignore it so the - * bsc-nat knows that we don't want to use Osmux. - */ - if (!p->endp->cfg->osmux) - break; - - if (strncmp("Osmux: ", line + 2, strlen("Osmux: ")) == 0) - osmux_cid = mgcp_osmux_setup(endp, line); - break; - case '\0': - have_sdp = 1; - goto mgcp_header_done; - default: - LOGP(DMGCP, LOGL_NOTICE, "Unhandled option: '%c'/%d on 0x%x\n", - *line, *line, ENDPOINT_NUMBER(endp)); - break; - } - } - -mgcp_header_done: - tcfg = p->endp->tcfg; - - /* Check required data */ - if (!callid || !mode) { - LOGP(DMGCP, LOGL_ERROR, "Missing callid and mode in CRCX on 0x%x\n", - ENDPOINT_NUMBER(endp)); - return create_err_response(endp, 400, "CRCX", p->trans); - } - - if (endp->allocated) { - if (tcfg->force_realloc) { - LOGP(DMGCP, LOGL_NOTICE, "Endpoint 0x%x already allocated. Forcing realloc.\n", - ENDPOINT_NUMBER(endp)); - mgcp_release_endp(endp); - if (p->cfg->realloc_cb) - p->cfg->realloc_cb(tcfg, ENDPOINT_NUMBER(endp)); - } else { - LOGP(DMGCP, LOGL_ERROR, "Endpoint is already used. 0x%x\n", - ENDPOINT_NUMBER(endp)); - return create_err_response(endp, 400, "CRCX", p->trans); - } - } - - /* copy some parameters */ - endp->callid = talloc_strdup(tcfg->endpoints, callid); - - set_local_cx_options(endp->tcfg->endpoints, &endp->local_options, - local_options); - - if (parse_conn_mode(mode, endp) != 0) { - error_code = 517; - goto error2; - } - - /* initialize */ - endp->net_end.rtp_port = endp->net_end.rtcp_port = endp->bts_end.rtp_port = endp->bts_end.rtcp_port = 0; - mgcp_rtp_end_config(endp, 0, &endp->net_end); - mgcp_rtp_end_config(endp, 0, &endp->bts_end); - - /* set to zero until we get the info */ - memset(&endp->net_end.addr, 0, sizeof(endp->net_end.addr)); - - /* bind to the port now */ - if (allocate_ports(endp) != 0) - goto error2; - - /* assign a local call identifier or fail */ - endp->ci = generate_call_id(p->cfg); - if (endp->ci == CI_UNUSED) - goto error2; - - /* Annotate Osmux circuit ID and set it to negotiating state until this - * is fully set up from the dummy load. - */ - endp->osmux.state = OSMUX_STATE_DISABLED; - if (osmux_cid >= 0) { - endp->osmux.cid = osmux_cid; - endp->osmux.state = OSMUX_STATE_NEGOTIATING; - } else if (endp->cfg->osmux == OSMUX_USAGE_ONLY) { - LOGP(DMGCP, LOGL_ERROR, - "Osmux only and no osmux offered on 0x%x\n", ENDPOINT_NUMBER(endp)); - goto error2; - } - - endp->allocated = 1; - - /* set up RTP media parameters */ - mgcp_set_audio_info(p->cfg, &endp->bts_end.codec, tcfg->audio_payload, tcfg->audio_name); - endp->bts_end.fmtp_extra = talloc_strdup(tcfg->endpoints, - tcfg->audio_fmtp_extra); - if (have_sdp) - mgcp_parse_sdp_data(endp, &endp->net_end, p); - else if (endp->local_options.codec) - mgcp_set_audio_info(p->cfg, &endp->net_end.codec, - PTYPE_UNDEFINED, endp->local_options.codec); - - if (p->cfg->bts_force_ptime) { - endp->bts_end.packet_duration_ms = p->cfg->bts_force_ptime; - endp->bts_end.force_output_ptime = 1; - } - - if (setup_rtp_processing(endp) != 0) - goto error2; - - /* policy CB */ - if (p->cfg->policy_cb) { - int rc; - rc = p->cfg->policy_cb(tcfg, ENDPOINT_NUMBER(endp), - MGCP_ENDP_CRCX, p->trans); - switch (rc) { - case MGCP_POLICY_REJECT: - LOGP(DMGCP, LOGL_NOTICE, "CRCX rejected by policy on 0x%x\n", - ENDPOINT_NUMBER(endp)); - mgcp_release_endp(endp); - return create_err_response(endp, 400, "CRCX", p->trans); - break; - case MGCP_POLICY_DEFER: - /* stop processing */ - create_transcoder(endp); - return NULL; - break; - case MGCP_POLICY_CONT: - /* just continue */ - break; - } - } - - LOGP(DMGCP, LOGL_DEBUG, "Creating endpoint on: 0x%x CI: %u port: %u/%u\n", - ENDPOINT_NUMBER(endp), endp->ci, - endp->net_end.local_port, endp->bts_end.local_port); - if (p->cfg->change_cb) - p->cfg->change_cb(tcfg, ENDPOINT_NUMBER(endp), MGCP_ENDP_CRCX); - - if (endp->conn_mode & MGCP_CONN_RECV_ONLY && tcfg->keepalive_interval != 0) { - send_dummy(endp); - } - - create_transcoder(endp); - return create_response_with_sdp(endp, "CRCX", p->trans); -error2: - mgcp_release_endp(endp); - LOGP(DMGCP, LOGL_NOTICE, "Resource error on 0x%x\n", ENDPOINT_NUMBER(endp)); - return create_err_response(endp, error_code, "CRCX", p->trans); -} - -static struct msgb *handle_modify_con(struct mgcp_parse_data *p) -{ - struct mgcp_endpoint *endp = p->endp; - int error_code = 500; - int silent = 0; - int have_sdp = 0; - char *line; - const char *local_options = NULL; - - if (p->found != 0) - return create_err_response(NULL, 510, "MDCX", p->trans); - - if (endp->ci == CI_UNUSED) { - LOGP(DMGCP, LOGL_ERROR, "Endpoint is not " - "holding a connection. 0x%x\n", ENDPOINT_NUMBER(endp)); - return create_err_response(endp, 400, "MDCX", p->trans); - } - - for_each_line(line, p->save) { - if (!mgcp_check_param(endp, line)) - continue; - - switch (line[0]) { - case 'C': { - if (verify_call_id(endp, line + 3) != 0) - goto error3; - break; - } - case 'I': { - if (verify_ci(endp, line + 3) != 0) - goto error3; - break; - } - case 'L': - local_options = (const char *) line + 3; - break; - case 'M': - if (parse_conn_mode(line + 3, endp) != 0) { - error_code = 517; - goto error3; - } - endp->orig_mode = endp->conn_mode; - break; - case 'Z': - silent = strcmp("noanswer", line + 3) == 0; - break; - case '\0': - /* SDP file begins */ - have_sdp = 1; - mgcp_parse_sdp_data(endp, &endp->net_end, p); - /* This will exhaust p->save, so the loop will - * terminate next time. - */ - break; - default: - LOGP(DMGCP, LOGL_NOTICE, "Unhandled MGCP option: '%c'/%d on 0x%x\n", - line[0], line[0], ENDPOINT_NUMBER(endp)); - break; - } - } - - set_local_cx_options(endp->tcfg->endpoints, &endp->local_options, - local_options); - - if (!have_sdp && endp->local_options.codec) - mgcp_set_audio_info(p->cfg, &endp->net_end.codec, - PTYPE_UNDEFINED, endp->local_options.codec); - - if (setup_rtp_processing(endp) != 0) - goto error3; - - /* policy CB */ - if (p->cfg->policy_cb) { - int rc; - rc = p->cfg->policy_cb(endp->tcfg, ENDPOINT_NUMBER(endp), - MGCP_ENDP_MDCX, p->trans); - switch (rc) { - case MGCP_POLICY_REJECT: - LOGP(DMGCP, LOGL_NOTICE, "MDCX rejected by policy on 0x%x\n", - ENDPOINT_NUMBER(endp)); - if (silent) - goto out_silent; - return create_err_response(endp, 400, "MDCX", p->trans); - break; - case MGCP_POLICY_DEFER: - /* stop processing */ - return NULL; - break; - case MGCP_POLICY_CONT: - /* just continue */ - break; - } - } - - mgcp_rtp_end_config(endp, 1, &endp->net_end); - mgcp_rtp_end_config(endp, 1, &endp->bts_end); - - /* modify */ - LOGP(DMGCP, LOGL_DEBUG, "Modified endpoint on: 0x%x Server: %s:%u\n", - ENDPOINT_NUMBER(endp), inet_ntoa(endp->net_end.addr), ntohs(endp->net_end.rtp_port)); - if (p->cfg->change_cb) - p->cfg->change_cb(endp->tcfg, ENDPOINT_NUMBER(endp), MGCP_ENDP_MDCX); - - if (endp->conn_mode & MGCP_CONN_RECV_ONLY && - endp->tcfg->keepalive_interval != 0) - send_dummy(endp); - - if (silent) - goto out_silent; - - return create_response_with_sdp(endp, "MDCX", p->trans); - -error3: - return create_err_response(endp, error_code, "MDCX", p->trans); - - -out_silent: - return NULL; -} - -static struct msgb *handle_delete_con(struct mgcp_parse_data *p) -{ - struct mgcp_endpoint *endp = p->endp; - int error_code = 400; - int silent = 0; - char *line; - char stats[1048]; - - if (p->found != 0) - return create_err_response(NULL, error_code, "DLCX", p->trans); - - if (!p->endp->allocated) { - LOGP(DMGCP, LOGL_ERROR, "Endpoint is not used. 0x%x\n", - ENDPOINT_NUMBER(endp)); - return create_err_response(endp, 400, "DLCX", p->trans); - } - - for_each_line(line, p->save) { - if (!mgcp_check_param(endp, line)) - continue; - - switch (line[0]) { - case 'C': - if (verify_call_id(endp, line + 3) != 0) - goto error3; - break; - case 'I': - if (verify_ci(endp, line + 3) != 0) - goto error3; - break; - case 'Z': - silent = strcmp("noanswer", line + 3) == 0; - break; - default: - LOGP(DMGCP, LOGL_NOTICE, "Unhandled option: '%c'/%d on 0x%x\n", - line[0], line[0], ENDPOINT_NUMBER(endp)); - break; - } - } - - /* policy CB */ - if (p->cfg->policy_cb) { - int rc; - rc = p->cfg->policy_cb(endp->tcfg, ENDPOINT_NUMBER(endp), - MGCP_ENDP_DLCX, p->trans); - switch (rc) { - case MGCP_POLICY_REJECT: - LOGP(DMGCP, LOGL_NOTICE, "DLCX rejected by policy on 0x%x\n", - ENDPOINT_NUMBER(endp)); - if (silent) - goto out_silent; - return create_err_response(endp, 400, "DLCX", p->trans); - break; - case MGCP_POLICY_DEFER: - /* stop processing */ - delete_transcoder(endp); - return NULL; - break; - case MGCP_POLICY_CONT: - /* just continue */ - break; - } - } - - /* free the connection */ - LOGP(DMGCP, LOGL_DEBUG, "Deleted endpoint on: 0x%x Server: %s:%u\n", - ENDPOINT_NUMBER(endp), inet_ntoa(endp->net_end.addr), ntohs(endp->net_end.rtp_port)); - - /* save the statistics of the current call */ - mgcp_format_stats(endp, stats, sizeof(stats)); - - delete_transcoder(endp); - mgcp_release_endp(endp); - if (p->cfg->change_cb) - p->cfg->change_cb(endp->tcfg, ENDPOINT_NUMBER(endp), MGCP_ENDP_DLCX); - - if (silent) - goto out_silent; - return create_ok_resp_with_param(endp, 250, "DLCX", p->trans, stats); - -error3: - return create_err_response(endp, error_code, "DLCX", p->trans); - -out_silent: - return NULL; -} - -static struct msgb *handle_rsip(struct mgcp_parse_data *p) -{ - if (p->found != 0) { - LOGP(DMGCP, LOGL_ERROR, "Failed to find the endpoint.\n"); - return NULL; - } - - if (p->cfg->reset_cb) - p->cfg->reset_cb(p->endp->tcfg); - return NULL; -} - -static char extract_tone(const char *line) -{ - const char *str = strstr(line, "D/"); - if (!str) - return CHAR_MAX; - - return str[2]; -} - -/* - * This can request like DTMF detection and forward, fax detection... it - * can also request when the notification should be send and such. We don't - * do this right now. - */ -static struct msgb *handle_noti_req(struct mgcp_parse_data *p) -{ - int res = 0; - char *line; - char tone = CHAR_MAX; - - if (p->found != 0) - return create_err_response(NULL, 400, "RQNT", p->trans); - - for_each_line(line, p->save) { - switch (line[0]) { - case 'S': - tone = extract_tone(line); - break; - } - } - - /* we didn't see a signal request with a tone */ - if (tone == CHAR_MAX) - return create_ok_response(p->endp, 200, "RQNT", p->trans); - - if (p->cfg->rqnt_cb) - res = p->cfg->rqnt_cb(p->endp, tone); - - return res == 0 ? - create_ok_response(p->endp, 200, "RQNT", p->trans) : - create_err_response(p->endp, res, "RQNT", p->trans); -} - -static void mgcp_keepalive_timer_cb(void *_tcfg) -{ - struct mgcp_trunk_config *tcfg = _tcfg; - int i; - LOGP(DMGCP, LOGL_DEBUG, "Triggered trunk %d keepalive timer.\n", - tcfg->trunk_nr); - - if (tcfg->keepalive_interval <= 0) - return; - - for (i = 1; i < tcfg->number_endpoints; ++i) { - struct mgcp_endpoint *endp = &tcfg->endpoints[i]; - if (endp->conn_mode == MGCP_CONN_RECV_ONLY) - send_dummy(endp); - } - - LOGP(DMGCP, LOGL_DEBUG, "Rescheduling trunk %d keepalive timer.\n", - tcfg->trunk_nr); - osmo_timer_schedule(&tcfg->keepalive_timer, tcfg->keepalive_interval, 0); -} - -void mgcp_trunk_set_keepalive(struct mgcp_trunk_config *tcfg, int interval) -{ - tcfg->keepalive_interval = interval; - osmo_timer_setup(&tcfg->keepalive_timer, mgcp_keepalive_timer_cb, tcfg); - - if (interval <= 0) - osmo_timer_del(&tcfg->keepalive_timer); - else - osmo_timer_schedule(&tcfg->keepalive_timer, - tcfg->keepalive_interval, 0); -} - -struct mgcp_config *mgcp_config_alloc(void) -{ - struct mgcp_config *cfg; - - cfg = talloc_zero(NULL, struct mgcp_config); - if (!cfg) { - LOGP(DMGCP, LOGL_FATAL, "Failed to allocate config.\n"); - return NULL; - } - - cfg->source_port = 2427; - cfg->source_addr = talloc_strdup(cfg, "0.0.0.0"); - cfg->osmux_addr = talloc_strdup(cfg, "0.0.0.0"); - - cfg->transcoder_remote_base = 4000; - - cfg->bts_ports.base_port = RTP_PORT_DEFAULT; - cfg->net_ports.base_port = RTP_PORT_NET_DEFAULT; - - cfg->rtp_processing_cb = &mgcp_rtp_processing_default; - cfg->setup_rtp_processing_cb = &mgcp_setup_rtp_processing_default; - - cfg->get_net_downlink_format_cb = &mgcp_get_net_downlink_format_default; - - /* default trunk handling */ - cfg->trunk.cfg = cfg; - cfg->trunk.trunk_nr = 0; - cfg->trunk.trunk_type = MGCP_TRUNK_VIRTUAL; - cfg->trunk.audio_name = talloc_strdup(cfg, "AMR/8000"); - cfg->trunk.audio_payload = 126; - cfg->trunk.audio_send_ptime = 1; - cfg->trunk.audio_send_name = 1; - cfg->trunk.omit_rtcp = 0; - mgcp_trunk_set_keepalive(&cfg->trunk, MGCP_KEEPALIVE_ONCE); - - INIT_LLIST_HEAD(&cfg->trunks); - - return cfg; -} - -struct mgcp_trunk_config *mgcp_trunk_alloc(struct mgcp_config *cfg, int nr) -{ - struct mgcp_trunk_config *trunk; - - trunk = talloc_zero(cfg, struct mgcp_trunk_config); - if (!trunk) { - LOGP(DMGCP, LOGL_ERROR, "Failed to allocate.\n"); - return NULL; - } - - trunk->cfg = cfg; - trunk->trunk_type = MGCP_TRUNK_E1; - trunk->trunk_nr = nr; - trunk->audio_name = talloc_strdup(cfg, "AMR/8000"); - trunk->audio_payload = 126; - trunk->audio_send_ptime = 1; - trunk->audio_send_name = 1; - trunk->number_endpoints = 33; - trunk->omit_rtcp = 0; - mgcp_trunk_set_keepalive(trunk, MGCP_KEEPALIVE_ONCE); - llist_add_tail(&trunk->entry, &cfg->trunks); - return trunk; -} - -struct mgcp_trunk_config *mgcp_trunk_num(struct mgcp_config *cfg, int index) -{ - struct mgcp_trunk_config *trunk; - - llist_for_each_entry(trunk, &cfg->trunks, entry) - if (trunk->trunk_nr == index) - return trunk; - - return NULL; -} - -static void mgcp_rtp_codec_reset(struct mgcp_rtp_codec *codec) -{ - codec->payload_type = -1; - talloc_free(codec->subtype_name); - codec->subtype_name = NULL; - talloc_free(codec->audio_name); - codec->audio_name = NULL; - codec->frame_duration_num = DEFAULT_RTP_AUDIO_FRAME_DUR_NUM; - codec->frame_duration_den = DEFAULT_RTP_AUDIO_FRAME_DUR_DEN; - codec->rate = DEFAULT_RTP_AUDIO_DEFAULT_RATE; - codec->channels = DEFAULT_RTP_AUDIO_DEFAULT_CHANNELS; -} - -static void mgcp_rtp_end_reset(struct mgcp_rtp_end *end) -{ - if (end->local_alloc == PORT_ALLOC_DYNAMIC) { - mgcp_free_rtp_port(end); - end->local_port = 0; - } - - end->packets = 0; - end->octets = 0; - end->dropped_packets = 0; - memset(&end->addr, 0, sizeof(end->addr)); - end->rtp_port = end->rtcp_port = 0; - end->local_alloc = -1; - talloc_free(end->fmtp_extra); - end->fmtp_extra = NULL; - talloc_free(end->rtp_process_data); - end->rtp_process_data = NULL; - - /* Set default values */ - end->frames_per_packet = 0; /* unknown */ - end->packet_duration_ms = DEFAULT_RTP_AUDIO_PACKET_DURATION_MS; - end->output_enabled = 0; - - mgcp_rtp_codec_reset(&end->codec); - mgcp_rtp_codec_reset(&end->alt_codec); -} - -static void mgcp_rtp_end_init(struct mgcp_rtp_end *end) -{ - mgcp_rtp_end_reset(end); - end->rtp.fd = -1; - end->rtcp.fd = -1; -} - -int mgcp_endpoints_allocate(struct mgcp_trunk_config *tcfg) -{ - int i; - - /* Initialize all endpoints */ - tcfg->endpoints = _talloc_zero_array(tcfg->cfg, - sizeof(struct mgcp_endpoint), - tcfg->number_endpoints, "endpoints"); - if (!tcfg->endpoints) - return -1; - - for (i = 0; i < tcfg->number_endpoints; ++i) { - tcfg->endpoints[i].osmux.allocated_cid = -1; - tcfg->endpoints[i].ci = CI_UNUSED; - tcfg->endpoints[i].cfg = tcfg->cfg; - tcfg->endpoints[i].tcfg = tcfg; - mgcp_rtp_end_init(&tcfg->endpoints[i].net_end); - mgcp_rtp_end_init(&tcfg->endpoints[i].bts_end); - mgcp_rtp_end_init(&tcfg->endpoints[i].trans_net); - mgcp_rtp_end_init(&tcfg->endpoints[i].trans_bts); - } - - return 0; -} - -void mgcp_release_endp(struct mgcp_endpoint *endp) -{ - LOGP(DMGCP, LOGL_DEBUG, "Releasing endpoint on: 0x%x\n", ENDPOINT_NUMBER(endp)); - endp->ci = CI_UNUSED; - endp->allocated = 0; - - talloc_free(endp->callid); - endp->callid = NULL; - - talloc_free(endp->local_options.string); - endp->local_options.string = NULL; - talloc_free(endp->local_options.codec); - endp->local_options.codec = NULL; - - mgcp_rtp_end_reset(&endp->bts_end); - mgcp_rtp_end_reset(&endp->net_end); - mgcp_rtp_end_reset(&endp->trans_net); - mgcp_rtp_end_reset(&endp->trans_bts); - endp->type = MGCP_RTP_DEFAULT; - - memset(&endp->net_state, 0, sizeof(endp->net_state)); - memset(&endp->bts_state, 0, sizeof(endp->bts_state)); - - endp->conn_mode = endp->orig_mode = MGCP_CONN_NONE; - - if (endp->osmux.state == OSMUX_STATE_ENABLED) - osmux_disable_endpoint(endp); - - /* release the circuit ID if it had been allocated */ - osmux_release_cid(endp); - - memset(&endp->taps, 0, sizeof(endp->taps)); -} - -void mgcp_initialize_endp(struct mgcp_endpoint *endp) -{ - return mgcp_release_endp(endp); -} - -static int send_trans(struct mgcp_config *cfg, const char *buf, int len) -{ - struct sockaddr_in addr; - - memset(&addr, 0, sizeof(addr)); - addr.sin_family = AF_INET; - addr.sin_addr = cfg->transcoder_in; - addr.sin_port = htons(2427); - return sendto(cfg->gw_fd.bfd.fd, buf, len, 0, - (struct sockaddr *) &addr, sizeof(addr)); -} - -static void send_msg(struct mgcp_endpoint *endp, int endpoint, int port, - const char *msg, const char *mode) -{ - char buf[2096]; - int len; - int nchars; - - /* hardcoded to AMR right now, we do not know the real type at this point */ - len = snprintf(buf, sizeof(buf), - "%s 42 %x@mgw MGCP 1.0\r\n" - "C: 4256\r\n" - "M: %s\r\n" - "\r\n", - msg, endpoint, mode); - - if (len < 0) - return; - - nchars = write_response_sdp(endp, buf + len, sizeof(buf) + len - 1, NULL); - if (nchars < 0) - return; - - len += nchars; - - buf[sizeof(buf) - 1] = '\0'; - - send_trans(endp->cfg, buf, len); -} - -static void send_dlcx(struct mgcp_endpoint *endp, int endpoint) -{ - char buf[2096]; - int len; - - len = snprintf(buf, sizeof(buf), - "DLCX 43 %x@mgw MGCP 1.0\r\n" - "C: 4256\r\n" - , endpoint); - - if (len < 0) - return; - - buf[sizeof(buf) - 1] = '\0'; - - send_trans(endp->cfg, buf, len); -} - -static int send_agent(struct mgcp_config *cfg, const char *buf, int len) -{ - return write(cfg->gw_fd.bfd.fd, buf, len); -} - -int mgcp_send_reset_all(struct mgcp_config *cfg) -{ - static const char mgcp_reset[] = { - "RSIP 1 *@mgw MGCP 1.0\r\n" - }; - - return send_agent(cfg, mgcp_reset, sizeof mgcp_reset -1); -} - -int mgcp_send_reset_ep(struct mgcp_endpoint *endp, int endpoint) -{ - char buf[128]; - int len; - - len = snprintf(buf, sizeof(buf), - "RSIP 39 %x@mgw MGCP 1.0\r\n" - , endpoint); - if (len < 0) - return len; - - buf[sizeof(buf) - 1] = '\0'; - - return send_agent(endp->cfg, buf, len); -} - -static int setup_rtp_processing(struct mgcp_endpoint *endp) -{ - int rc = 0; - struct mgcp_config *cfg = endp->cfg; - - if (endp->type != MGCP_RTP_DEFAULT) - return 0; - - if (endp->conn_mode == MGCP_CONN_LOOPBACK) - return 0; - - if (endp->conn_mode & MGCP_CONN_SEND_ONLY) - rc |= cfg->setup_rtp_processing_cb(endp, &endp->net_end, &endp->bts_end); - else - rc |= cfg->setup_rtp_processing_cb(endp, &endp->net_end, NULL); - - if (endp->conn_mode & MGCP_CONN_RECV_ONLY) - rc |= cfg->setup_rtp_processing_cb(endp, &endp->bts_end, &endp->net_end); - else - rc |= cfg->setup_rtp_processing_cb(endp, &endp->bts_end, NULL); - return rc; -} - -static void create_transcoder(struct mgcp_endpoint *endp) -{ - int port; - int in_endp = ENDPOINT_NUMBER(endp); - int out_endp = endp_back_channel(in_endp); - - if (endp->type != MGCP_RTP_TRANSCODED) - return; - - send_msg(endp, in_endp, endp->trans_bts.local_port, "CRCX", "sendrecv"); - send_msg(endp, in_endp, endp->trans_bts.local_port, "MDCX", "sendrecv"); - send_msg(endp, out_endp, endp->trans_net.local_port, "CRCX", "sendrecv"); - send_msg(endp, out_endp, endp->trans_net.local_port, "MDCX", "sendrecv"); - - port = rtp_calculate_port(in_endp, endp->cfg->transcoder_remote_base); - endp->trans_bts.rtp_port = htons(port); - endp->trans_bts.rtcp_port = htons(port + 1); - - port = rtp_calculate_port(out_endp, endp->cfg->transcoder_remote_base); - endp->trans_net.rtp_port = htons(port); - endp->trans_net.rtcp_port = htons(port + 1); -} - -static void delete_transcoder(struct mgcp_endpoint *endp) -{ - int in_endp = ENDPOINT_NUMBER(endp); - int out_endp = endp_back_channel(in_endp); - - if (endp->type != MGCP_RTP_TRANSCODED) - return; - - send_dlcx(endp, in_endp); - send_dlcx(endp, out_endp); -} - -int mgcp_reset_transcoder(struct mgcp_config *cfg) -{ - if (!cfg->transcoder_ip) - return 0; - - static const char mgcp_reset[] = { - "RSIP 1 13@mgw MGCP 1.0\r\n" - }; - - return send_trans(cfg, mgcp_reset, sizeof mgcp_reset -1); -} - -void mgcp_format_stats(struct mgcp_endpoint *endp, char *msg, size_t size) -{ - uint32_t expected, jitter; - int ploss; - int nchars; - mgcp_state_calc_loss(&endp->net_state, &endp->net_end, - &expected, &ploss); - jitter = mgcp_state_calc_jitter(&endp->net_state); - - nchars = snprintf(msg, size, - "\r\nP: PS=%u, OS=%u, PR=%u, OR=%u, PL=%d, JI=%u", - endp->bts_end.packets, endp->bts_end.octets, - endp->net_end.packets, endp->net_end.octets, - ploss, jitter); - if (nchars < 0 || nchars >= size) - goto truncate; - - msg += nchars; - size -= nchars; - - /* Error Counter */ - nchars = snprintf(msg, size, - "\r\nX-Osmo-CP: EC TIS=%u, TOS=%u, TIR=%u, TOR=%u", - endp->net_state.in_stream.err_ts_counter, - endp->net_state.out_stream.err_ts_counter, - endp->bts_state.in_stream.err_ts_counter, - endp->bts_state.out_stream.err_ts_counter); - if (nchars < 0 || nchars >= size) - goto truncate; - - msg += nchars; - size -= nchars; - - if (endp->osmux.state == OSMUX_STATE_ENABLED) { - snprintf(msg, size, - "\r\nX-Osmux-ST: CR=%u, BR=%u", - endp->osmux.stats.chunks, - endp->osmux.stats.octets); - } -truncate: - msg[size - 1] = '\0'; -} - -int mgcp_parse_stats(struct msgb *msg, uint32_t *ps, uint32_t *os, - uint32_t *pr, uint32_t *_or, int *loss, uint32_t *jitter) -{ - char *line, *save; - int rc; - - /* initialize with bad values */ - *ps = *os = *pr = *_or = *jitter = UINT_MAX; - *loss = INT_MAX; - - - line = strtok_r((char *) msg->l2h, "\r\n", &save); - if (!line) - return -1; - - /* this can only parse the message that is created above... */ - for_each_non_empty_line(line, save) { - switch (line[0]) { - case 'P': - rc = sscanf(line, "P: PS=%u, OS=%u, PR=%u, OR=%u, PL=%d, JI=%u", - ps, os, pr, _or, loss, jitter); - return rc == 6 ? 0 : -1; - } - } - - return -1; -} diff --git a/openbsc/src/libmgcp/mgcp_sdp.c b/openbsc/src/libmgcp/mgcp_sdp.c deleted file mode 100644 index b6489449..00000000 --- a/openbsc/src/libmgcp/mgcp_sdp.c +++ /dev/null @@ -1,305 +0,0 @@ -/* - * Some SDP file parsing... - * - * (C) 2009-2015 by Holger Hans Peter Freyther - * (C) 2009-2014 by On-Waves - * All Rights Reserved - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation; either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - * - */ - -#include -#include - -#include - -struct sdp_rtp_map { - /* the type */ - int payload_type; - /* null, static or later dynamic codec name */ - char *codec_name; - /* A pointer to the original line for later parsing */ - char *map_line; - - int rate; - int channels; -}; - -int mgcp_set_audio_info(void *ctx, struct mgcp_rtp_codec *codec, - int payload_type, const char *audio_name) -{ - int rate = codec->rate; - int channels = codec->channels; - char audio_codec[64]; - - talloc_free(codec->subtype_name); - codec->subtype_name = NULL; - talloc_free(codec->audio_name); - codec->audio_name = NULL; - - if (payload_type != PTYPE_UNDEFINED) - codec->payload_type = payload_type; - - if (!audio_name) { - switch (payload_type) { - case 0: audio_name = "PCMU/8000/1"; break; - case 3: audio_name = "GSM/8000/1"; break; - case 8: audio_name = "PCMA/8000/1"; break; - case 18: audio_name = "G729/8000/1"; break; - default: - /* Payload type is unknown, don't change rate and - * channels. */ - /* TODO: return value? */ - return 0; - } - } - - if (sscanf(audio_name, "%63[^/]/%d/%d", - audio_codec, &rate, &channels) < 1) - return -EINVAL; - - codec->rate = rate; - codec->channels = channels; - codec->subtype_name = talloc_strdup(ctx, audio_codec); - codec->audio_name = talloc_strdup(ctx, audio_name); - - if (!strcmp(audio_codec, "G729")) { - codec->frame_duration_num = 10; - codec->frame_duration_den = 1000; - } else { - codec->frame_duration_num = DEFAULT_RTP_AUDIO_FRAME_DUR_NUM; - codec->frame_duration_den = DEFAULT_RTP_AUDIO_FRAME_DUR_DEN; - } - - if (payload_type < 0) { - payload_type = 96; - if (rate == 8000 && channels == 1) { - if (!strcmp(audio_codec, "GSM")) - payload_type = 3; - else if (!strcmp(audio_codec, "PCMA")) - payload_type = 8; - else if (!strcmp(audio_codec, "PCMU")) - payload_type = 0; - else if (!strcmp(audio_codec, "G729")) - payload_type = 18; - } - - codec->payload_type = payload_type; - } - - if (channels != 1) - LOGP(DMGCP, LOGL_NOTICE, - "Channels != 1 in SDP: '%s'\n", audio_name); - - return 0; -} - -void codecs_initialize(void *ctx, struct sdp_rtp_map *codecs, int used) -{ - int i; - - for (i = 0; i < used; ++i) { - switch (codecs[i].payload_type) { - case 0: - codecs[i].codec_name = "PCMU"; - codecs[i].rate = 8000; - codecs[i].channels = 1; - break; - case 3: - codecs[i].codec_name = "GSM"; - codecs[i].rate = 8000; - codecs[i].channels = 1; - break; - case 8: - codecs[i].codec_name = "PCMA"; - codecs[i].rate = 8000; - codecs[i].channels = 1; - break; - case 18: - codecs[i].codec_name = "G729"; - codecs[i].rate = 8000; - codecs[i].channels = 1; - break; - } - } -} - -void codecs_update(void *ctx, struct sdp_rtp_map *codecs, int used, int payload, char *audio_name) -{ - int i; - - for (i = 0; i < used; ++i) { - char audio_codec[64]; - int rate = -1; - int channels = -1; - if (codecs[i].payload_type != payload) - continue; - if (sscanf(audio_name, "%63[^/]/%d/%d", - audio_codec, &rate, &channels) < 1) { - LOGP(DMGCP, LOGL_ERROR, "Failed to parse '%s'\n", audio_name); - continue; - } - - codecs[i].map_line = talloc_strdup(ctx, audio_name); - codecs[i].codec_name = talloc_strdup(ctx, audio_codec); - codecs[i].rate = rate; - codecs[i].channels = channels; - return; - } - - LOGP(DMGCP, LOGL_ERROR, "Unconfigured PT(%d) with %s\n", payload, audio_name); -} - -int is_codec_compatible(struct mgcp_endpoint *endp, struct sdp_rtp_map *codec) -{ - char *bts_codec; - char audio_codec[64]; - - if (!codec->codec_name) - return 0; - - /* - * GSM, GSM/8000 and GSM/8000/1 should all be compatible.. let's go - * by name first. - */ - bts_codec = endp->tcfg->audio_name; - if (sscanf(bts_codec, "%63[^/]/%*d/%*d", audio_codec) < 1) - return 0; - - return strcasecmp(audio_codec, codec->codec_name) == 0; -} - -int mgcp_parse_sdp_data(struct mgcp_endpoint *endp, struct mgcp_rtp_end *rtp, struct mgcp_parse_data *p) -{ - struct sdp_rtp_map codecs[10]; - int codecs_used = 0; - char *line; - int maxptime = -1; - int i; - int codecs_assigned = 0; - void *tmp_ctx = talloc_new(NULL); - - memset(&codecs, 0, sizeof(codecs)); - - for_each_line(line, p->save) { - switch (line[0]) { - case 'o': - case 's': - case 't': - case 'v': - /* skip these SDP attributes */ - break; - case 'a': { - int payload; - int ptime, ptime2 = 0; - char audio_name[64]; - - - if (sscanf(line, "a=rtpmap:%d %63s", - &payload, audio_name) == 2) { - codecs_update(tmp_ctx, codecs, codecs_used, payload, audio_name); - } else if (sscanf(line, "a=ptime:%d-%d", - &ptime, &ptime2) >= 1) { - if (ptime2 > 0 && ptime2 != ptime) - rtp->packet_duration_ms = 0; - else - rtp->packet_duration_ms = ptime; - } else if (sscanf(line, "a=maxptime:%d", &ptime2) == 1) { - maxptime = ptime2; - } - break; - } - case 'm': { - int port, rc; - - rc = sscanf(line, "m=audio %d RTP/AVP %d %d %d %d %d %d %d %d %d %d", - &port, - &codecs[0].payload_type, - &codecs[1].payload_type, - &codecs[2].payload_type, - &codecs[3].payload_type, - &codecs[4].payload_type, - &codecs[5].payload_type, - &codecs[6].payload_type, - &codecs[7].payload_type, - &codecs[8].payload_type, - &codecs[9].payload_type); - if (rc >= 2) { - rtp->rtp_port = htons(port); - rtp->rtcp_port = htons(port + 1); - codecs_used = rc - 1; - codecs_initialize(tmp_ctx, codecs, codecs_used); - } - break; - } - case 'c': { - char ipv4[16]; - - if (sscanf(line, "c=IN IP4 %15s", ipv4) == 1) { - inet_aton(ipv4, &rtp->addr); - } - break; - } - default: - if (p->endp) - LOGP(DMGCP, LOGL_NOTICE, - "Unhandled SDP option: '%c'/%d on 0x%x\n", - line[0], line[0], ENDPOINT_NUMBER(p->endp)); - else - LOGP(DMGCP, LOGL_NOTICE, - "Unhandled SDP option: '%c'/%d\n", - line[0], line[0]); - break; - } - } - - /* Now select the primary and alt_codec */ - for (i = 0; i < codecs_used && codecs_assigned < 2; ++i) { - struct mgcp_rtp_codec *codec = codecs_assigned == 0 ? - &rtp->codec : &rtp->alt_codec; - - if (endp->tcfg->no_audio_transcoding && - !is_codec_compatible(endp, &codecs[i])) { - LOGP(DMGCP, LOGL_NOTICE, "Skipping codec %s\n", - codecs[i].codec_name); - continue; - } - - mgcp_set_audio_info(p->cfg, codec, - codecs[i].payload_type, - codecs[i].map_line); - codecs_assigned += 1; - } - - if (codecs_assigned > 0) { - /* TODO/XXX: Store this per codec and derive it on use */ - if (maxptime >= 0 && maxptime * rtp->codec.frame_duration_den > - rtp->codec.frame_duration_num * 1500) { - /* more than 1 frame */ - rtp->packet_duration_ms = 0; - } - - LOGP(DMGCP, LOGL_NOTICE, - "Got media info via SDP: port %d, payload %d (%s), " - "duration %d, addr %s\n", - ntohs(rtp->rtp_port), rtp->codec.payload_type, - rtp->codec.subtype_name ? rtp->codec.subtype_name : "unknown", - rtp->packet_duration_ms, inet_ntoa(rtp->addr)); - } - - talloc_free(tmp_ctx); - return codecs_assigned > 0; -} - diff --git a/openbsc/src/libmgcp/mgcp_transcode.c b/openbsc/src/libmgcp/mgcp_transcode.c deleted file mode 100644 index f31e7aef..00000000 --- a/openbsc/src/libmgcp/mgcp_transcode.c +++ /dev/null @@ -1,612 +0,0 @@ -/* - * (C) 2014 by On-Waves - * All Rights Reserved - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation; either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - * - */ - -#include -#include -#include - - -#include "g711common.h" - -#include -#include -#include -#include - -#include -#include - -int mgcp_transcoding_get_frame_size(void *state_, int nsamples, int dst) -{ - struct mgcp_process_rtp_state *state = state_; - if (dst) - return (nsamples >= 0 ? - nsamples / state->dst_samples_per_frame : - 1) * state->dst_frame_size; - else - return (nsamples >= 0 ? - nsamples / state->src_samples_per_frame : - 1) * state->src_frame_size; -} - -static enum audio_format get_audio_format(const struct mgcp_rtp_codec *codec) -{ - if (codec->subtype_name) { - if (!strcasecmp("GSM", codec->subtype_name)) - return AF_GSM; - if (!strcasecmp("PCMA", codec->subtype_name)) - return AF_PCMA; - if (!strcasecmp("PCMU", codec->subtype_name)) - return AF_PCMU; -#ifdef HAVE_BCG729 - if (!strcasecmp("G729", codec->subtype_name)) - return AF_G729; -#endif - if (!strcasecmp("L16", codec->subtype_name)) - return AF_L16; - } - - switch (codec->payload_type) { - case 0 /* PCMU */: - return AF_PCMU; - case 3 /* GSM */: - return AF_GSM; - case 8 /* PCMA */: - return AF_PCMA; -#ifdef HAVE_BCG729 - case 18 /* G.729 */: - return AF_G729; -#endif - case 11 /* L16 */: - return AF_L16; - default: - return AF_INVALID; - } -} - -static void l16_encode(short *sample, unsigned char *buf, size_t n) -{ - for (; n > 0; --n, ++sample, buf += 2) { - buf[0] = sample[0] >> 8; - buf[1] = sample[0] & 0xff; - } -} - -static void l16_decode(unsigned char *buf, short *sample, size_t n) -{ - for (; n > 0; --n, ++sample, buf += 2) - sample[0] = ((short)buf[0] << 8) | buf[1]; -} - -static void alaw_encode(short *sample, unsigned char *buf, size_t n) -{ - for (; n > 0; --n) - *(buf++) = s16_to_alaw(*(sample++)); -} - -static void alaw_decode(unsigned char *buf, short *sample, size_t n) -{ - for (; n > 0; --n) - *(sample++) = alaw_to_s16(*(buf++)); -} - -static void ulaw_encode(short *sample, unsigned char *buf, size_t n) -{ - for (; n > 0; --n) - *(buf++) = s16_to_ulaw(*(sample++)); -} - -static void ulaw_decode(unsigned char *buf, short *sample, size_t n) -{ - for (; n > 0; --n) - *(sample++) = ulaw_to_s16(*(buf++)); -} - -static int processing_state_destructor(struct mgcp_process_rtp_state *state) -{ - switch (state->src_fmt) { - case AF_GSM: - if (state->src.gsm_handle) - gsm_destroy(state->src.gsm_handle); - break; -#ifdef HAVE_BCG729 - case AF_G729: - if (state->src.g729_dec) - closeBcg729DecoderChannel(state->src.g729_dec); - break; -#endif - default: - break; - } - switch (state->dst_fmt) { - case AF_GSM: - if (state->dst.gsm_handle) - gsm_destroy(state->dst.gsm_handle); - break; -#ifdef HAVE_BCG729 - case AF_G729: - if (state->dst.g729_enc) - closeBcg729EncoderChannel(state->dst.g729_enc); - break; -#endif - default: - break; - } - return 0; -} - -int mgcp_transcoding_setup(struct mgcp_endpoint *endp, - struct mgcp_rtp_end *dst_end, - struct mgcp_rtp_end *src_end) -{ - struct mgcp_process_rtp_state *state; - enum audio_format src_fmt, dst_fmt; - const struct mgcp_rtp_codec *dst_codec = &dst_end->codec; - - /* cleanup first */ - if (dst_end->rtp_process_data) { - talloc_free(dst_end->rtp_process_data); - dst_end->rtp_process_data = NULL; - } - - if (!src_end) - return 0; - - const struct mgcp_rtp_codec *src_codec = &src_end->codec; - - if (endp->tcfg->no_audio_transcoding) { - LOGP(DMGCP, LOGL_NOTICE, - "Transcoding disabled on endpoint 0x%x\n", - ENDPOINT_NUMBER(endp)); - return 0; - } - - src_fmt = get_audio_format(src_codec); - dst_fmt = get_audio_format(dst_codec); - - LOGP(DMGCP, LOGL_ERROR, - "Checking transcoding: %s (%d) -> %s (%d)\n", - src_codec->subtype_name, src_codec->payload_type, - dst_codec->subtype_name, dst_codec->payload_type); - - if (src_fmt == AF_INVALID || dst_fmt == AF_INVALID) { - if (!src_codec->subtype_name || !dst_codec->subtype_name) - /* Not enough info, do nothing */ - return 0; - - if (strcasecmp(src_codec->subtype_name, dst_codec->subtype_name) == 0) - /* Nothing to do */ - return 0; - - LOGP(DMGCP, LOGL_ERROR, - "Cannot transcode: %s codec not supported (%s -> %s).\n", - src_fmt != AF_INVALID ? "destination" : "source", - src_codec->audio_name, dst_codec->audio_name); - return -EINVAL; - } - - if (src_codec->rate && dst_codec->rate && src_codec->rate != dst_codec->rate) { - LOGP(DMGCP, LOGL_ERROR, - "Cannot transcode: rate conversion (%d -> %d) not supported.\n", - src_codec->rate, dst_codec->rate); - return -EINVAL; - } - - state = talloc_zero(endp->tcfg->cfg, struct mgcp_process_rtp_state); - talloc_set_destructor(state, processing_state_destructor); - dst_end->rtp_process_data = state; - - state->src_fmt = src_fmt; - - switch (state->src_fmt) { - case AF_L16: - case AF_S16: - state->src_frame_size = 80 * sizeof(short); - state->src_samples_per_frame = 80; - break; - case AF_GSM: - state->src_frame_size = sizeof(gsm_frame); - state->src_samples_per_frame = 160; - state->src.gsm_handle = gsm_create(); - if (!state->src.gsm_handle) { - LOGP(DMGCP, LOGL_ERROR, - "Failed to initialize GSM decoder.\n"); - return -EINVAL; - } - break; -#ifdef HAVE_BCG729 - case AF_G729: - state->src_frame_size = 10; - state->src_samples_per_frame = 80; - state->src.g729_dec = initBcg729DecoderChannel(); - if (!state->src.g729_dec) { - LOGP(DMGCP, LOGL_ERROR, - "Failed to initialize G.729 decoder.\n"); - return -EINVAL; - } - break; -#endif - case AF_PCMU: - case AF_PCMA: - state->src_frame_size = 80; - state->src_samples_per_frame = 80; - break; - default: - break; - } - - state->dst_fmt = dst_fmt; - - switch (state->dst_fmt) { - case AF_L16: - case AF_S16: - state->dst_frame_size = 80*sizeof(short); - state->dst_samples_per_frame = 80; - break; - case AF_GSM: - state->dst_frame_size = sizeof(gsm_frame); - state->dst_samples_per_frame = 160; - state->dst.gsm_handle = gsm_create(); - if (!state->dst.gsm_handle) { - LOGP(DMGCP, LOGL_ERROR, - "Failed to initialize GSM encoder.\n"); - return -EINVAL; - } - break; -#ifdef HAVE_BCG729 - case AF_G729: - state->dst_frame_size = 10; - state->dst_samples_per_frame = 80; - state->dst.g729_enc = initBcg729EncoderChannel(); - if (!state->dst.g729_enc) { - LOGP(DMGCP, LOGL_ERROR, - "Failed to initialize G.729 decoder.\n"); - return -EINVAL; - } - break; -#endif - case AF_PCMU: - case AF_PCMA: - state->dst_frame_size = 80; - state->dst_samples_per_frame = 80; - break; - default: - break; - } - - if (dst_end->force_output_ptime) - state->dst_packet_duration = mgcp_rtp_packet_duration(endp, dst_end); - - LOGP(DMGCP, LOGL_INFO, - "Initialized RTP processing on: 0x%x " - "conv: %d (%d, %d, %s) -> %d (%d, %d, %s)\n", - ENDPOINT_NUMBER(endp), - src_fmt, src_codec->payload_type, src_codec->rate, src_end->fmtp_extra, - dst_fmt, dst_codec->payload_type, dst_codec->rate, dst_end->fmtp_extra); - - return 0; -} - -void mgcp_transcoding_net_downlink_format(struct mgcp_endpoint *endp, - int *payload_type, - const char**audio_name, - const char**fmtp_extra) -{ - struct mgcp_process_rtp_state *state = endp->net_end.rtp_process_data; - struct mgcp_rtp_codec *net_codec = &endp->net_end.codec; - struct mgcp_rtp_codec *bts_codec = &endp->bts_end.codec; - - if (!state || net_codec->payload_type < 0) { - *payload_type = bts_codec->payload_type; - *audio_name = bts_codec->audio_name; - *fmtp_extra = endp->bts_end.fmtp_extra; - return; - } - - *payload_type = net_codec->payload_type; - *audio_name = net_codec->audio_name; - *fmtp_extra = endp->net_end.fmtp_extra; -} - -static int decode_audio(struct mgcp_process_rtp_state *state, - uint8_t **src, size_t *nbytes) -{ - while (*nbytes >= state->src_frame_size) { - if (state->sample_cnt + state->src_samples_per_frame > ARRAY_SIZE(state->samples)) { - LOGP(DMGCP, LOGL_ERROR, - "Sample buffer too small: %zu > %zu.\n", - state->sample_cnt + state->src_samples_per_frame, - ARRAY_SIZE(state->samples)); - return -ENOSPC; - } - switch (state->src_fmt) { - case AF_GSM: - if (gsm_decode(state->src.gsm_handle, - (gsm_byte *)*src, state->samples + state->sample_cnt) < 0) { - LOGP(DMGCP, LOGL_ERROR, - "Failed to decode GSM.\n"); - return -EINVAL; - } - break; -#ifdef HAVE_BCG729 - case AF_G729: - bcg729Decoder(state->src.g729_dec, *src, 0, state->samples + state->sample_cnt); - break; -#endif - case AF_PCMU: - ulaw_decode(*src, state->samples + state->sample_cnt, - state->src_samples_per_frame); - break; - case AF_PCMA: - alaw_decode(*src, state->samples + state->sample_cnt, - state->src_samples_per_frame); - break; - case AF_S16: - memmove(state->samples + state->sample_cnt, *src, - state->src_frame_size); - break; - case AF_L16: - l16_decode(*src, state->samples + state->sample_cnt, - state->src_samples_per_frame); - break; - default: - break; - } - *src += state->src_frame_size; - *nbytes -= state->src_frame_size; - state->sample_cnt += state->src_samples_per_frame; - } - return 0; -} - -static int encode_audio(struct mgcp_process_rtp_state *state, - uint8_t *dst, size_t buf_size, size_t max_samples) -{ - int nbytes = 0; - size_t nsamples = 0; - /* Encode samples into dst */ - while (nsamples + state->dst_samples_per_frame <= max_samples) { - if (nbytes + state->dst_frame_size > buf_size) { - if (nbytes > 0) - break; - - /* Not even one frame fits into the buffer */ - LOGP(DMGCP, LOGL_INFO, - "Encoding (RTP) buffer too small: %zu > %zu.\n", - nbytes + state->dst_frame_size, buf_size); - return -ENOSPC; - } - switch (state->dst_fmt) { - case AF_GSM: - gsm_encode(state->dst.gsm_handle, - state->samples + state->sample_offs, dst); - break; -#ifdef HAVE_BCG729 - case AF_G729: - bcg729Encoder(state->dst.g729_enc, - state->samples + state->sample_offs, dst); - break; -#endif - case AF_PCMU: - ulaw_encode(state->samples + state->sample_offs, dst, - state->src_samples_per_frame); - break; - case AF_PCMA: - alaw_encode(state->samples + state->sample_offs, dst, - state->src_samples_per_frame); - break; - case AF_S16: - memmove(dst, state->samples + state->sample_offs, - state->dst_frame_size); - break; - case AF_L16: - l16_encode(state->samples + state->sample_offs, dst, - state->src_samples_per_frame); - break; - default: - break; - } - dst += state->dst_frame_size; - nbytes += state->dst_frame_size; - state->sample_offs += state->dst_samples_per_frame; - nsamples += state->dst_samples_per_frame; - } - state->sample_cnt -= nsamples; - return nbytes; -} - -static struct mgcp_rtp_end *source_for_dest(struct mgcp_endpoint *endp, - struct mgcp_rtp_end *dst_end) -{ - if (&endp->bts_end == dst_end) - return &endp->net_end; - else if (&endp->net_end == dst_end) - return &endp->bts_end; - OSMO_ASSERT(0); -} - -/* - * With some modems we get offered multiple codecs - * and we have selected one of them. It might not - * be the right one and we need to detect this with - * the first audio packets. One difficulty is that - * we patch the rtp payload type in place, so we - * need to discuss this. - */ -struct mgcp_process_rtp_state *check_transcode_state( - struct mgcp_endpoint *endp, - struct mgcp_rtp_end *dst_end, - struct rtp_hdr *rtp_hdr) -{ - struct mgcp_rtp_end *src_end; - - /* Only deal with messages from net to bts */ - if (&endp->bts_end != dst_end) - goto done; - - src_end = source_for_dest(endp, dst_end); - - /* Already patched */ - if (rtp_hdr->payload_type == dst_end->codec.payload_type) - goto done; - /* The payload we expect */ - if (rtp_hdr->payload_type == src_end->codec.payload_type) - goto done; - /* The matching alternate payload type? Then switch */ - if (rtp_hdr->payload_type == src_end->alt_codec.payload_type) { - struct mgcp_config *cfg = endp->cfg; - struct mgcp_rtp_codec tmp_codec = src_end->alt_codec; - src_end->alt_codec = src_end->codec; - src_end->codec = tmp_codec; - cfg->setup_rtp_processing_cb(endp, &endp->net_end, &endp->bts_end); - cfg->setup_rtp_processing_cb(endp, &endp->bts_end, &endp->net_end); - } - -done: - return dst_end->rtp_process_data; -} - -int mgcp_transcoding_process_rtp(struct mgcp_endpoint *endp, - struct mgcp_rtp_end *dst_end, - char *data, int *len, int buf_size) -{ - struct mgcp_process_rtp_state *state; - const size_t rtp_hdr_size = sizeof(struct rtp_hdr); - struct rtp_hdr *rtp_hdr = (struct rtp_hdr *) data; - char *payload_data = (char *) &rtp_hdr->data[0]; - int payload_len = *len - rtp_hdr_size; - uint8_t *src = (uint8_t *)payload_data; - uint8_t *dst = (uint8_t *)payload_data; - size_t nbytes = payload_len; - size_t nsamples; - size_t max_samples; - uint32_t ts_no; - int rc; - - state = check_transcode_state(endp, dst_end, rtp_hdr); - if (!state) - return 0; - - if (state->src_fmt == state->dst_fmt) { - if (!state->dst_packet_duration) - return 0; - - /* TODO: repackage without transcoding */ - } - - /* If the remaining samples do not fit into a fixed ptime, - * a) discard them, if the next packet is much later - * b) add silence and * send it, if the current packet is not - * yet too late - * c) append the sample data, if the timestamp matches exactly - */ - - /* TODO: check payload type (-> G.711 comfort noise) */ - - if (payload_len > 0) { - ts_no = ntohl(rtp_hdr->timestamp); - if (!state->is_running) { - state->next_seq = ntohs(rtp_hdr->sequence); - state->next_time = ts_no; - state->is_running = 1; - } - - - if (state->sample_cnt > 0) { - int32_t delta = ts_no - state->next_time; - /* TODO: check sequence? reordering? packet loss? */ - - if (delta > state->sample_cnt) { - /* There is a time gap between the last packet - * and the current one. Just discard the - * partial data that is left in the buffer. - * TODO: This can be improved by adding silence - * instead if the delta is small enough. - */ - LOGP(DMGCP, LOGL_NOTICE, - "0x%x dropping sample buffer due delta=%d sample_cnt=%zu\n", - ENDPOINT_NUMBER(endp), delta, state->sample_cnt); - state->sample_cnt = 0; - state->next_time = ts_no; - } else if (delta < 0) { - LOGP(DMGCP, LOGL_NOTICE, - "RTP time jumps backwards, delta = %d, " - "discarding buffered samples\n", - delta); - state->sample_cnt = 0; - state->sample_offs = 0; - return -EAGAIN; - } - - /* Make sure the samples start without offset */ - if (state->sample_offs && state->sample_cnt) - memmove(&state->samples[0], - &state->samples[state->sample_offs], - state->sample_cnt * - sizeof(state->samples[0])); - } - - state->sample_offs = 0; - - /* Append decoded audio to samples */ - decode_audio(state, &src, &nbytes); - - if (nbytes > 0) - LOGP(DMGCP, LOGL_NOTICE, - "Skipped audio frame in RTP packet: %zu octets\n", - nbytes); - } else - ts_no = state->next_time; - - if (state->sample_cnt < state->dst_packet_duration) - return -EAGAIN; - - max_samples = - state->dst_packet_duration ? - state->dst_packet_duration : state->sample_cnt; - - nsamples = state->sample_cnt; - - rc = encode_audio(state, dst, buf_size, max_samples); - /* - * There were no samples to encode? - * TODO: how does this work for comfort noise? - */ - if (rc == 0) - return -ENOMSG; - /* Any other error during the encoding */ - if (rc < 0) - return rc; - - nsamples -= state->sample_cnt; - - *len = rtp_hdr_size + rc; - rtp_hdr->sequence = htons(state->next_seq); - rtp_hdr->timestamp = htonl(ts_no); - - state->next_seq += 1; - state->next_time = ts_no + nsamples; - - /* - * XXX: At this point we should always have consumed - * samples. So doing OSMO_ASSERT(nsamples > 0) and returning - * rtp_hdr_size should be fine. - */ - return nsamples ? rtp_hdr_size : 0; -} diff --git a/openbsc/src/libmgcp/mgcp_vty.c b/openbsc/src/libmgcp/mgcp_vty.c deleted file mode 100644 index 7d4b2da8..00000000 --- a/openbsc/src/libmgcp/mgcp_vty.c +++ /dev/null @@ -1,1543 +0,0 @@ -/* A Media Gateway Control Protocol Media Gateway: RFC 3435 */ -/* The protocol implementation */ - -/* - * (C) 2009-2014 by Holger Hans Peter Freyther - * (C) 2009-2011 by On-Waves - * All Rights Reserved - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation; either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - * - */ - - -#include - -#include -#include -#include - -#include - -#define RTCP_OMIT_STR "Drop RTCP packets in both directions\n" -#define RTP_PATCH_STR "Modify RTP packet header in both directions\n" -#define RTP_KEEPALIVE_STR "Send dummy UDP packet to net RTP destination\n" - -static struct mgcp_config *g_cfg = NULL; - -static struct mgcp_trunk_config *find_trunk(struct mgcp_config *cfg, int nr) -{ - struct mgcp_trunk_config *trunk; - - if (nr == 0) - trunk = &cfg->trunk; - else - trunk = mgcp_trunk_num(cfg, nr); - - return trunk; -} - -/* - * vty code for mgcp below - */ -struct cmd_node mgcp_node = { - MGCP_NODE, - "%s(config-mgcp)# ", - 1, -}; - -struct cmd_node trunk_node = { - TRUNK_NODE, - "%s(config-mgcp-trunk)# ", - 1, -}; - -static int config_write_mgcp(struct vty *vty) -{ - vty_out(vty, "mgcp%s", VTY_NEWLINE); - if (g_cfg->local_ip) - vty_out(vty, " local ip %s%s", g_cfg->local_ip, VTY_NEWLINE); - if (g_cfg->bts_ip && strlen(g_cfg->bts_ip) != 0) - vty_out(vty, " bts ip %s%s", g_cfg->bts_ip, VTY_NEWLINE); - vty_out(vty, " bind ip %s%s", g_cfg->source_addr, VTY_NEWLINE); - vty_out(vty, " bind port %u%s", g_cfg->source_port, VTY_NEWLINE); - - if (g_cfg->bts_ports.mode == PORT_ALLOC_STATIC) - vty_out(vty, " rtp bts-base %u%s", g_cfg->bts_ports.base_port, VTY_NEWLINE); - else - vty_out(vty, " rtp bts-range %u %u%s", - g_cfg->bts_ports.range_start, g_cfg->bts_ports.range_end, VTY_NEWLINE); - if (g_cfg->bts_ports.bind_addr) - vty_out(vty, " rtp bts-bind-ip %s%s", g_cfg->bts_ports.bind_addr, VTY_NEWLINE); - - if (g_cfg->net_ports.mode == PORT_ALLOC_STATIC) - vty_out(vty, " rtp net-base %u%s", g_cfg->net_ports.base_port, VTY_NEWLINE); - else - vty_out(vty, " rtp net-range %u %u%s", - g_cfg->net_ports.range_start, g_cfg->net_ports.range_end, VTY_NEWLINE); - if (g_cfg->net_ports.bind_addr) - vty_out(vty, " rtp net-bind-ip %s%s", g_cfg->net_ports.bind_addr, VTY_NEWLINE); - - vty_out(vty, " rtp ip-dscp %d%s", g_cfg->endp_dscp, VTY_NEWLINE); - if (g_cfg->trunk.keepalive_interval == MGCP_KEEPALIVE_ONCE) - vty_out(vty, " rtp keep-alive once%s", VTY_NEWLINE); - else if (g_cfg->trunk.keepalive_interval) - vty_out(vty, " rtp keep-alive %d%s", - g_cfg->trunk.keepalive_interval, VTY_NEWLINE); - else - vty_out(vty, " no rtp keep-alive%s", VTY_NEWLINE); - - if (g_cfg->trunk.omit_rtcp) - vty_out(vty, " rtcp-omit%s", VTY_NEWLINE); - else - vty_out(vty, " no rtcp-omit%s", VTY_NEWLINE); - if (g_cfg->trunk.force_constant_ssrc || g_cfg->trunk.force_aligned_timing) { - vty_out(vty, " %srtp-patch ssrc%s", - g_cfg->trunk.force_constant_ssrc ? "" : "no ", VTY_NEWLINE); - vty_out(vty, " %srtp-patch timestamp%s", - g_cfg->trunk.force_aligned_timing ? "" : "no ", VTY_NEWLINE); - } else - vty_out(vty, " no rtp-patch%s", VTY_NEWLINE); - if (g_cfg->trunk.audio_payload != -1) - vty_out(vty, " sdp audio-payload number %d%s", - g_cfg->trunk.audio_payload, VTY_NEWLINE); - if (g_cfg->trunk.audio_name) - vty_out(vty, " sdp audio-payload name %s%s", - g_cfg->trunk.audio_name, VTY_NEWLINE); - if (g_cfg->trunk.audio_fmtp_extra) - vty_out(vty, " sdp audio fmtp-extra %s%s", - g_cfg->trunk.audio_fmtp_extra, VTY_NEWLINE); - vty_out(vty, " %ssdp audio-payload send-ptime%s", - g_cfg->trunk.audio_send_ptime ? "" : "no ", VTY_NEWLINE); - vty_out(vty, " %ssdp audio-payload send-name%s", - g_cfg->trunk.audio_send_name ? "" : "no ", VTY_NEWLINE); - vty_out(vty, " loop %u%s", !!g_cfg->trunk.audio_loop, VTY_NEWLINE); - vty_out(vty, " number endpoints %u%s", g_cfg->trunk.number_endpoints - 1, VTY_NEWLINE); - vty_out(vty, " %sallow-transcoding%s", - g_cfg->trunk.no_audio_transcoding ? "no " : "", VTY_NEWLINE); - if (g_cfg->call_agent_addr) - vty_out(vty, " call-agent ip %s%s", g_cfg->call_agent_addr, VTY_NEWLINE); - if (g_cfg->transcoder_ip) - vty_out(vty, " transcoder-mgw %s%s", g_cfg->transcoder_ip, VTY_NEWLINE); - - if (g_cfg->transcoder_ports.mode == PORT_ALLOC_STATIC) - vty_out(vty, " rtp transcoder-base %u%s", g_cfg->transcoder_ports.base_port, VTY_NEWLINE); - else - vty_out(vty, " rtp transcoder-range %u %u%s", - g_cfg->transcoder_ports.range_start, g_cfg->transcoder_ports.range_end, VTY_NEWLINE); - if (g_cfg->bts_force_ptime > 0) - vty_out(vty, " rtp force-ptime %d%s", g_cfg->bts_force_ptime, VTY_NEWLINE); - vty_out(vty, " transcoder-remote-base %u%s", g_cfg->transcoder_remote_base, VTY_NEWLINE); - - switch (g_cfg->osmux) { - case OSMUX_USAGE_ON: - vty_out(vty, " osmux on%s", VTY_NEWLINE); - break; - case OSMUX_USAGE_ONLY: - vty_out(vty, " osmux only%s", VTY_NEWLINE); - break; - case OSMUX_USAGE_OFF: - default: - vty_out(vty, " osmux off%s", VTY_NEWLINE); - break; - } - if (g_cfg->osmux) { - vty_out(vty, " osmux bind-ip %s%s", - g_cfg->osmux_addr, VTY_NEWLINE); - vty_out(vty, " osmux batch-factor %d%s", - g_cfg->osmux_batch, VTY_NEWLINE); - vty_out(vty, " osmux batch-size %u%s", - g_cfg->osmux_batch_size, VTY_NEWLINE); - vty_out(vty, " osmux port %u%s", - g_cfg->osmux_port, VTY_NEWLINE); - vty_out(vty, " osmux dummy %s%s", - g_cfg->osmux_dummy ? "on" : "off", VTY_NEWLINE); - } - return CMD_SUCCESS; -} - -static void dump_rtp_end(const char *end_name, struct vty *vty, - struct mgcp_rtp_state *state, struct mgcp_rtp_end *end) -{ - struct mgcp_rtp_codec *codec = &end->codec; - - vty_out(vty, - " %s%s" - " Timestamp Errs: %d->%d%s" - " Dropped Packets: %d%s" - " Payload Type: %d Rate: %u Channels: %d %s" - " Frame Duration: %u Frame Denominator: %u%s" - " FPP: %d Packet Duration: %u%s" - " FMTP-Extra: %s Audio-Name: %s Sub-Type: %s%s" - " Output-Enabled: %d Force-PTIME: %d%s", - end_name, VTY_NEWLINE, - state->in_stream.err_ts_counter, - state->out_stream.err_ts_counter, VTY_NEWLINE, - end->dropped_packets, VTY_NEWLINE, - codec->payload_type, codec->rate, codec->channels, VTY_NEWLINE, - codec->frame_duration_num, codec->frame_duration_den, VTY_NEWLINE, - end->frames_per_packet, end->packet_duration_ms, VTY_NEWLINE, - end->fmtp_extra, codec->audio_name, codec->subtype_name, VTY_NEWLINE, - end->output_enabled, end->force_output_ptime, VTY_NEWLINE); -} - -static void dump_trunk(struct vty *vty, struct mgcp_trunk_config *cfg, int verbose) -{ - int i; - - vty_out(vty, "%s trunk nr %d with %d endpoints:%s", - cfg->trunk_type == MGCP_TRUNK_VIRTUAL ? "Virtual" : "E1", - cfg->trunk_nr, cfg->number_endpoints - 1, VTY_NEWLINE); - - if (!cfg->endpoints) { - vty_out(vty, "No endpoints allocated yet.%s", VTY_NEWLINE); - return; - } - - for (i = 1; i < cfg->number_endpoints; ++i) { - struct mgcp_endpoint *endp = &cfg->endpoints[i]; - vty_out(vty, - " Endpoint 0x%.2x: CI: %d net: %u/%u bts: %u/%u on %s " - "traffic received bts: %u remote: %u transcoder: %u/%u%s", - i, endp->ci, - ntohs(endp->net_end.rtp_port), ntohs(endp->net_end.rtcp_port), - ntohs(endp->bts_end.rtp_port), ntohs(endp->bts_end.rtcp_port), - inet_ntoa(endp->bts_end.addr), - endp->bts_end.packets, endp->net_end.packets, - endp->trans_net.packets, endp->trans_bts.packets, - VTY_NEWLINE); - - if (verbose && endp->allocated) { - dump_rtp_end("Net->BTS", vty, &endp->bts_state, &endp->bts_end); - dump_rtp_end("BTS->Net", vty, &endp->net_state, &endp->net_end); - } - } -} - -DEFUN(show_mcgp, show_mgcp_cmd, - "show mgcp [stats]", - SHOW_STR - "Display information about the MGCP Media Gateway\n" - "Include Statistics\n") -{ - struct mgcp_trunk_config *trunk; - int show_stats = argc >= 1; - - dump_trunk(vty, &g_cfg->trunk, show_stats); - - llist_for_each_entry(trunk, &g_cfg->trunks, entry) - dump_trunk(vty, trunk, show_stats); - - if (g_cfg->osmux) - vty_out(vty, "Osmux used CID: %d%s", osmux_used_cid(), VTY_NEWLINE); - - return CMD_SUCCESS; -} - -DEFUN(cfg_mgcp, - cfg_mgcp_cmd, - "mgcp", - "Configure the MGCP") -{ - vty->node = MGCP_NODE; - return CMD_SUCCESS; -} - -DEFUN(cfg_mgcp_local_ip, - cfg_mgcp_local_ip_cmd, - "local ip A.B.C.D", - "Local options for the SDP record\n" - IP_STR - "IPv4 Address to use in SDP record\n") -{ - osmo_talloc_replace_string(g_cfg, &g_cfg->local_ip, argv[0]); - return CMD_SUCCESS; -} - -DEFUN(cfg_mgcp_bts_ip, - cfg_mgcp_bts_ip_cmd, - "bts ip A.B.C.D", - "BTS Audio source/destination options\n" - IP_STR - "IPv4 Address of the BTS\n") -{ - osmo_talloc_replace_string(g_cfg, &g_cfg->bts_ip, argv[0]); - inet_aton(g_cfg->bts_ip, &g_cfg->bts_in); - return CMD_SUCCESS; -} - -#define BIND_STR "Listen/Bind related socket option\n" -DEFUN(cfg_mgcp_bind_ip, - cfg_mgcp_bind_ip_cmd, - "bind ip A.B.C.D", - BIND_STR - IP_STR - "IPv4 Address to bind to\n") -{ - osmo_talloc_replace_string(g_cfg, &g_cfg->source_addr, argv[0]); - return CMD_SUCCESS; -} - -DEFUN(cfg_mgcp_bind_port, - cfg_mgcp_bind_port_cmd, - "bind port <0-65534>", - BIND_STR - "Port information\n" - "UDP port to listen for MGCP messages\n") -{ - unsigned int port = atoi(argv[0]); - g_cfg->source_port = port; - return CMD_SUCCESS; -} - -DEFUN(cfg_mgcp_bind_early, - cfg_mgcp_bind_early_cmd, - "bind early (0|1)", - BIND_STR - "Bind local ports on start up\n" - "Bind on demand\n" "Bind on startup\n") -{ - vty_out(vty, "bind early is deprecated, remove it from the config.\n"); - return CMD_WARNING; -} - -static void parse_base(struct mgcp_port_range *range, const char **argv) -{ - unsigned int port = atoi(argv[0]); - range->mode = PORT_ALLOC_STATIC; - range->base_port = port; -} - -static void parse_range(struct mgcp_port_range *range, const char **argv) -{ - range->mode = PORT_ALLOC_DYNAMIC; - range->range_start = atoi(argv[0]); - range->range_end = atoi(argv[1]); - range->last_port = g_cfg->bts_ports.range_start; -} - - -#define RTP_STR "RTP configuration\n" -#define BTS_START_STR "First UDP port allocated for the BTS side\n" -#define NET_START_STR "First UDP port allocated for the NET side\n" -#define UDP_PORT_STR "UDP Port number\n" -DEFUN(cfg_mgcp_rtp_bts_base_port, - cfg_mgcp_rtp_bts_base_port_cmd, - "rtp bts-base <0-65534>", - RTP_STR - BTS_START_STR - UDP_PORT_STR) -{ - parse_base(&g_cfg->bts_ports, argv); - return CMD_SUCCESS; -} - -#define RANGE_START_STR "Start of the range of ports\n" -#define RANGE_END_STR "End of the range of ports\n" -DEFUN(cfg_mgcp_rtp_bts_range, - cfg_mgcp_rtp_bts_range_cmd, - "rtp bts-range <0-65534> <0-65534>", - RTP_STR "Range of ports to use for the BTS side\n" - RANGE_START_STR RANGE_END_STR) -{ - parse_range(&g_cfg->bts_ports, argv); - return CMD_SUCCESS; -} - -DEFUN(cfg_mgcp_rtp_net_range, - cfg_mgcp_rtp_net_range_cmd, - "rtp net-range <0-65534> <0-65534>", - RTP_STR "Range of ports to use for the NET side\n" - RANGE_START_STR RANGE_END_STR) -{ - parse_range(&g_cfg->net_ports, argv); - return CMD_SUCCESS; -} - -DEFUN(cfg_mgcp_rtp_net_base_port, - cfg_mgcp_rtp_net_base_port_cmd, - "rtp net-base <0-65534>", - RTP_STR NET_START_STR UDP_PORT_STR) -{ - parse_base(&g_cfg->net_ports, argv); - return CMD_SUCCESS; -} - -ALIAS_DEPRECATED(cfg_mgcp_rtp_bts_base_port, cfg_mgcp_rtp_base_port_cmd, - "rtp base <0-65534>", - RTP_STR BTS_START_STR UDP_PORT_STR) - -DEFUN(cfg_mgcp_rtp_transcoder_range, - cfg_mgcp_rtp_transcoder_range_cmd, - "rtp transcoder-range <0-65534> <0-65534>", - RTP_STR "Range of ports to use for the Transcoder\n" - RANGE_START_STR RANGE_END_STR) -{ - parse_range(&g_cfg->transcoder_ports, argv); - return CMD_SUCCESS; -} - -DEFUN(cfg_mgcp_rtp_transcoder_base, - cfg_mgcp_rtp_transcoder_base_cmd, - "rtp transcoder-base <0-65534>", - RTP_STR "First UDP port allocated for the Transcoder side\n" - UDP_PORT_STR) -{ - parse_base(&g_cfg->transcoder_ports, argv); - return CMD_SUCCESS; -} - -DEFUN(cfg_mgcp_rtp_bts_bind_ip, - cfg_mgcp_rtp_bts_bind_ip_cmd, - "rtp bts-bind-ip A.B.C.D", - RTP_STR "Bind endpoints facing the BTS\n" "Address to bind to\n") -{ - osmo_talloc_replace_string(g_cfg, &g_cfg->bts_ports.bind_addr, argv[0]); - return CMD_SUCCESS; -} - -DEFUN(cfg_mgcp_rtp_no_bts_bind_ip, - cfg_mgcp_rtp_no_bts_bind_ip_cmd, - "no rtp bts-bind-ip", - NO_STR RTP_STR "Bind endpoints facing the BTS\n" "Address to bind to\n") -{ - talloc_free(g_cfg->bts_ports.bind_addr); - g_cfg->bts_ports.bind_addr = NULL; - return CMD_SUCCESS; -} - -DEFUN(cfg_mgcp_rtp_net_bind_ip, - cfg_mgcp_rtp_net_bind_ip_cmd, - "rtp net-bind-ip A.B.C.D", - RTP_STR "Bind endpoints facing the Network\n" "Address to bind to\n") -{ - osmo_talloc_replace_string(g_cfg, &g_cfg->net_ports.bind_addr, argv[0]); - return CMD_SUCCESS; -} - -DEFUN(cfg_mgcp_rtp_no_net_bind_ip, - cfg_mgcp_rtp_no_net_bind_ip_cmd, - "no rtp net-bind-ip", - NO_STR RTP_STR "Bind endpoints facing the Network\n" "Address to bind to\n") -{ - talloc_free(g_cfg->net_ports.bind_addr); - g_cfg->net_ports.bind_addr = NULL; - return CMD_SUCCESS; -} - -DEFUN(cfg_mgcp_rtp_ip_dscp, - cfg_mgcp_rtp_ip_dscp_cmd, - "rtp ip-dscp <0-255>", - RTP_STR - "Apply IP_TOS to the audio stream (including Osmux)\n" "The DSCP value\n") -{ - int dscp = atoi(argv[0]); - g_cfg->endp_dscp = dscp; - return CMD_SUCCESS; -} - -ALIAS_DEPRECATED(cfg_mgcp_rtp_ip_dscp, cfg_mgcp_rtp_ip_tos_cmd, - "rtp ip-tos <0-255>", - RTP_STR - "Apply IP_TOS to the audio stream\n" "The DSCP value\n") - -#define FORCE_PTIME_STR "Force a fixed ptime for packets sent to the BTS" -DEFUN(cfg_mgcp_rtp_force_ptime, - cfg_mgcp_rtp_force_ptime_cmd, - "rtp force-ptime (10|20|40)", - RTP_STR FORCE_PTIME_STR - "The required ptime (packet duration) in ms\n" - "10 ms\n20 ms\n40 ms\n") -{ - g_cfg->bts_force_ptime = atoi(argv[0]); - return CMD_SUCCESS; -} - -DEFUN(cfg_mgcp_no_rtp_force_ptime, - cfg_mgcp_no_rtp_force_ptime_cmd, - "no rtp force-ptime", - NO_STR RTP_STR FORCE_PTIME_STR) -{ - g_cfg->bts_force_ptime = 0; - return CMD_SUCCESS; -} - -DEFUN(cfg_mgcp_sdp_fmtp_extra, - cfg_mgcp_sdp_fmtp_extra_cmd, - "sdp audio fmtp-extra .NAME", - "Add extra fmtp for the SDP file\n" "Audio\n" "Fmtp-extra\n" - "Extra Information\n") -{ - char *txt = argv_concat(argv, argc, 0); - if (!txt) - return CMD_WARNING; - - osmo_talloc_replace_string(g_cfg, &g_cfg->trunk.audio_fmtp_extra, txt); - talloc_free(txt); - return CMD_SUCCESS; -} - -DEFUN(cfg_mgcp_allow_transcoding, - cfg_mgcp_allow_transcoding_cmd, - "allow-transcoding", - "Allow transcoding\n") -{ - g_cfg->trunk.no_audio_transcoding = 0; - return CMD_SUCCESS; -} - -DEFUN(cfg_mgcp_no_allow_transcoding, - cfg_mgcp_no_allow_transcoding_cmd, - "no allow-transcoding", - NO_STR "Allow transcoding\n") -{ - g_cfg->trunk.no_audio_transcoding = 1; - return CMD_SUCCESS; -} - -#define SDP_STR "SDP File related options\n" -#define AUDIO_STR "Audio payload options\n" -DEFUN(cfg_mgcp_sdp_payload_number, - cfg_mgcp_sdp_payload_number_cmd, - "sdp audio-payload number <0-255>", - SDP_STR AUDIO_STR - "Number\n" "Payload number\n") -{ - unsigned int payload = atoi(argv[0]); - g_cfg->trunk.audio_payload = payload; - return CMD_SUCCESS; -} - -ALIAS_DEPRECATED(cfg_mgcp_sdp_payload_number, cfg_mgcp_sdp_payload_number_cmd_old, - "sdp audio payload number <0-255>", - SDP_STR AUDIO_STR AUDIO_STR "Number\n" "Payload number\n") - - -DEFUN(cfg_mgcp_sdp_payload_name, - cfg_mgcp_sdp_payload_name_cmd, - "sdp audio-payload name NAME", - SDP_STR AUDIO_STR "Name\n" "Payload name\n") -{ - osmo_talloc_replace_string(g_cfg, &g_cfg->trunk.audio_name, argv[0]); - return CMD_SUCCESS; -} - -ALIAS_DEPRECATED(cfg_mgcp_sdp_payload_name, cfg_mgcp_sdp_payload_name_cmd_old, - "sdp audio payload name NAME", - SDP_STR AUDIO_STR AUDIO_STR "Name\n" "Payload name\n") - -DEFUN(cfg_mgcp_sdp_payload_send_ptime, - cfg_mgcp_sdp_payload_send_ptime_cmd, - "sdp audio-payload send-ptime", - SDP_STR AUDIO_STR - "Send SDP ptime (packet duration) attribute\n") -{ - g_cfg->trunk.audio_send_ptime = 1; - return CMD_SUCCESS; -} - -DEFUN(cfg_mgcp_no_sdp_payload_send_ptime, - cfg_mgcp_no_sdp_payload_send_ptime_cmd, - "no sdp audio-payload send-ptime", - NO_STR SDP_STR AUDIO_STR - "Send SDP ptime (packet duration) attribute\n") -{ - g_cfg->trunk.audio_send_ptime = 0; - return CMD_SUCCESS; -} - -DEFUN(cfg_mgcp_sdp_payload_send_name, - cfg_mgcp_sdp_payload_send_name_cmd, - "sdp audio-payload send-name", - SDP_STR AUDIO_STR - "Send SDP rtpmap with the audio name\n") -{ - g_cfg->trunk.audio_send_name = 1; - return CMD_SUCCESS; -} - -DEFUN(cfg_mgcp_no_sdp_payload_send_name, - cfg_mgcp_no_sdp_payload_send_name_cmd, - "no sdp audio-payload send-name", - NO_STR SDP_STR AUDIO_STR - "Send SDP rtpmap with the audio name\n") -{ - g_cfg->trunk.audio_send_name = 0; - return CMD_SUCCESS; -} - -DEFUN(cfg_mgcp_loop, - cfg_mgcp_loop_cmd, - "loop (0|1)", - "Loop audio for all endpoints on main trunk\n" - "Don't Loop\n" "Loop\n") -{ - if (g_cfg->osmux) { - vty_out(vty, "Cannot use `loop' with `osmux'.%s", VTY_NEWLINE); - return CMD_WARNING; - } - g_cfg->trunk.audio_loop = atoi(argv[0]); - return CMD_SUCCESS; -} - -DEFUN(cfg_mgcp_number_endp, - cfg_mgcp_number_endp_cmd, - "number endpoints <0-65534>", - "Number options\n" "Endpoints available\n" "Number endpoints\n") -{ - /* + 1 as we start counting at one */ - g_cfg->trunk.number_endpoints = atoi(argv[0]) + 1; - return CMD_SUCCESS; -} - -DEFUN(cfg_mgcp_omit_rtcp, - cfg_mgcp_omit_rtcp_cmd, - "rtcp-omit", - RTCP_OMIT_STR) -{ - g_cfg->trunk.omit_rtcp = 1; - return CMD_SUCCESS; -} - -DEFUN(cfg_mgcp_no_omit_rtcp, - cfg_mgcp_no_omit_rtcp_cmd, - "no rtcp-omit", - NO_STR RTCP_OMIT_STR) -{ - g_cfg->trunk.omit_rtcp = 0; - return CMD_SUCCESS; -} - -DEFUN(cfg_mgcp_patch_rtp_ssrc, - cfg_mgcp_patch_rtp_ssrc_cmd, - "rtp-patch ssrc", - RTP_PATCH_STR - "Force a fixed SSRC\n" - ) -{ - g_cfg->trunk.force_constant_ssrc = 1; - return CMD_SUCCESS; -} - -DEFUN(cfg_mgcp_no_patch_rtp_ssrc, - cfg_mgcp_no_patch_rtp_ssrc_cmd, - "no rtp-patch ssrc", - NO_STR RTP_PATCH_STR - "Force a fixed SSRC\n" - ) -{ - g_cfg->trunk.force_constant_ssrc = 0; - return CMD_SUCCESS; -} - -DEFUN(cfg_mgcp_patch_rtp_ts, - cfg_mgcp_patch_rtp_ts_cmd, - "rtp-patch timestamp", - RTP_PATCH_STR - "Adjust RTP timestamp\n" - ) -{ - g_cfg->trunk.force_aligned_timing = 1; - return CMD_SUCCESS; -} - -DEFUN(cfg_mgcp_no_patch_rtp_ts, - cfg_mgcp_no_patch_rtp_ts_cmd, - "no rtp-patch timestamp", - NO_STR RTP_PATCH_STR - "Adjust RTP timestamp\n" - ) -{ - g_cfg->trunk.force_aligned_timing = 0; - return CMD_SUCCESS; -} - -DEFUN(cfg_mgcp_no_patch_rtp, - cfg_mgcp_no_patch_rtp_cmd, - "no rtp-patch", - NO_STR RTP_PATCH_STR) -{ - g_cfg->trunk.force_constant_ssrc = 0; - g_cfg->trunk.force_aligned_timing = 0; - return CMD_SUCCESS; -} - -DEFUN(cfg_mgcp_rtp_keepalive, - cfg_mgcp_rtp_keepalive_cmd, - "rtp keep-alive <1-120>", - RTP_STR RTP_KEEPALIVE_STR - "Keep alive interval in secs\n" - ) -{ - mgcp_trunk_set_keepalive(&g_cfg->trunk, atoi(argv[0])); - return CMD_SUCCESS; -} - -DEFUN(cfg_mgcp_rtp_keepalive_once, - cfg_mgcp_rtp_keepalive_once_cmd, - "rtp keep-alive once", - RTP_STR RTP_KEEPALIVE_STR - "Send dummy packet only once after CRCX/MDCX\n" - ) -{ - mgcp_trunk_set_keepalive(&g_cfg->trunk, MGCP_KEEPALIVE_ONCE); - return CMD_SUCCESS; -} - -DEFUN(cfg_mgcp_no_rtp_keepalive, - cfg_mgcp_no_rtp_keepalive_cmd, - "no rtp keep-alive", - NO_STR RTP_STR RTP_KEEPALIVE_STR - ) -{ - mgcp_trunk_set_keepalive(&g_cfg->trunk, 0); - return CMD_SUCCESS; -} - - - -#define CALL_AGENT_STR "Callagent information\n" -DEFUN(cfg_mgcp_agent_addr, - cfg_mgcp_agent_addr_cmd, - "call-agent ip A.B.C.D", - CALL_AGENT_STR IP_STR - "IPv4 Address of the callagent\n") -{ - osmo_talloc_replace_string(g_cfg, &g_cfg->call_agent_addr, argv[0]); - return CMD_SUCCESS; -} - -ALIAS_DEPRECATED(cfg_mgcp_agent_addr, cfg_mgcp_agent_addr_cmd_old, - "call agent ip A.B.C.D", - CALL_AGENT_STR CALL_AGENT_STR IP_STR - "IPv4 Address of the callagent\n") - - -DEFUN(cfg_mgcp_transcoder, - cfg_mgcp_transcoder_cmd, - "transcoder-mgw A.B.C.D", - "Use a MGW to detranscoder RTP\n" - "The IP address of the MGW") -{ - osmo_talloc_replace_string(g_cfg, &g_cfg->transcoder_ip, argv[0]); - inet_aton(g_cfg->transcoder_ip, &g_cfg->transcoder_in); - - return CMD_SUCCESS; -} - -DEFUN(cfg_mgcp_no_transcoder, - cfg_mgcp_no_transcoder_cmd, - "no transcoder-mgw", - NO_STR "Disable the transcoding\n") -{ - if (g_cfg->transcoder_ip) { - LOGP(DMGCP, LOGL_NOTICE, "Disabling transcoding on future calls.\n"); - talloc_free(g_cfg->transcoder_ip); - g_cfg->transcoder_ip = NULL; - } - - return CMD_SUCCESS; -} - -DEFUN(cfg_mgcp_transcoder_remote_base, - cfg_mgcp_transcoder_remote_base_cmd, - "transcoder-remote-base <0-65534>", - "Set the base port for the transcoder\n" "The RTP base port on the transcoder") -{ - g_cfg->transcoder_remote_base = atoi(argv[0]); - return CMD_SUCCESS; -} - -DEFUN(cfg_mgcp_trunk, cfg_mgcp_trunk_cmd, - "trunk <1-64>", - "Configure a SS7 trunk\n" "Trunk Nr\n") -{ - struct mgcp_trunk_config *trunk; - int index = atoi(argv[0]); - - trunk = mgcp_trunk_num(g_cfg, index); - if (!trunk) - trunk = mgcp_trunk_alloc(g_cfg, index); - - if (!trunk) { - vty_out(vty, "%%Unable to allocate trunk %u.%s", - index, VTY_NEWLINE); - return CMD_WARNING; - } - - vty->node = TRUNK_NODE; - vty->index = trunk; - return CMD_SUCCESS; -} - -static int config_write_trunk(struct vty *vty) -{ - struct mgcp_trunk_config *trunk; - - llist_for_each_entry(trunk, &g_cfg->trunks, entry) { - vty_out(vty, " trunk %d%s", trunk->trunk_nr, VTY_NEWLINE); - vty_out(vty, " sdp audio-payload number %d%s", - trunk->audio_payload, VTY_NEWLINE); - vty_out(vty, " sdp audio-payload name %s%s", - trunk->audio_name, VTY_NEWLINE); - vty_out(vty, " %ssdp audio-payload send-ptime%s", - trunk->audio_send_ptime ? "" : "no ", VTY_NEWLINE); - vty_out(vty, " %ssdp audio-payload send-name%s", - trunk->audio_send_name ? "" : "no ", VTY_NEWLINE); - - if (trunk->keepalive_interval == MGCP_KEEPALIVE_ONCE) - vty_out(vty, " rtp keep-alive once%s", VTY_NEWLINE); - else if (trunk->keepalive_interval) - vty_out(vty, " rtp keep-alive %d%s", - trunk->keepalive_interval, VTY_NEWLINE); - else - vty_out(vty, " no rtp keep-alive%s", VTY_NEWLINE); - - vty_out(vty, " loop %d%s", - trunk->audio_loop, VTY_NEWLINE); - if (trunk->omit_rtcp) - vty_out(vty, " rtcp-omit%s", VTY_NEWLINE); - else - vty_out(vty, " no rtcp-omit%s", VTY_NEWLINE); - if (trunk->force_constant_ssrc || trunk->force_aligned_timing) { - vty_out(vty, " %srtp-patch ssrc%s", - trunk->force_constant_ssrc ? "" : "no ", VTY_NEWLINE); - vty_out(vty, " %srtp-patch timestamp%s", - trunk->force_aligned_timing ? "" : "no ", VTY_NEWLINE); - } else - vty_out(vty, " no rtp-patch%s", VTY_NEWLINE); - if (trunk->audio_fmtp_extra) - vty_out(vty, " sdp audio fmtp-extra %s%s", - trunk->audio_fmtp_extra, VTY_NEWLINE); - vty_out(vty, " %sallow-transcoding%s", - trunk->no_audio_transcoding ? "no " : "", VTY_NEWLINE); - } - - return CMD_SUCCESS; -} - -DEFUN(cfg_trunk_sdp_fmtp_extra, - cfg_trunk_sdp_fmtp_extra_cmd, - "sdp audio fmtp-extra .NAME", - "Add extra fmtp for the SDP file\n" "Audio\n" "Fmtp-extra\n" - "Extra Information\n") -{ - struct mgcp_trunk_config *trunk = vty->index; - char *txt = argv_concat(argv, argc, 0); - if (!txt) - return CMD_WARNING; - - osmo_talloc_replace_string(g_cfg, &trunk->audio_fmtp_extra, txt); - talloc_free(txt); - return CMD_SUCCESS; -} - -DEFUN(cfg_trunk_payload_number, - cfg_trunk_payload_number_cmd, - "sdp audio-payload number <0-255>", - SDP_STR AUDIO_STR "Number\n" "Payload Number\n") -{ - struct mgcp_trunk_config *trunk = vty->index; - unsigned int payload = atoi(argv[0]); - - trunk->audio_payload = payload; - return CMD_SUCCESS; -} - -ALIAS_DEPRECATED(cfg_trunk_payload_number, cfg_trunk_payload_number_cmd_old, - "sdp audio payload number <0-255>", - SDP_STR AUDIO_STR AUDIO_STR "Number\n" "Payload Number\n") - -DEFUN(cfg_trunk_payload_name, - cfg_trunk_payload_name_cmd, - "sdp audio-payload name NAME", - SDP_STR AUDIO_STR "Payload\n" "Payload Name\n") -{ - struct mgcp_trunk_config *trunk = vty->index; - - osmo_talloc_replace_string(g_cfg, &trunk->audio_name, argv[0]); - return CMD_SUCCESS; -} - -ALIAS_DEPRECATED(cfg_trunk_payload_name, cfg_trunk_payload_name_cmd_old, - "sdp audio payload name NAME", - SDP_STR AUDIO_STR AUDIO_STR "Payload\n" "Payload Name\n") - - -DEFUN(cfg_trunk_loop, - cfg_trunk_loop_cmd, - "loop (0|1)", - "Loop audio for all endpoints on this trunk\n" - "Don't Loop\n" "Loop\n") -{ - struct mgcp_trunk_config *trunk = vty->index; - - if (g_cfg->osmux) { - vty_out(vty, "Cannot use `loop' with `osmux'.%s", VTY_NEWLINE); - return CMD_WARNING; - } - trunk->audio_loop = atoi(argv[0]); - return CMD_SUCCESS; -} - -DEFUN(cfg_trunk_sdp_payload_send_ptime, - cfg_trunk_sdp_payload_send_ptime_cmd, - "sdp audio-payload send-ptime", - SDP_STR AUDIO_STR - "Send SDP ptime (packet duration) attribute\n") -{ - struct mgcp_trunk_config *trunk = vty->index; - trunk->audio_send_ptime = 1; - return CMD_SUCCESS; -} - -DEFUN(cfg_trunk_no_sdp_payload_send_ptime, - cfg_trunk_no_sdp_payload_send_ptime_cmd, - "no sdp audio-payload send-ptime", - NO_STR SDP_STR AUDIO_STR - "Send SDP ptime (packet duration) attribute\n") -{ - struct mgcp_trunk_config *trunk = vty->index; - trunk->audio_send_ptime = 0; - return CMD_SUCCESS; -} - -DEFUN(cfg_trunk_sdp_payload_send_name, - cfg_trunk_sdp_payload_send_name_cmd, - "sdp audio-payload send-name", - SDP_STR AUDIO_STR - "Send SDP rtpmap with the audio name\n") -{ - struct mgcp_trunk_config *trunk = vty->index; - trunk->audio_send_name = 1; - return CMD_SUCCESS; -} - -DEFUN(cfg_trunk_no_sdp_payload_send_name, - cfg_trunk_no_sdp_payload_send_name_cmd, - "no sdp audio-payload send-name", - NO_STR SDP_STR AUDIO_STR - "Send SDP rtpmap with the audio name\n") -{ - struct mgcp_trunk_config *trunk = vty->index; - trunk->audio_send_name = 0; - return CMD_SUCCESS; -} - -DEFUN(cfg_trunk_omit_rtcp, - cfg_trunk_omit_rtcp_cmd, - "rtcp-omit", - RTCP_OMIT_STR) -{ - struct mgcp_trunk_config *trunk = vty->index; - trunk->omit_rtcp = 1; - return CMD_SUCCESS; -} - -DEFUN(cfg_trunk_no_omit_rtcp, - cfg_trunk_no_omit_rtcp_cmd, - "no rtcp-omit", - NO_STR RTCP_OMIT_STR) -{ - struct mgcp_trunk_config *trunk = vty->index; - trunk->omit_rtcp = 0; - return CMD_SUCCESS; -} - -DEFUN(cfg_trunk_patch_rtp_ssrc, - cfg_trunk_patch_rtp_ssrc_cmd, - "rtp-patch ssrc", - RTP_PATCH_STR - "Force a fixed SSRC\n" - ) -{ - struct mgcp_trunk_config *trunk = vty->index; - trunk->force_constant_ssrc = 1; - return CMD_SUCCESS; -} - -DEFUN(cfg_trunk_no_patch_rtp_ssrc, - cfg_trunk_no_patch_rtp_ssrc_cmd, - "no rtp-patch ssrc", - NO_STR RTP_PATCH_STR - "Force a fixed SSRC\n" - ) -{ - struct mgcp_trunk_config *trunk = vty->index; - trunk->force_constant_ssrc = 0; - return CMD_SUCCESS; -} - -DEFUN(cfg_trunk_patch_rtp_ts, - cfg_trunk_patch_rtp_ts_cmd, - "rtp-patch timestamp", - RTP_PATCH_STR - "Adjust RTP timestamp\n" - ) -{ - struct mgcp_trunk_config *trunk = vty->index; - trunk->force_aligned_timing = 1; - return CMD_SUCCESS; -} - -DEFUN(cfg_trunk_no_patch_rtp_ts, - cfg_trunk_no_patch_rtp_ts_cmd, - "no rtp-patch timestamp", - NO_STR RTP_PATCH_STR - "Adjust RTP timestamp\n" - ) -{ - struct mgcp_trunk_config *trunk = vty->index; - trunk->force_aligned_timing = 0; - return CMD_SUCCESS; -} - -DEFUN(cfg_trunk_no_patch_rtp, - cfg_trunk_no_patch_rtp_cmd, - "no rtp-patch", - NO_STR RTP_PATCH_STR) -{ - struct mgcp_trunk_config *trunk = vty->index; - trunk->force_constant_ssrc = 0; - trunk->force_aligned_timing = 0; - return CMD_SUCCESS; -} - -DEFUN(cfg_trunk_rtp_keepalive, - cfg_trunk_rtp_keepalive_cmd, - "rtp keep-alive <1-120>", - RTP_STR RTP_KEEPALIVE_STR - "Keep-alive interval in secs\n" - ) -{ - struct mgcp_trunk_config *trunk = vty->index; - mgcp_trunk_set_keepalive(trunk, atoi(argv[0])); - return CMD_SUCCESS; -} - -DEFUN(cfg_trunk_rtp_keepalive_once, - cfg_trunk_rtp_keepalive_once_cmd, - "rtp keep-alive once", - RTP_STR RTP_KEEPALIVE_STR - "Send dummy packet only once after CRCX/MDCX\n" - ) -{ - struct mgcp_trunk_config *trunk = vty->index; - mgcp_trunk_set_keepalive(trunk, MGCP_KEEPALIVE_ONCE); - return CMD_SUCCESS; -} - -DEFUN(cfg_trunk_no_rtp_keepalive, - cfg_trunk_no_rtp_keepalive_cmd, - "no rtp keep-alive", - NO_STR RTP_STR RTP_KEEPALIVE_STR - ) -{ - struct mgcp_trunk_config *trunk = vty->index; - mgcp_trunk_set_keepalive(trunk, 0); - return CMD_SUCCESS; -} - -DEFUN(cfg_trunk_allow_transcoding, - cfg_trunk_allow_transcoding_cmd, - "allow-transcoding", - "Allow transcoding\n") -{ - struct mgcp_trunk_config *trunk = vty->index; - trunk->no_audio_transcoding = 0; - return CMD_SUCCESS; -} - -DEFUN(cfg_trunk_no_allow_transcoding, - cfg_trunk_no_allow_transcoding_cmd, - "no allow-transcoding", - NO_STR "Allow transcoding\n") -{ - struct mgcp_trunk_config *trunk = vty->index; - trunk->no_audio_transcoding = 1; - return CMD_SUCCESS; -} - -DEFUN(loop_endp, - loop_endp_cmd, - "loop-endpoint <0-64> NAME (0|1)", - "Loop a given endpoint\n" "Trunk number\n" - "The name in hex of the endpoint\n" "Disable the loop\n" "Enable the loop\n") -{ - struct mgcp_trunk_config *trunk; - struct mgcp_endpoint *endp; - - trunk = find_trunk(g_cfg, atoi(argv[0])); - if (!trunk) { - vty_out(vty, "%%Trunk %d not found in the config.%s", - atoi(argv[0]), VTY_NEWLINE); - return CMD_WARNING; - } - - if (!trunk->endpoints) { - vty_out(vty, "%%Trunk %d has no endpoints allocated.%s", - trunk->trunk_nr, VTY_NEWLINE); - return CMD_WARNING; - } - - int endp_no = strtoul(argv[1], NULL, 16); - if (endp_no < 1 || endp_no >= trunk->number_endpoints) { - vty_out(vty, "Loopback number %s/%d is invalid.%s", - argv[1], endp_no, VTY_NEWLINE); - return CMD_WARNING; - } - - - endp = &trunk->endpoints[endp_no]; - int loop = atoi(argv[2]); - - if (loop) - endp->conn_mode = MGCP_CONN_LOOPBACK; - else - endp->conn_mode = endp->orig_mode; - - /* Handle it like a MDCX, switch on SSRC patching if enabled */ - mgcp_rtp_end_config(endp, 1, &endp->bts_end); - mgcp_rtp_end_config(endp, 1, &endp->net_end); - - return CMD_SUCCESS; -} - -DEFUN(tap_call, - tap_call_cmd, - "tap-call <0-64> ENDPOINT (bts-in|bts-out|net-in|net-out) A.B.C.D <0-65534>", - "Forward data on endpoint to a different system\n" "Trunk number\n" - "The endpoint in hex\n" - "Forward the data coming from the bts\n" - "Forward the data coming from the bts leaving to the network\n" - "Forward the data coming from the net\n" - "Forward the data coming from the net leaving to the bts\n" - "destination IP of the data\n" "destination port\n") -{ - struct mgcp_rtp_tap *tap; - struct mgcp_trunk_config *trunk; - struct mgcp_endpoint *endp; - int port = 0; - - trunk = find_trunk(g_cfg, atoi(argv[0])); - if (!trunk) { - vty_out(vty, "%%Trunk %d not found in the config.%s", - atoi(argv[0]), VTY_NEWLINE); - return CMD_WARNING; - } - - if (!trunk->endpoints) { - vty_out(vty, "%%Trunk %d has no endpoints allocated.%s", - trunk->trunk_nr, VTY_NEWLINE); - return CMD_WARNING; - } - - int endp_no = strtoul(argv[1], NULL, 16); - if (endp_no < 1 || endp_no >= trunk->number_endpoints) { - vty_out(vty, "Endpoint number %s/%d is invalid.%s", - argv[1], endp_no, VTY_NEWLINE); - return CMD_WARNING; - } - - endp = &trunk->endpoints[endp_no]; - - if (strcmp(argv[2], "bts-in") == 0) { - port = MGCP_TAP_BTS_IN; - } else if (strcmp(argv[2], "bts-out") == 0) { - port = MGCP_TAP_BTS_OUT; - } else if (strcmp(argv[2], "net-in") == 0) { - port = MGCP_TAP_NET_IN; - } else if (strcmp(argv[2], "net-out") == 0) { - port = MGCP_TAP_NET_OUT; - } else { - vty_out(vty, "Unknown mode... tricked vty?%s", VTY_NEWLINE); - return CMD_WARNING; - } - - tap = &endp->taps[port]; - memset(&tap->forward, 0, sizeof(tap->forward)); - inet_aton(argv[3], &tap->forward.sin_addr); - tap->forward.sin_port = htons(atoi(argv[4])); - tap->enabled = 1; - return CMD_SUCCESS; -} - -DEFUN(free_endp, free_endp_cmd, - "free-endpoint <0-64> NUMBER", - "Free the given endpoint\n" "Trunk number\n" - "Endpoint number in hex.\n") -{ - struct mgcp_trunk_config *trunk; - struct mgcp_endpoint *endp; - - trunk = find_trunk(g_cfg, atoi(argv[0])); - if (!trunk) { - vty_out(vty, "%%Trunk %d not found in the config.%s", - atoi(argv[0]), VTY_NEWLINE); - return CMD_WARNING; - } - - if (!trunk->endpoints) { - vty_out(vty, "%%Trunk %d has no endpoints allocated.%s", - trunk->trunk_nr, VTY_NEWLINE); - return CMD_WARNING; - } - - int endp_no = strtoul(argv[1], NULL, 16); - if (endp_no < 1 || endp_no >= trunk->number_endpoints) { - vty_out(vty, "Endpoint number %s/%d is invalid.%s", - argv[1], endp_no, VTY_NEWLINE); - return CMD_WARNING; - } - - endp = &trunk->endpoints[endp_no]; - mgcp_release_endp(endp); - return CMD_SUCCESS; -} - -DEFUN(reset_endp, reset_endp_cmd, - "reset-endpoint <0-64> NUMBER", - "Reset the given endpoint\n" "Trunk number\n" - "Endpoint number in hex.\n") -{ - struct mgcp_trunk_config *trunk; - struct mgcp_endpoint *endp; - int endp_no, rc; - - trunk = find_trunk(g_cfg, atoi(argv[0])); - if (!trunk) { - vty_out(vty, "%%Trunk %d not found in the config.%s", - atoi(argv[0]), VTY_NEWLINE); - return CMD_WARNING; - } - - if (!trunk->endpoints) { - vty_out(vty, "%%Trunk %d has no endpoints allocated.%s", - trunk->trunk_nr, VTY_NEWLINE); - return CMD_WARNING; - } - - endp_no = strtoul(argv[1], NULL, 16); - if (endp_no < 1 || endp_no >= trunk->number_endpoints) { - vty_out(vty, "Endpoint number %s/%d is invalid.%s", - argv[1], endp_no, VTY_NEWLINE); - return CMD_WARNING; - } - - endp = &trunk->endpoints[endp_no]; - rc = mgcp_send_reset_ep(endp, ENDPOINT_NUMBER(endp)); - if (rc < 0) { - vty_out(vty, "Error %d sending reset.%s", rc, VTY_NEWLINE); - return CMD_WARNING; - } - return CMD_SUCCESS; -} - -DEFUN(reset_all_endp, reset_all_endp_cmd, - "reset-all-endpoints", - "Reset all endpoints\n") -{ - int rc; - - rc = mgcp_send_reset_all(g_cfg); - if (rc < 0) { - vty_out(vty, "Error %d during endpoint reset.%s", - rc, VTY_NEWLINE); - return CMD_WARNING; - } - return CMD_SUCCESS; -} - -#define OSMUX_STR "RTP multiplexing\n" -DEFUN(cfg_mgcp_osmux, - cfg_mgcp_osmux_cmd, - "osmux (on|off|only)", - OSMUX_STR "Enable OSMUX\n" "Disable OSMUX\n" "Only use OSMUX\n") -{ - if (strcmp(argv[0], "off") == 0) { - g_cfg->osmux = OSMUX_USAGE_OFF; - return CMD_SUCCESS; - } - - if (strcmp(argv[0], "on") == 0) - g_cfg->osmux = OSMUX_USAGE_ON; - else if (strcmp(argv[0], "only") == 0) - g_cfg->osmux = OSMUX_USAGE_ONLY; - - if (g_cfg->trunk.audio_loop) { - vty_out(vty, "Cannot use `loop' with `osmux'.%s", - VTY_NEWLINE); - return CMD_WARNING; - } - - return CMD_SUCCESS; -} - -DEFUN(cfg_mgcp_osmux_ip, - cfg_mgcp_osmux_ip_cmd, - "osmux bind-ip A.B.C.D", - OSMUX_STR IP_STR "IPv4 Address to bind to\n") -{ - osmo_talloc_replace_string(g_cfg, &g_cfg->osmux_addr, argv[0]); - return CMD_SUCCESS; -} - -DEFUN(cfg_mgcp_osmux_batch_factor, - cfg_mgcp_osmux_batch_factor_cmd, - "osmux batch-factor <1-8>", - OSMUX_STR "Batching factor\n" "Number of messages in the batch\n") -{ - g_cfg->osmux_batch = atoi(argv[0]); - return CMD_SUCCESS; -} - -DEFUN(cfg_mgcp_osmux_batch_size, - cfg_mgcp_osmux_batch_size_cmd, - "osmux batch-size <1-65535>", - OSMUX_STR "batch size\n" "Batch size in bytes\n") -{ - g_cfg->osmux_batch_size = atoi(argv[0]); - return CMD_SUCCESS; -} - -DEFUN(cfg_mgcp_osmux_port, - cfg_mgcp_osmux_port_cmd, - "osmux port <1-65535>", - OSMUX_STR "port\n" "UDP port\n") -{ - g_cfg->osmux_port = atoi(argv[0]); - return CMD_SUCCESS; -} - -DEFUN(cfg_mgcp_osmux_dummy, - cfg_mgcp_osmux_dummy_cmd, - "osmux dummy (on|off)", - OSMUX_STR "Dummy padding\n" "Enable dummy padding\n" "Disable dummy padding\n") -{ - if (strcmp(argv[0], "on") == 0) - g_cfg->osmux_dummy = 1; - else if (strcmp(argv[0], "off") == 0) - g_cfg->osmux_dummy = 0; - - return CMD_SUCCESS; -} - -int mgcp_vty_init(void) -{ - install_element_ve(&show_mgcp_cmd); - install_element(ENABLE_NODE, &loop_endp_cmd); - install_element(ENABLE_NODE, &tap_call_cmd); - install_element(ENABLE_NODE, &free_endp_cmd); - install_element(ENABLE_NODE, &reset_endp_cmd); - install_element(ENABLE_NODE, &reset_all_endp_cmd); - - install_element(CONFIG_NODE, &cfg_mgcp_cmd); - install_node(&mgcp_node, config_write_mgcp); - - vty_install_default(MGCP_NODE); - install_element(MGCP_NODE, &cfg_mgcp_local_ip_cmd); - install_element(MGCP_NODE, &cfg_mgcp_bts_ip_cmd); - install_element(MGCP_NODE, &cfg_mgcp_bind_ip_cmd); - install_element(MGCP_NODE, &cfg_mgcp_bind_port_cmd); - install_element(MGCP_NODE, &cfg_mgcp_bind_early_cmd); - install_element(MGCP_NODE, &cfg_mgcp_rtp_base_port_cmd); - install_element(MGCP_NODE, &cfg_mgcp_rtp_bts_base_port_cmd); - install_element(MGCP_NODE, &cfg_mgcp_rtp_net_base_port_cmd); - install_element(MGCP_NODE, &cfg_mgcp_rtp_bts_range_cmd); - install_element(MGCP_NODE, &cfg_mgcp_rtp_bts_bind_ip_cmd); - install_element(MGCP_NODE, &cfg_mgcp_rtp_no_bts_bind_ip_cmd); - install_element(MGCP_NODE, &cfg_mgcp_rtp_net_range_cmd); - install_element(MGCP_NODE, &cfg_mgcp_rtp_net_bind_ip_cmd); - install_element(MGCP_NODE, &cfg_mgcp_rtp_no_net_bind_ip_cmd); - install_element(MGCP_NODE, &cfg_mgcp_rtp_transcoder_range_cmd); - install_element(MGCP_NODE, &cfg_mgcp_rtp_transcoder_base_cmd); - install_element(MGCP_NODE, &cfg_mgcp_rtp_ip_dscp_cmd); - install_element(MGCP_NODE, &cfg_mgcp_rtp_ip_tos_cmd); - install_element(MGCP_NODE, &cfg_mgcp_rtp_force_ptime_cmd); - install_element(MGCP_NODE, &cfg_mgcp_no_rtp_force_ptime_cmd); - install_element(MGCP_NODE, &cfg_mgcp_rtp_keepalive_cmd); - install_element(MGCP_NODE, &cfg_mgcp_rtp_keepalive_once_cmd); - install_element(MGCP_NODE, &cfg_mgcp_no_rtp_keepalive_cmd); - install_element(MGCP_NODE, &cfg_mgcp_agent_addr_cmd); - install_element(MGCP_NODE, &cfg_mgcp_agent_addr_cmd_old); - install_element(MGCP_NODE, &cfg_mgcp_transcoder_cmd); - install_element(MGCP_NODE, &cfg_mgcp_no_transcoder_cmd); - install_element(MGCP_NODE, &cfg_mgcp_transcoder_remote_base_cmd); - install_element(MGCP_NODE, &cfg_mgcp_sdp_payload_number_cmd); - install_element(MGCP_NODE, &cfg_mgcp_sdp_payload_name_cmd); - install_element(MGCP_NODE, &cfg_mgcp_sdp_payload_number_cmd_old); - install_element(MGCP_NODE, &cfg_mgcp_sdp_payload_name_cmd_old); - install_element(MGCP_NODE, &cfg_mgcp_loop_cmd); - install_element(MGCP_NODE, &cfg_mgcp_number_endp_cmd); - install_element(MGCP_NODE, &cfg_mgcp_omit_rtcp_cmd); - install_element(MGCP_NODE, &cfg_mgcp_no_omit_rtcp_cmd); - install_element(MGCP_NODE, &cfg_mgcp_patch_rtp_ssrc_cmd); - install_element(MGCP_NODE, &cfg_mgcp_no_patch_rtp_ssrc_cmd); - install_element(MGCP_NODE, &cfg_mgcp_patch_rtp_ts_cmd); - install_element(MGCP_NODE, &cfg_mgcp_no_patch_rtp_ts_cmd); - install_element(MGCP_NODE, &cfg_mgcp_no_patch_rtp_cmd); - install_element(MGCP_NODE, &cfg_mgcp_sdp_fmtp_extra_cmd); - install_element(MGCP_NODE, &cfg_mgcp_sdp_payload_send_ptime_cmd); - install_element(MGCP_NODE, &cfg_mgcp_no_sdp_payload_send_ptime_cmd); - install_element(MGCP_NODE, &cfg_mgcp_sdp_payload_send_name_cmd); - install_element(MGCP_NODE, &cfg_mgcp_no_sdp_payload_send_name_cmd); - install_element(MGCP_NODE, &cfg_mgcp_osmux_cmd); - install_element(MGCP_NODE, &cfg_mgcp_osmux_ip_cmd); - install_element(MGCP_NODE, &cfg_mgcp_osmux_batch_factor_cmd); - install_element(MGCP_NODE, &cfg_mgcp_osmux_batch_size_cmd); - install_element(MGCP_NODE, &cfg_mgcp_osmux_port_cmd); - install_element(MGCP_NODE, &cfg_mgcp_osmux_dummy_cmd); - install_element(MGCP_NODE, &cfg_mgcp_allow_transcoding_cmd); - install_element(MGCP_NODE, &cfg_mgcp_no_allow_transcoding_cmd); - - - install_element(MGCP_NODE, &cfg_mgcp_trunk_cmd); - install_node(&trunk_node, config_write_trunk); - vty_install_default(TRUNK_NODE); - install_element(TRUNK_NODE, &cfg_trunk_rtp_keepalive_cmd); - install_element(TRUNK_NODE, &cfg_trunk_rtp_keepalive_once_cmd); - install_element(TRUNK_NODE, &cfg_trunk_no_rtp_keepalive_cmd); - install_element(TRUNK_NODE, &cfg_trunk_payload_number_cmd); - install_element(TRUNK_NODE, &cfg_trunk_payload_name_cmd); - install_element(TRUNK_NODE, &cfg_trunk_payload_number_cmd_old); - install_element(TRUNK_NODE, &cfg_trunk_payload_name_cmd_old); - install_element(TRUNK_NODE, &cfg_trunk_loop_cmd); - install_element(TRUNK_NODE, &cfg_trunk_omit_rtcp_cmd); - install_element(TRUNK_NODE, &cfg_trunk_no_omit_rtcp_cmd); - install_element(TRUNK_NODE, &cfg_trunk_patch_rtp_ssrc_cmd); - install_element(TRUNK_NODE, &cfg_trunk_no_patch_rtp_ssrc_cmd); - install_element(TRUNK_NODE, &cfg_trunk_patch_rtp_ts_cmd); - install_element(TRUNK_NODE, &cfg_trunk_no_patch_rtp_ts_cmd); - install_element(TRUNK_NODE, &cfg_trunk_no_patch_rtp_cmd); - install_element(TRUNK_NODE, &cfg_trunk_sdp_fmtp_extra_cmd); - install_element(TRUNK_NODE, &cfg_trunk_sdp_payload_send_ptime_cmd); - install_element(TRUNK_NODE, &cfg_trunk_no_sdp_payload_send_ptime_cmd); - install_element(TRUNK_NODE, &cfg_trunk_sdp_payload_send_name_cmd); - install_element(TRUNK_NODE, &cfg_trunk_no_sdp_payload_send_name_cmd); - install_element(TRUNK_NODE, &cfg_trunk_allow_transcoding_cmd); - install_element(TRUNK_NODE, &cfg_trunk_no_allow_transcoding_cmd); - - return 0; -} - -static int allocate_trunk(struct mgcp_trunk_config *trunk) -{ - int i; - struct mgcp_config *cfg = trunk->cfg; - - if (mgcp_endpoints_allocate(trunk) != 0) { - LOGP(DMGCP, LOGL_ERROR, - "Failed to allocate %d endpoints on trunk %d.\n", - trunk->number_endpoints, trunk->trunk_nr); - return -1; - } - - /* early bind */ - for (i = 1; i < trunk->number_endpoints; ++i) { - struct mgcp_endpoint *endp = &trunk->endpoints[i]; - - if (cfg->bts_ports.mode == PORT_ALLOC_STATIC) { - cfg->last_bts_port += 2; - if (mgcp_bind_bts_rtp_port(endp, cfg->last_bts_port) != 0) { - LOGP(DMGCP, LOGL_FATAL, - "Failed to bind: %d\n", cfg->last_bts_port); - return -1; - } - endp->bts_end.local_alloc = PORT_ALLOC_STATIC; - } - - if (cfg->net_ports.mode == PORT_ALLOC_STATIC) { - cfg->last_net_port += 2; - if (mgcp_bind_net_rtp_port(endp, cfg->last_net_port) != 0) { - LOGP(DMGCP, LOGL_FATAL, - "Failed to bind: %d\n", cfg->last_net_port); - return -1; - } - endp->net_end.local_alloc = PORT_ALLOC_STATIC; - } - - if (trunk->trunk_type == MGCP_TRUNK_VIRTUAL && - cfg->transcoder_ip && cfg->transcoder_ports.mode == PORT_ALLOC_STATIC) { - int rtp_port; - - /* network side */ - rtp_port = rtp_calculate_port(ENDPOINT_NUMBER(endp), - cfg->transcoder_ports.base_port); - if (mgcp_bind_trans_net_rtp_port(endp, rtp_port) != 0) { - LOGP(DMGCP, LOGL_FATAL, "Failed to bind: %d\n", rtp_port); - return -1; - } - endp->trans_net.local_alloc = PORT_ALLOC_STATIC; - - /* bts side */ - rtp_port = rtp_calculate_port(endp_back_channel(ENDPOINT_NUMBER(endp)), - cfg->transcoder_ports.base_port); - if (mgcp_bind_trans_bts_rtp_port(endp, rtp_port) != 0) { - LOGP(DMGCP, LOGL_FATAL, "Failed to bind: %d\n", rtp_port); - return -1; - } - endp->trans_bts.local_alloc = PORT_ALLOC_STATIC; - } - } - - return 0; -} - -int mgcp_parse_config(const char *config_file, struct mgcp_config *cfg, - enum mgcp_role role) -{ - int rc; - struct mgcp_trunk_config *trunk; - - cfg->osmux_port = OSMUX_PORT; - cfg->osmux_batch = 4; - cfg->osmux_batch_size = OSMUX_BATCH_DEFAULT_MAX; - - g_cfg = cfg; - rc = vty_read_config_file(config_file, NULL); - if (rc < 0) { - fprintf(stderr, "Failed to parse the config file: '%s'\n", config_file); - return rc; - } - - - if (!g_cfg->bts_ip) - fprintf(stderr, "No BTS ip address specified. This will allow everyone to connect.\n"); - - if (!g_cfg->source_addr) { - fprintf(stderr, "You need to specify a bind address.\n"); - return -1; - } - - /* initialize the last ports */ - g_cfg->last_bts_port = rtp_calculate_port(0, g_cfg->bts_ports.base_port); - g_cfg->last_net_port = rtp_calculate_port(0, g_cfg->net_ports.base_port); - - if (allocate_trunk(&g_cfg->trunk) != 0) { - LOGP(DMGCP, LOGL_ERROR, "Failed to initialize the virtual trunk.\n"); - return -1; - } - - llist_for_each_entry(trunk, &g_cfg->trunks, entry) { - if (allocate_trunk(trunk) != 0) { - LOGP(DMGCP, LOGL_ERROR, - "Failed to initialize E1 trunk %d.\n", trunk->trunk_nr); - return -1; - } - } - cfg->role = role; - - return 0; -} - diff --git a/openbsc/src/libmsc/Makefile.am b/openbsc/src/libmsc/Makefile.am deleted file mode 100644 index 9d966dbc..00000000 --- a/openbsc/src/libmsc/Makefile.am +++ /dev/null @@ -1,58 +0,0 @@ -AM_CPPFLAGS = \ - $(all_includes) \ - -I$(top_srcdir)/include \ - -I$(top_builddir) \ - $(NULL) - -AM_CFLAGS = \ - -Wall \ - $(LIBOSMOCORE_CFLAGS) \ - $(LIBOSMOVTY_CFLAGS) \ - $(LIBOSMOABIS_CFLAGS) \ - $(COVERAGE_CFLAGS) \ - $(LIBCRYPTO_CFLAGS) \ - $(LIBSMPP34_CFLAGS) \ - $(NULL) - -noinst_HEADERS = \ - meas_feed.h \ - $(NULL) - -noinst_LIBRARIES = \ - libmsc.a \ - $(NULL) - -libmsc_a_SOURCES = \ - auth.c \ - db.c \ - gsm_04_08.c \ - gsm_04_11.c \ - gsm_04_80.c \ - gsm_subscriber.c \ - mncc.c \ - mncc_builtin.c \ - mncc_sock.c \ - rrlp.c \ - silent_call.c \ - sms_queue.c \ - token_auth.c \ - ussd.c \ - vty_interface_layer3.c \ - transaction.c \ - osmo_msc.c \ - ctrl_commands.c \ - meas_feed.c \ - $(NULL) - -if BUILD_SMPP -noinst_HEADERS += \ - smpp_smsc.h \ - $(NULL) - -libmsc_a_SOURCES += \ - smpp_smsc.c \ - smpp_openbsc.c \ - smpp_vty.c \ - smpp_utils.c \ - $(NULL) -endif diff --git a/openbsc/src/libmsc/auth.c b/openbsc/src/libmsc/auth.c deleted file mode 100644 index 19def1ec..00000000 --- a/openbsc/src/libmsc/auth.c +++ /dev/null @@ -1,157 +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 - -#include - -#include - -const struct value_string auth_action_names[] = { - OSMO_VALUE_STRING(AUTH_ERROR), - OSMO_VALUE_STRING(AUTH_NOT_AVAIL), - OSMO_VALUE_STRING(AUTH_DO_AUTH_THEN_CIPH), - OSMO_VALUE_STRING(AUTH_DO_CIPH), - OSMO_VALUE_STRING(AUTH_DO_AUTH), - { 0, NULL } -}; - -static int -_use_xor(struct gsm_auth_info *ainfo, struct gsm_auth_tuple *atuple) -{ - int i, l = ainfo->a3a8_ki_len; - - if ((l > A38_XOR_MAX_KEY_LEN) || (l < A38_XOR_MIN_KEY_LEN)) { - LOGP(DMM, LOGL_ERROR, "Invalid XOR key (len=%d) %s\n", - ainfo->a3a8_ki_len, - osmo_hexdump(ainfo->a3a8_ki, ainfo->a3a8_ki_len)); - return -1; - } - - for (i=0; i<4; i++) - atuple->vec.sres[i] = atuple->vec.rand[i] ^ ainfo->a3a8_ki[i]; - for (i=4; i<12; i++) - atuple->vec.kc[i-4] = atuple->vec.rand[i] ^ ainfo->a3a8_ki[i]; - - return 0; -} - -static int -_use_comp128_v1(struct gsm_auth_info *ainfo, struct gsm_auth_tuple *atuple) -{ - if (ainfo->a3a8_ki_len != A38_COMP128_KEY_LEN) { - LOGP(DMM, LOGL_ERROR, "Invalid COMP128v1 key (len=%d) %s\n", - ainfo->a3a8_ki_len, - osmo_hexdump(ainfo->a3a8_ki, ainfo->a3a8_ki_len)); - return -1; - } - - comp128(ainfo->a3a8_ki, atuple->vec.rand, atuple->vec.sres, atuple->vec.kc); - - return 0; -} - -/* Return values - * -1 -> Internal error - * 0 -> Not available - * 1 -> Tuple returned, need to do auth, then enable cipher - * 2 -> Tuple returned, need to enable cipher - */ -int auth_get_tuple_for_subscr(struct gsm_auth_tuple *atuple, - struct gsm_subscriber *subscr, int key_seq) -{ - struct gsm_auth_info ainfo; - int rc; - - /* Get subscriber info (if any) */ - rc = db_get_authinfo_for_subscr(&ainfo, subscr); - if (rc < 0) { - LOGP(DMM, LOGL_NOTICE, - "No retrievable Ki for subscriber %s, skipping auth\n", - subscr_name(subscr)); - return rc == -ENOENT ? AUTH_NOT_AVAIL : AUTH_ERROR; - } - - /* If possible, re-use the last tuple and skip auth */ - rc = db_get_lastauthtuple_for_subscr(atuple, subscr); - if ((rc == 0) && - (key_seq != GSM_KEY_SEQ_INVAL) && - (key_seq == atuple->key_seq) && - (atuple->use_count < 3)) - { - atuple->use_count++; - db_sync_lastauthtuple_for_subscr(atuple, subscr); - DEBUGP(DMM, "Auth tuple use < 3, just doing ciphering\n"); - return AUTH_DO_CIPH; - } - - /* Generate a new one */ - if (rc != 0) { - /* If db_get_lastauthtuple_for_subscr() returned nothing, make - * sure the atuple memory is initialized to zero and thus start - * off with key_seq = 0. */ - memset(atuple, 0, sizeof(*atuple)); - } else { - /* If db_get_lastauthtuple_for_subscr() returned a previous - * tuple, use the next key_seq. */ - atuple->key_seq = (atuple->key_seq + 1) % 7; - } - atuple->use_count = 1; - - if (RAND_bytes(atuple->vec.rand, sizeof(atuple->vec.rand)) != 1) { - LOGP(DMM, LOGL_NOTICE, "RAND_bytes failed, can't generate new auth tuple\n"); - return AUTH_ERROR; - } - - switch (ainfo.auth_algo) { - case AUTH_ALGO_NONE: - DEBUGP(DMM, "No authentication for subscriber\n"); - return AUTH_NOT_AVAIL; - - case AUTH_ALGO_XOR: - if (_use_xor(&ainfo, atuple)) - return AUTH_NOT_AVAIL; - break; - - case AUTH_ALGO_COMP128v1: - if (_use_comp128_v1(&ainfo, atuple)) - return AUTH_NOT_AVAIL; - break; - - default: - DEBUGP(DMM, "Unsupported auth type algo_id=%d\n", - ainfo.auth_algo); - return AUTH_NOT_AVAIL; - } - - db_sync_lastauthtuple_for_subscr(atuple, subscr); - - DEBUGP(DMM, "Need to do authentication and ciphering\n"); - return AUTH_DO_AUTH_THEN_CIPH; -} - diff --git a/openbsc/src/libmsc/ctrl_commands.c b/openbsc/src/libmsc/ctrl_commands.c deleted file mode 100644 index c99dde44..00000000 --- a/openbsc/src/libmsc/ctrl_commands.c +++ /dev/null @@ -1,212 +0,0 @@ -/* - * (C) 2014 by Holger Hans Peter Freyther - * (C) 2014 by sysmocom s.f.m.c. GmbH - * - * All Rights Reserved - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation; either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - * - */ - -#include -#include -#include -#include -#include -#include - -#include - -static bool alg_supported(const char *alg) -{ - /* - * TODO: share this with the vty_interface and extend to all - * algorithms supported by libosmocore now. Make it table based - * as well. - */ - if (strcasecmp(alg, "none") == 0) - return true; - if (strcasecmp(alg, "xor") == 0) - return true; - if (strcasecmp(alg, "comp128v1") == 0) - return true; - return false; -} - -static int verify_subscriber_modify(struct ctrl_cmd *cmd, const char *value, void *d) -{ - char *tmp, *imsi, *msisdn, *alg, *ki, *saveptr = NULL; - int rc = 0; - - tmp = talloc_strdup(cmd, value); - if (!tmp) - return 1; - - imsi = strtok_r(tmp, ",", &saveptr); - msisdn = strtok_r(NULL, ",", &saveptr); - alg = strtok_r(NULL, ",", &saveptr); - ki = strtok_r(NULL, ",", &saveptr); - - if (!imsi || !msisdn) - rc = 1; - else if (strlen(imsi) > GSM23003_IMSI_MAX_DIGITS) - rc = 1; - else if (strlen(msisdn) >= GSM_EXTENSION_LENGTH) - rc = 1; - else if (alg) { - if (!alg_supported(alg)) - rc = 1; - else if (strcasecmp(alg, "none") != 0 && !ki) - rc = 1; - } - - talloc_free(tmp); - return rc; -} - -static int set_subscriber_modify(struct ctrl_cmd *cmd, void *data) -{ - struct gsm_network *net = cmd->node; - char *tmp, *imsi, *msisdn, *alg, *ki, *saveptr = NULL; - struct gsm_subscriber* subscr; - int rc; - - tmp = talloc_strdup(cmd, cmd->value); - if (!tmp) - return 1; - - imsi = strtok_r(tmp, ",", &saveptr); - msisdn = strtok_r(NULL, ",", &saveptr); - alg = strtok_r(NULL, ",", &saveptr); - ki = strtok_r(NULL, ",", &saveptr); - - subscr = subscr_get_by_imsi(net->subscr_group, imsi); - if (!subscr) - subscr = subscr_create_subscriber(net->subscr_group, imsi); - if (!subscr) - goto fail; - - subscr->authorized = 1; - osmo_strlcpy(subscr->extension, msisdn, sizeof(subscr->extension)); - - /* put it back to the db */ - rc = db_sync_subscriber(subscr); - db_subscriber_update(subscr); - - /* handle optional ciphering */ - if (alg) { - if (strcasecmp(alg, "none") == 0) - db_sync_authinfo_for_subscr(NULL, subscr); - else { - struct gsm_auth_info ainfo = { 0, }; - /* the verify should make sure that this is okay */ - OSMO_ASSERT(alg); - OSMO_ASSERT(ki); - - if (strcasecmp(alg, "xor") == 0) - ainfo.auth_algo = AUTH_ALGO_XOR; - else if (strcasecmp(alg, "comp128v1") == 0) - ainfo.auth_algo = AUTH_ALGO_COMP128v1; - - rc = osmo_hexparse(ki, ainfo.a3a8_ki, sizeof(ainfo.a3a8_ki)); - if (rc < 0) { - subscr_put(subscr); - talloc_free(tmp); - cmd->reply = "Failed to parse KI"; - return CTRL_CMD_ERROR; - } - - ainfo.a3a8_ki_len = rc; - db_sync_authinfo_for_subscr(&ainfo, subscr); - rc = 0; - } - db_sync_lastauthtuple_for_subscr(NULL, subscr); - } - subscr_put(subscr); - - talloc_free(tmp); - - if (rc != 0) { - cmd->reply = "Failed to store the record in the DB"; - return CTRL_CMD_ERROR; - } - - cmd->reply = "OK"; - return CTRL_CMD_REPLY; - -fail: - talloc_free(tmp); - cmd->reply = "Failed to create subscriber"; - return CTRL_CMD_ERROR; -} - -CTRL_CMD_DEFINE_WO(subscriber_modify, "subscriber-modify-v1"); - -static int set_subscriber_delete(struct ctrl_cmd *cmd, void *data) -{ - int was_used = 0; - int rc; - struct gsm_subscriber *subscr; - struct gsm_network *net = cmd->node; - - subscr = subscr_get_by_imsi(net->subscr_group, cmd->value); - if (!subscr) { - cmd->reply = "Failed to find subscriber"; - return CTRL_CMD_ERROR; - } - - if (subscr->use_count != 1) { - LOGP(DCTRL, LOGL_NOTICE, "Going to remove active subscriber.\n"); - was_used = 1; - } - - rc = db_subscriber_delete(subscr); - subscr_put(subscr); - - if (rc != 0) { - cmd->reply = "Failed to remove subscriber"; - return CTRL_CMD_ERROR; - } - - cmd->reply = was_used ? "Removed active subscriber" : "Removed"; - return CTRL_CMD_REPLY; -} -CTRL_CMD_DEFINE_WO_NOVRF(subscriber_delete, "subscriber-delete-v1"); - -static void list_cb(struct gsm_subscriber *subscr, void *d) -{ - char **data = (char **) d; - *data = talloc_asprintf_append(*data, "%s,%s\n", - subscr->imsi, subscr->extension); -} - -static int get_subscriber_list(struct ctrl_cmd *cmd, void *d) -{ - cmd->reply = talloc_strdup(cmd, ""); - - db_subscriber_list_active(list_cb, &cmd->reply); - printf("%s\n", cmd->reply); - return CTRL_CMD_REPLY; -} -CTRL_CMD_DEFINE_RO(subscriber_list, "subscriber-list-active-v1"); - -int msc_ctrl_cmds_install(void) -{ - int rc = 0; - - rc |= ctrl_cmd_install(CTRL_NODE_ROOT, &cmd_subscriber_modify); - rc |= ctrl_cmd_install(CTRL_NODE_ROOT, &cmd_subscriber_delete); - rc |= ctrl_cmd_install(CTRL_NODE_ROOT, &cmd_subscriber_list); - return rc; -} diff --git a/openbsc/src/libmsc/db.c b/openbsc/src/libmsc/db.c deleted file mode 100644 index 5fe2a3c6..00000000 --- a/openbsc/src/libmsc/db.c +++ /dev/null @@ -1,1752 +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 -#include -#include -#include - -#include - -/* Semi-Private-Interface (SPI) for the subscriber code */ -void subscr_direct_free(struct gsm_subscriber *subscr); - -static char *db_basename = NULL; -static char *db_dirname = NULL; -static dbi_conn conn; - -#define SCHEMA_REVISION "4" - -enum { - SCHEMA_META, - INSERT_META, - SCHEMA_SUBSCRIBER, - SCHEMA_AUTH, - SCHEMA_EQUIPMENT, - SCHEMA_EQUIPMENT_WATCH, - SCHEMA_SMS, - SCHEMA_VLR, - SCHEMA_APDU, - SCHEMA_COUNTERS, - SCHEMA_RATE, - SCHEMA_AUTHKEY, - SCHEMA_AUTHLAST, -}; - -static const char *create_stmts[] = { - [SCHEMA_META] = "CREATE TABLE IF NOT EXISTS Meta (" - "id INTEGER PRIMARY KEY AUTOINCREMENT, " - "key TEXT UNIQUE NOT NULL, " - "value TEXT NOT NULL" - ")", - [INSERT_META] = "INSERT OR IGNORE INTO Meta " - "(key, value) " - "VALUES " - "('revision', " SCHEMA_REVISION ")", - [SCHEMA_SUBSCRIBER] = "CREATE TABLE IF NOT EXISTS Subscriber (" - "id INTEGER PRIMARY KEY AUTOINCREMENT, " - "created TIMESTAMP NOT NULL, " - "updated TIMESTAMP NOT NULL, " - "imsi NUMERIC UNIQUE NOT NULL, " - "name TEXT, " - "extension TEXT UNIQUE, " - "authorized INTEGER NOT NULL DEFAULT 0, " - "tmsi TEXT UNIQUE, " - "lac INTEGER NOT NULL DEFAULT 0, " - "expire_lu TIMESTAMP DEFAULT NULL" - ")", - [SCHEMA_AUTH] = "CREATE TABLE IF NOT EXISTS AuthToken (" - "id INTEGER PRIMARY KEY AUTOINCREMENT, " - "subscriber_id INTEGER UNIQUE NOT NULL, " - "created TIMESTAMP NOT NULL, " - "token TEXT UNIQUE NOT NULL" - ")", - [SCHEMA_EQUIPMENT] = "CREATE TABLE IF NOT EXISTS Equipment (" - "id INTEGER PRIMARY KEY AUTOINCREMENT, " - "created TIMESTAMP NOT NULL, " - "updated TIMESTAMP NOT NULL, " - "name TEXT, " - "classmark1 NUMERIC, " - "classmark2 BLOB, " - "classmark3 BLOB, " - "imei NUMERIC UNIQUE NOT NULL" - ")", - [SCHEMA_EQUIPMENT_WATCH] = "CREATE TABLE IF NOT EXISTS EquipmentWatch (" - "id INTEGER PRIMARY KEY AUTOINCREMENT, " - "created TIMESTAMP NOT NULL, " - "updated TIMESTAMP NOT NULL, " - "subscriber_id NUMERIC NOT NULL, " - "equipment_id NUMERIC NOT NULL, " - "UNIQUE (subscriber_id, equipment_id) " - ")", - [SCHEMA_SMS] = "CREATE TABLE IF NOT EXISTS SMS (" - /* metadata, not part of sms */ - "id INTEGER PRIMARY KEY AUTOINCREMENT, " - "created TIMESTAMP NOT NULL, " - "sent TIMESTAMP, " - "deliver_attempts INTEGER NOT NULL DEFAULT 0, " - /* data directly copied/derived from SMS */ - "valid_until TIMESTAMP, " - "reply_path_req INTEGER NOT NULL, " - "status_rep_req INTEGER NOT NULL, " - "protocol_id INTEGER NOT NULL, " - "data_coding_scheme INTEGER NOT NULL, " - "ud_hdr_ind INTEGER NOT NULL, " - "src_addr TEXT NOT NULL, " - "src_ton INTEGER NOT NULL, " - "src_npi INTEGER NOT NULL, " - "dest_addr TEXT NOT NULL, " - "dest_ton INTEGER NOT NULL, " - "dest_npi INTEGER NOT NULL, " - "user_data BLOB, " /* TP-UD */ - /* additional data, interpreted from SMS */ - "header BLOB, " /* UD Header */ - "text TEXT " /* decoded UD after UDH */ - ")", - [SCHEMA_VLR] = "CREATE TABLE IF NOT EXISTS VLR (" - "id INTEGER PRIMARY KEY AUTOINCREMENT, " - "created TIMESTAMP NOT NULL, " - "updated TIMESTAMP NOT NULL, " - "subscriber_id NUMERIC UNIQUE NOT NULL, " - "last_bts NUMERIC NOT NULL " - ")", - [SCHEMA_APDU] = "CREATE TABLE IF NOT EXISTS ApduBlobs (" - "id INTEGER PRIMARY KEY AUTOINCREMENT, " - "created TIMESTAMP NOT NULL, " - "apdu_id_flags INTEGER NOT NULL, " - "subscriber_id INTEGER NOT NULL, " - "apdu BLOB " - ")", - [SCHEMA_COUNTERS] = "CREATE TABLE IF NOT EXISTS Counters (" - "id INTEGER PRIMARY KEY AUTOINCREMENT, " - "timestamp TIMESTAMP NOT NULL, " - "value INTEGER NOT NULL, " - "name TEXT NOT NULL " - ")", - [SCHEMA_RATE] = "CREATE TABLE IF NOT EXISTS RateCounters (" - "id INTEGER PRIMARY KEY AUTOINCREMENT, " - "timestamp TIMESTAMP NOT NULL, " - "value INTEGER NOT NULL, " - "name TEXT NOT NULL, " - "idx INTEGER NOT NULL " - ")", - [SCHEMA_AUTHKEY] = "CREATE TABLE IF NOT EXISTS AuthKeys (" - "subscriber_id INTEGER PRIMARY KEY, " - "algorithm_id INTEGER NOT NULL, " - "a3a8_ki BLOB " - ")", - [SCHEMA_AUTHLAST] = "CREATE TABLE IF NOT EXISTS AuthLastTuples (" - "subscriber_id INTEGER PRIMARY KEY, " - "issued TIMESTAMP NOT NULL, " - "use_count INTEGER NOT NULL DEFAULT 0, " - "key_seq INTEGER NOT NULL, " - "rand BLOB NOT NULL, " - "sres BLOB NOT NULL, " - "kc BLOB NOT NULL " - ")", -}; - -void db_error_func(dbi_conn conn, void *data) -{ - const char *msg; - dbi_conn_error(conn, &msg); - LOGP(DDB, LOGL_ERROR, "DBI: %s\n", msg); - osmo_log_backtrace(DDB, LOGL_ERROR); -} - -static int update_db_revision_2(void) -{ - dbi_result result; - - result = dbi_conn_query(conn, - "ALTER TABLE Subscriber " - "ADD COLUMN expire_lu " - "TIMESTAMP DEFAULT NULL"); - if (!result) { - LOGP(DDB, LOGL_ERROR, - "Failed to alter table Subscriber (upgrade from rev 2).\n"); - return -EINVAL; - } - dbi_result_free(result); - - result = dbi_conn_query(conn, - "UPDATE Meta " - "SET value = '3' " - "WHERE key = 'revision'"); - if (!result) { - LOGP(DDB, LOGL_ERROR, - "Failed to update DB schema revision (upgrade from rev 2).\n"); - return -EINVAL; - } - dbi_result_free(result); - - return 0; -} - -/** - * Copied from the normal sms_from_result_v3 to avoid having - * to make sure that the real routine will remain backward - * compatible. - */ -static struct gsm_sms *sms_from_result_v3(dbi_result result) -{ - struct gsm_sms *sms = sms_alloc(); - long long unsigned int sender_id; - struct gsm_subscriber *sender; - const char *text, *daddr; - const unsigned char *user_data; - char buf[32]; - - if (!sms) - return NULL; - - sms->id = dbi_result_get_ulonglong(result, "id"); - - sender_id = dbi_result_get_ulonglong(result, "sender_id"); - snprintf(buf, sizeof(buf), "%llu", sender_id); - sender = db_get_subscriber(GSM_SUBSCRIBER_ID, buf); - OSMO_ASSERT(sender); - osmo_strlcpy(sms->src.addr, sender->extension, sizeof(sms->src.addr)); - subscr_direct_free(sender); - sender = NULL; - - sms->reply_path_req = dbi_result_get_ulonglong(result, "reply_path_req"); - sms->status_rep_req = dbi_result_get_ulonglong(result, "status_rep_req"); - sms->ud_hdr_ind = dbi_result_get_ulonglong(result, "ud_hdr_ind"); - sms->protocol_id = dbi_result_get_ulonglong(result, "protocol_id"); - sms->data_coding_scheme = dbi_result_get_ulonglong(result, - "data_coding_scheme"); - - daddr = dbi_result_get_string(result, "dest_addr"); - if (daddr) - osmo_strlcpy(sms->dst.addr, daddr, sizeof(sms->dst.addr)); - - sms->user_data_len = dbi_result_get_field_length(result, "user_data"); - user_data = dbi_result_get_binary(result, "user_data"); - if (sms->user_data_len > sizeof(sms->user_data)) - sms->user_data_len = (uint8_t) sizeof(sms->user_data); - memcpy(sms->user_data, user_data, sms->user_data_len); - - text = dbi_result_get_string(result, "text"); - if (text) - osmo_strlcpy(sms->text, text, sizeof(sms->text)); - return sms; -} - -static int update_db_revision_3(void) -{ - dbi_result result; - struct gsm_sms *sms; - - LOGP(DDB, LOGL_NOTICE, "Going to migrate from revision 3\n"); - - result = dbi_conn_query(conn, "BEGIN EXCLUSIVE TRANSACTION"); - if (!result) { - LOGP(DDB, LOGL_ERROR, - "Failed to begin transaction (upgrade from rev 3)\n"); - return -EINVAL; - } - dbi_result_free(result); - - /* Rename old SMS table to be able create a new one */ - result = dbi_conn_query(conn, "ALTER TABLE SMS RENAME TO SMS_3"); - if (!result) { - LOGP(DDB, LOGL_ERROR, - "Failed to rename the old SMS table (upgrade from rev 3).\n"); - goto rollback; - } - dbi_result_free(result); - - /* Create new SMS table with all the bells and whistles! */ - result = dbi_conn_query(conn, create_stmts[SCHEMA_SMS]); - if (!result) { - LOGP(DDB, LOGL_ERROR, - "Failed to create a new SMS table (upgrade from rev 3).\n"); - goto rollback; - } - dbi_result_free(result); - - /* Cycle through old messages and convert them to the new format */ - result = dbi_conn_query(conn, "SELECT * FROM SMS_3"); - if (!result) { - LOGP(DDB, LOGL_ERROR, - "Failed fetch messages from the old SMS table (upgrade from rev 3).\n"); - goto rollback; - } - while (dbi_result_next_row(result)) { - sms = sms_from_result_v3(result); - if (db_sms_store(sms) != 0) { - LOGP(DDB, LOGL_ERROR, "Failed to store message to the new SMS table(upgrade from rev 3).\n"); - sms_free(sms); - dbi_result_free(result); - goto rollback; - } - sms_free(sms); - } - dbi_result_free(result); - - /* Remove the temporary table */ - result = dbi_conn_query(conn, "DROP TABLE SMS_3"); - if (!result) { - LOGP(DDB, LOGL_ERROR, - "Failed to drop the old SMS table (upgrade from rev 3).\n"); - goto rollback; - } - dbi_result_free(result); - - /* We're done. Bump DB Meta revision to 4 */ - result = dbi_conn_query(conn, - "UPDATE Meta " - "SET value = '4' " - "WHERE key = 'revision'"); - if (!result) { - LOGP(DDB, LOGL_ERROR, - "Failed to update DB schema revision (upgrade from rev 3).\n"); - goto rollback; - } - dbi_result_free(result); - - result = dbi_conn_query(conn, "COMMIT TRANSACTION"); - if (!result) { - LOGP(DDB, LOGL_ERROR, - "Failed to commit the transaction (upgrade from rev 3)\n"); - return -EINVAL; - } else { - dbi_result_free(result); - } - - /* Shrink DB file size by actually wiping out SMS_3 table data */ - result = dbi_conn_query(conn, "VACUUM"); - if (!result) - LOGP(DDB, LOGL_ERROR, - "VACUUM failed. Ignoring it (upgrade from rev 3).\n"); - else - dbi_result_free(result); - - return 0; - -rollback: - result = dbi_conn_query(conn, "ROLLBACK TRANSACTION"); - if (!result) - LOGP(DDB, LOGL_ERROR, - "Rollback failed (upgrade from rev 3).\n"); - else - dbi_result_free(result); - return -EINVAL; -} - -static int check_db_revision(void) -{ - dbi_result result; - const char *rev_s; - int db_rev = 0; - - /* Make a query */ - result = dbi_conn_query(conn, - "SELECT value FROM Meta " - "WHERE key = 'revision'"); - - if (!result) - return -EINVAL; - - if (!dbi_result_next_row(result)) { - dbi_result_free(result); - return -EINVAL; - } - - /* Fetch the DB schema revision */ - rev_s = dbi_result_get_string(result, "value"); - if (!rev_s) { - dbi_result_free(result); - return -EINVAL; - } - - if (!strcmp(rev_s, SCHEMA_REVISION)) { - /* Everything is fine */ - dbi_result_free(result); - return 0; - } - - db_rev = atoi(rev_s); - dbi_result_free(result); - - /* Incremental migration waterfall */ - switch (db_rev) { - case 2: - if (update_db_revision_2()) - goto error; - case 3: - if (update_db_revision_3()) - goto error; - - /* The end of waterfall */ - break; - default: - LOGP(DDB, LOGL_FATAL, - "Invalid database schema revision '%d'.\n", db_rev); - return -EINVAL; - } - - return 0; - -error: - LOGP(DDB, LOGL_FATAL, "Failed to update database " - "from schema revision '%d'.\n", db_rev); - return -EINVAL; -} - -static int db_configure(void) -{ - dbi_result result; - - result = dbi_conn_query(conn, - "PRAGMA synchronous = FULL"); - if (!result) - return -EINVAL; - - dbi_result_free(result); - return 0; -} - -int db_init(const char *name) -{ - dbi_initialize(NULL); - - conn = dbi_conn_new("sqlite3"); - if (conn == NULL) { - LOGP(DDB, LOGL_FATAL, "Failed to create connection.\n"); - return 1; - } - - dbi_conn_error_handler( conn, db_error_func, NULL ); - - /* MySQL - dbi_conn_set_option(conn, "host", "localhost"); - dbi_conn_set_option(conn, "username", "your_name"); - dbi_conn_set_option(conn, "password", "your_password"); - dbi_conn_set_option(conn, "dbname", "your_dbname"); - dbi_conn_set_option(conn, "encoding", "UTF-8"); - */ - - /* SqLite 3 */ - db_basename = strdup(name); - db_dirname = strdup(name); - dbi_conn_set_option(conn, "sqlite3_dbdir", dirname(db_dirname)); - dbi_conn_set_option(conn, "dbname", basename(db_basename)); - - if (dbi_conn_connect(conn) < 0) - goto out_err; - - return 0; - -out_err: - free(db_dirname); - free(db_basename); - db_dirname = db_basename = NULL; - return -1; -} - - -int db_prepare(void) -{ - dbi_result result; - int i; - - for (i = 0; i < ARRAY_SIZE(create_stmts); i++) { - result = dbi_conn_query(conn, create_stmts[i]); - if (!result) { - LOGP(DDB, LOGL_ERROR, - "Failed to create some table.\n"); - return 1; - } - dbi_result_free(result); - } - - if (check_db_revision() < 0) { - LOGP(DDB, LOGL_FATAL, "Database schema revision invalid, " - "please update your database schema\n"); - return -1; - } - - db_configure(); - - return 0; -} - -int db_fini(void) -{ - dbi_conn_close(conn); - dbi_shutdown(); - - free(db_dirname); - free(db_basename); - return 0; -} - -struct gsm_subscriber *db_create_subscriber(const char *imsi, uint64_t smin, - uint64_t smax, bool alloc_exten) -{ - dbi_result result; - struct gsm_subscriber *subscr; - - /* Is this subscriber known in the db? */ - subscr = db_get_subscriber(GSM_SUBSCRIBER_IMSI, imsi); - if (subscr) { - subscr_put(subscr); - return NULL; - } - - subscr = subscr_alloc(); - if (!subscr) - return NULL; - subscr->flags |= GSM_SUBSCRIBER_FIRST_CONTACT; - result = dbi_conn_queryf(conn, - "INSERT INTO Subscriber " - "(imsi, created, updated) " - "VALUES " - "(%s, datetime('now'), datetime('now')) ", - imsi - ); - if (!result) { - LOGP(DDB, LOGL_ERROR, "Failed to create Subscriber by IMSI.\n"); - subscr_put(subscr); - return NULL; - } - subscr->id = dbi_conn_sequence_last(conn, NULL); - osmo_strlcpy(subscr->imsi, imsi, sizeof(subscr->imsi)); - dbi_result_free(result); - LOGP(DDB, LOGL_INFO, "New Subscriber: ID %llu, IMSI %s\n", subscr->id, subscr->imsi); - if (alloc_exten) - db_subscriber_alloc_exten(subscr, smin, smax); - return subscr; -} - -osmo_static_assert(sizeof(unsigned char) == sizeof(struct gsm48_classmark1), classmark1_size); - -static int get_equipment_by_subscr(struct gsm_subscriber *subscr) -{ - dbi_result result; - const char *string; - unsigned char cm1; - const unsigned char *cm2, *cm3; - struct gsm_equipment *equip = &subscr->equipment; - - result = dbi_conn_queryf(conn, - "SELECT Equipment.* " - "FROM Equipment JOIN EquipmentWatch ON " - "EquipmentWatch.equipment_id=Equipment.id " - "WHERE EquipmentWatch.subscriber_id = %llu " - "ORDER BY EquipmentWatch.updated DESC", subscr->id); - if (!result) - return -EIO; - - if (!dbi_result_next_row(result)) { - dbi_result_free(result); - return -ENOENT; - } - - equip->id = dbi_result_get_ulonglong(result, "id"); - - string = dbi_result_get_string(result, "imei"); - if (string) - osmo_strlcpy(equip->imei, string, sizeof(equip->imei)); - - string = dbi_result_get_string(result, "classmark1"); - if (string) { - cm1 = atoi(string) & 0xff; - memcpy(&equip->classmark1, &cm1, sizeof(equip->classmark1)); - } - - equip->classmark2_len = dbi_result_get_field_length(result, "classmark2"); - cm2 = dbi_result_get_binary(result, "classmark2"); - if (equip->classmark2_len > sizeof(equip->classmark2)) - equip->classmark2_len = sizeof(equip->classmark2); - if (cm2) - memcpy(equip->classmark2, cm2, equip->classmark2_len); - - equip->classmark3_len = dbi_result_get_field_length(result, "classmark3"); - cm3 = dbi_result_get_binary(result, "classmark3"); - if (equip->classmark3_len > sizeof(equip->classmark3)) - equip->classmark3_len = sizeof(equip->classmark3); - if (cm3) - memcpy(equip->classmark3, cm3, equip->classmark3_len); - - dbi_result_free(result); - - return 0; -} - -int db_get_authinfo_for_subscr(struct gsm_auth_info *ainfo, - struct gsm_subscriber *subscr) -{ - dbi_result result; - const unsigned char *a3a8_ki; - - result = dbi_conn_queryf(conn, - "SELECT * FROM AuthKeys WHERE subscriber_id=%llu", - subscr->id); - if (!result) - return -EIO; - - if (!dbi_result_next_row(result)) { - dbi_result_free(result); - return -ENOENT; - } - - ainfo->auth_algo = dbi_result_get_ulonglong(result, "algorithm_id"); - ainfo->a3a8_ki_len = dbi_result_get_field_length(result, "a3a8_ki"); - a3a8_ki = dbi_result_get_binary(result, "a3a8_ki"); - if (ainfo->a3a8_ki_len > sizeof(ainfo->a3a8_ki)) - ainfo->a3a8_ki_len = sizeof(ainfo->a3a8_ki); - memcpy(ainfo->a3a8_ki, a3a8_ki, ainfo->a3a8_ki_len); - - dbi_result_free(result); - - return 0; -} - -int db_sync_authinfo_for_subscr(struct gsm_auth_info *ainfo, - struct gsm_subscriber *subscr) -{ - dbi_result result; - struct gsm_auth_info ainfo_old; - int rc, upd; - unsigned char *ki_str; - - /* Deletion ? */ - if (ainfo == NULL) { - result = dbi_conn_queryf(conn, - "DELETE FROM AuthKeys WHERE subscriber_id=%llu", - subscr->id); - - if (!result) - return -EIO; - - dbi_result_free(result); - - return 0; - } - - /* Check if already existing */ - rc = db_get_authinfo_for_subscr(&ainfo_old, subscr); - if (rc && rc != -ENOENT) - return rc; - upd = rc ? 0 : 1; - - /* Update / Insert */ - dbi_conn_quote_binary_copy(conn, - ainfo->a3a8_ki, ainfo->a3a8_ki_len, &ki_str); - - if (!upd) { - result = dbi_conn_queryf(conn, - "INSERT INTO AuthKeys " - "(subscriber_id, algorithm_id, a3a8_ki) " - "VALUES (%llu, %u, %s)", - subscr->id, ainfo->auth_algo, ki_str); - } else { - result = dbi_conn_queryf(conn, - "UPDATE AuthKeys " - "SET algorithm_id=%u, a3a8_ki=%s " - "WHERE subscriber_id=%llu", - ainfo->auth_algo, ki_str, subscr->id); - } - - free(ki_str); - - if (!result) - return -EIO; - - dbi_result_free(result); - - return 0; -} - -int db_get_lastauthtuple_for_subscr(struct gsm_auth_tuple *atuple, - struct gsm_subscriber *subscr) -{ - dbi_result result; - int len; - const unsigned char *blob; - - result = dbi_conn_queryf(conn, - "SELECT * FROM AuthLastTuples WHERE subscriber_id=%llu", - subscr->id); - if (!result) - return -EIO; - - if (!dbi_result_next_row(result)) { - dbi_result_free(result); - return -ENOENT; - } - - memset(atuple, 0, sizeof(*atuple)); - - atuple->use_count = dbi_result_get_ulonglong(result, "use_count"); - atuple->key_seq = dbi_result_get_ulonglong(result, "key_seq"); - - len = dbi_result_get_field_length(result, "rand"); - if (len != sizeof(atuple->vec.rand)) - goto err_size; - - blob = dbi_result_get_binary(result, "rand"); - memcpy(atuple->vec.rand, blob, len); - - len = dbi_result_get_field_length(result, "sres"); - if (len != sizeof(atuple->vec.sres)) - goto err_size; - - blob = dbi_result_get_binary(result, "sres"); - memcpy(atuple->vec.sres, blob, len); - - len = dbi_result_get_field_length(result, "kc"); - if (len != sizeof(atuple->vec.kc)) - goto err_size; - - blob = dbi_result_get_binary(result, "kc"); - memcpy(atuple->vec.kc, blob, len); - - dbi_result_free(result); - - return 0; - -err_size: - dbi_result_free(result); - return -EIO; -} - -int db_sync_lastauthtuple_for_subscr(struct gsm_auth_tuple *atuple, - struct gsm_subscriber *subscr) -{ - dbi_result result; - int rc, upd; - struct gsm_auth_tuple atuple_old; - unsigned char *rand_str, *sres_str, *kc_str; - - /* Deletion ? */ - if (atuple == NULL) { - result = dbi_conn_queryf(conn, - "DELETE FROM AuthLastTuples WHERE subscriber_id=%llu", - subscr->id); - - if (!result) - return -EIO; - - dbi_result_free(result); - - return 0; - } - - /* Check if already existing */ - rc = db_get_lastauthtuple_for_subscr(&atuple_old, subscr); - if (rc && rc != -ENOENT) - return rc; - upd = rc ? 0 : 1; - - /* Update / Insert */ - dbi_conn_quote_binary_copy(conn, - atuple->vec.rand, sizeof(atuple->vec.rand), &rand_str); - dbi_conn_quote_binary_copy(conn, - atuple->vec.sres, sizeof(atuple->vec.sres), &sres_str); - dbi_conn_quote_binary_copy(conn, - atuple->vec.kc, sizeof(atuple->vec.kc), &kc_str); - - if (!upd) { - result = dbi_conn_queryf(conn, - "INSERT INTO AuthLastTuples " - "(subscriber_id, issued, use_count, " - "key_seq, rand, sres, kc) " - "VALUES (%llu, datetime('now'), %u, " - "%u, %s, %s, %s ) ", - subscr->id, atuple->use_count, atuple->key_seq, - rand_str, sres_str, kc_str); - } else { - char *issued = atuple->key_seq == atuple_old.key_seq ? - "issued" : "datetime('now')"; - result = dbi_conn_queryf(conn, - "UPDATE AuthLastTuples " - "SET issued=%s, use_count=%u, " - "key_seq=%u, rand=%s, sres=%s, kc=%s " - "WHERE subscriber_id = %llu", - issued, atuple->use_count, atuple->key_seq, - rand_str, sres_str, kc_str, subscr->id); - } - - free(rand_str); - free(sres_str); - free(kc_str); - - if (!result) - return -EIO; - - dbi_result_free(result); - - return 0; -} - -static void db_set_from_query(struct gsm_subscriber *subscr, dbi_conn result) -{ - const char *string; - string = dbi_result_get_string(result, "imsi"); - if (string) - osmo_strlcpy(subscr->imsi, string, sizeof(subscr->imsi)); - - string = dbi_result_get_string(result, "tmsi"); - if (string) - subscr->tmsi = tmsi_from_string(string); - - string = dbi_result_get_string(result, "name"); - if (string) - osmo_strlcpy(subscr->name, string, sizeof(subscr->name)); - - string = dbi_result_get_string(result, "extension"); - if (string) - osmo_strlcpy(subscr->extension, string, sizeof(subscr->extension)); - - subscr->lac = dbi_result_get_ulonglong(result, "lac"); - - if (!dbi_result_field_is_null(result, "expire_lu")) - subscr->expire_lu = dbi_result_get_datetime(result, "expire_lu"); - else - subscr->expire_lu = GSM_SUBSCRIBER_NO_EXPIRATION; - - subscr->authorized = dbi_result_get_ulonglong(result, "authorized"); - -} - -#define BASE_QUERY "SELECT * FROM Subscriber " -struct gsm_subscriber *db_get_subscriber(enum gsm_subscriber_field field, - const char *id) -{ - dbi_result result; - char *quoted; - struct gsm_subscriber *subscr; - - switch (field) { - case GSM_SUBSCRIBER_IMSI: - dbi_conn_quote_string_copy(conn, id, "ed); - result = dbi_conn_queryf(conn, - BASE_QUERY - "WHERE imsi = %s ", - quoted - ); - free(quoted); - break; - case GSM_SUBSCRIBER_TMSI: - dbi_conn_quote_string_copy(conn, id, "ed); - result = dbi_conn_queryf(conn, - BASE_QUERY - "WHERE tmsi = %s ", - quoted - ); - free(quoted); - break; - case GSM_SUBSCRIBER_EXTENSION: - dbi_conn_quote_string_copy(conn, id, "ed); - result = dbi_conn_queryf(conn, - BASE_QUERY - "WHERE extension = %s ", - quoted - ); - free(quoted); - break; - case GSM_SUBSCRIBER_ID: - dbi_conn_quote_string_copy(conn, id, "ed); - result = dbi_conn_queryf(conn, - BASE_QUERY - "WHERE id = %s ", quoted); - free(quoted); - break; - default: - LOGP(DDB, LOGL_NOTICE, "Unknown query selector for Subscriber.\n"); - return NULL; - } - if (!result) { - LOGP(DDB, LOGL_ERROR, "Failed to query Subscriber.\n"); - return NULL; - } - if (!dbi_result_next_row(result)) { - DEBUGP(DDB, "Failed to find the Subscriber. '%u' '%s'\n", - field, id); - dbi_result_free(result); - return NULL; - } - - subscr = subscr_alloc(); - subscr->id = dbi_result_get_ulonglong(result, "id"); - - db_set_from_query(subscr, result); - DEBUGP(DDB, "Found Subscriber: ID %llu, IMSI %s, NAME '%s', TMSI %x, EXTEN '%s', LAC %hu, AUTH %u\n", - subscr->id, subscr->imsi, subscr->name, subscr->tmsi, subscr->extension, - subscr->lac, subscr->authorized); - dbi_result_free(result); - - get_equipment_by_subscr(subscr); - - return subscr; -} - -int db_subscriber_update(struct gsm_subscriber *subscr) -{ - char buf[32]; - dbi_result result; - - /* Copy the id to a string as queryf with %llu is failing */ - sprintf(buf, "%llu", subscr->id); - result = dbi_conn_queryf(conn, - BASE_QUERY - "WHERE id = %s", buf); - - if (!result) { - LOGP(DDB, LOGL_ERROR, "Failed to query Subscriber: %llu\n", subscr->id); - return -EIO; - } - if (!dbi_result_next_row(result)) { - DEBUGP(DDB, "Failed to find the Subscriber. %llu\n", - subscr->id); - dbi_result_free(result); - return -EIO; - } - - db_set_from_query(subscr, result); - dbi_result_free(result); - get_equipment_by_subscr(subscr); - - return 0; -} - -int db_sync_subscriber(struct gsm_subscriber *subscriber) -{ - dbi_result result; - char tmsi[14]; - char *q_tmsi, *q_name, *q_extension; - - dbi_conn_quote_string_copy(conn, - subscriber->name, &q_name); - if (subscriber->extension[0] != '\0') - dbi_conn_quote_string_copy(conn, - subscriber->extension, &q_extension); - else - q_extension = strdup("NULL"); - - if (subscriber->tmsi != GSM_RESERVED_TMSI) { - sprintf(tmsi, "%u", subscriber->tmsi); - dbi_conn_quote_string_copy(conn, - tmsi, - &q_tmsi); - } else - q_tmsi = strdup("NULL"); - - if (subscriber->expire_lu == GSM_SUBSCRIBER_NO_EXPIRATION) { - result = dbi_conn_queryf(conn, - "UPDATE Subscriber " - "SET updated = datetime('now'), " - "name = %s, " - "extension = %s, " - "authorized = %i, " - "tmsi = %s, " - "lac = %i, " - "expire_lu = NULL " - "WHERE imsi = %s ", - q_name, - q_extension, - subscriber->authorized, - q_tmsi, - subscriber->lac, - subscriber->imsi); - } else { - result = dbi_conn_queryf(conn, - "UPDATE Subscriber " - "SET updated = datetime('now'), " - "name = %s, " - "extension = %s, " - "authorized = %i, " - "tmsi = %s, " - "lac = %i, " - "expire_lu = datetime(%i, 'unixepoch') " - "WHERE imsi = %s ", - q_name, - q_extension, - subscriber->authorized, - q_tmsi, - subscriber->lac, - (int) subscriber->expire_lu, - subscriber->imsi); - } - - free(q_tmsi); - free(q_name); - free(q_extension); - - if (!result) { - LOGP(DDB, LOGL_ERROR, "Failed to update Subscriber (by IMSI).\n"); - return 1; - } - - dbi_result_free(result); - - return 0; -} - -int db_subscriber_delete(struct gsm_subscriber *subscr) -{ - dbi_result result; - - result = dbi_conn_queryf(conn, - "DELETE FROM AuthKeys WHERE subscriber_id=%llu", - subscr->id); - if (!result) { - LOGP(DDB, LOGL_ERROR, - "Failed to delete Authkeys for %llu\n", subscr->id); - return -1; - } - dbi_result_free(result); - - result = dbi_conn_queryf(conn, - "DELETE FROM AuthLastTuples WHERE subscriber_id=%llu", - subscr->id); - if (!result) { - LOGP(DDB, LOGL_ERROR, - "Failed to delete AuthLastTuples for %llu\n", subscr->id); - return -1; - } - dbi_result_free(result); - - result = dbi_conn_queryf(conn, - "DELETE FROM AuthToken WHERE subscriber_id=%llu", - subscr->id); - if (!result) { - LOGP(DDB, LOGL_ERROR, - "Failed to delete AuthToken for %llu\n", subscr->id); - return -1; - } - dbi_result_free(result); - - result = dbi_conn_queryf(conn, - "DELETE FROM EquipmentWatch WHERE subscriber_id=%llu", - subscr->id); - if (!result) { - LOGP(DDB, LOGL_ERROR, - "Failed to delete EquipmentWatch for %llu\n", subscr->id); - return -1; - } - dbi_result_free(result); - - if (subscr->extension[0] != '\0') { - result = dbi_conn_queryf(conn, - "DELETE FROM SMS WHERE src_addr=%s OR dest_addr=%s", - subscr->extension, subscr->extension); - if (!result) { - LOGP(DDB, LOGL_ERROR, - "Failed to delete SMS for %llu\n", subscr->id); - return -1; - } - dbi_result_free(result); - } - - result = dbi_conn_queryf(conn, - "DELETE FROM VLR WHERE subscriber_id=%llu", - subscr->id); - if (!result) { - LOGP(DDB, LOGL_ERROR, - "Failed to delete VLR for %llu\n", subscr->id); - return -1; - } - dbi_result_free(result); - - result = dbi_conn_queryf(conn, - "DELETE FROM ApduBlobs WHERE subscriber_id=%llu", - subscr->id); - if (!result) { - LOGP(DDB, LOGL_ERROR, - "Failed to delete ApduBlobs for %llu\n", subscr->id); - return -1; - } - dbi_result_free(result); - - result = dbi_conn_queryf(conn, - "DELETE FROM Subscriber WHERE id=%llu", - subscr->id); - if (!result) { - LOGP(DDB, LOGL_ERROR, - "Failed to delete Subscriber for %llu\n", subscr->id); - return -1; - } - dbi_result_free(result); - - return 0; -} - -/** - * List all the authorized and non-expired subscribers. The callback will - * be called one by one. The subscr argument is not fully initialize and - * subscr_get/subscr_put must not be called. The passed in pointer will be - * deleted after the callback by the database call. - */ -int db_subscriber_list_active(void (*cb)(struct gsm_subscriber*,void*), void *closure) -{ - dbi_result result; - - result = dbi_conn_query(conn, - "SELECT * from Subscriber WHERE LAC != 0 AND authorized = 1"); - if (!result) { - LOGP(DDB, LOGL_ERROR, "Failed to list active subscribers\n"); - return -1; - } - - while (dbi_result_next_row(result)) { - struct gsm_subscriber *subscr; - - subscr = subscr_alloc(); - subscr->id = dbi_result_get_ulonglong(result, "id"); - db_set_from_query(subscr, result); - cb(subscr, closure); - OSMO_ASSERT(subscr->use_count == 1); - llist_del(&subscr->entry); - talloc_free(subscr); - } - - dbi_result_free(result); - return 0; -} - -int db_sync_equipment(struct gsm_equipment *equip) -{ - dbi_result result; - unsigned char *cm2, *cm3; - char *q_imei; - uint8_t classmark1; - - memcpy(&classmark1, &equip->classmark1, sizeof(classmark1)); - DEBUGP(DDB, "Sync Equipment IMEI=%s, classmark1=%02x", - equip->imei, classmark1); - if (equip->classmark2_len) - DEBUGPC(DDB, ", classmark2=%s", - osmo_hexdump(equip->classmark2, equip->classmark2_len)); - if (equip->classmark3_len) - DEBUGPC(DDB, ", classmark3=%s", - osmo_hexdump(equip->classmark3, equip->classmark3_len)); - DEBUGPC(DDB, "\n"); - - dbi_conn_quote_binary_copy(conn, equip->classmark2, - equip->classmark2_len, &cm2); - dbi_conn_quote_binary_copy(conn, equip->classmark3, - equip->classmark3_len, &cm3); - dbi_conn_quote_string_copy(conn, equip->imei, &q_imei); - - result = dbi_conn_queryf(conn, - "UPDATE Equipment SET " - "updated = datetime('now'), " - "classmark1 = %u, " - "classmark2 = %s, " - "classmark3 = %s " - "WHERE imei = %s ", - classmark1, cm2, cm3, q_imei); - - free(cm2); - free(cm3); - free(q_imei); - - if (!result) { - LOGP(DDB, LOGL_ERROR, "Failed to update Equipment\n"); - return -EIO; - } - - dbi_result_free(result); - return 0; -} - -int db_subscriber_expire(void *priv, void (*callback)(void *priv, long long unsigned int id)) -{ - dbi_result result; - - result = dbi_conn_query(conn, - "SELECT id " - "FROM Subscriber " - "WHERE lac != 0 AND " - "( expire_lu is NOT NULL " - "AND expire_lu < datetime('now') ) " - "LIMIT 1"); - if (!result) { - LOGP(DDB, LOGL_ERROR, "Failed to get expired subscribers\n"); - return -EIO; - } - - while (dbi_result_next_row(result)) - callback(priv, dbi_result_get_ulonglong(result, "id")); - - dbi_result_free(result); - return 0; -} - -int db_subscriber_alloc_tmsi(struct gsm_subscriber *subscriber) -{ - dbi_result result = NULL; - char tmsi[14]; - char *tmsi_quoted; - - for (;;) { - if (RAND_bytes((uint8_t *) &subscriber->tmsi, sizeof(subscriber->tmsi)) != 1) { - LOGP(DDB, LOGL_ERROR, "RAND_bytes failed\n"); - return 1; - } - if (subscriber->tmsi == GSM_RESERVED_TMSI) - continue; - - sprintf(tmsi, "%u", subscriber->tmsi); - dbi_conn_quote_string_copy(conn, tmsi, &tmsi_quoted); - result = dbi_conn_queryf(conn, - "SELECT * FROM Subscriber " - "WHERE tmsi = %s ", - tmsi_quoted); - - free(tmsi_quoted); - - if (!result) { - LOGP(DDB, LOGL_ERROR, "Failed to query Subscriber " - "while allocating new TMSI.\n"); - return 1; - } - if (dbi_result_get_numrows(result)) { - dbi_result_free(result); - continue; - } - if (!dbi_result_next_row(result)) { - dbi_result_free(result); - DEBUGP(DDB, "Allocated TMSI %u for IMSI %s.\n", - subscriber->tmsi, subscriber->imsi); - return db_sync_subscriber(subscriber); - } - dbi_result_free(result); - } - return 0; -} - -int db_subscriber_alloc_exten(struct gsm_subscriber *subscriber, uint64_t smin, - uint64_t smax) -{ - dbi_result result = NULL; - uint64_t try; - - for (;;) { - try = (rand() % (smax - smin + 1) + smin); - result = dbi_conn_queryf(conn, - "SELECT * FROM Subscriber " - "WHERE extension = %"PRIu64, - try - ); - if (!result) { - LOGP(DDB, LOGL_ERROR, "Failed to query Subscriber " - "while allocating new extension.\n"); - return 1; - } - if (dbi_result_get_numrows(result)){ - dbi_result_free(result); - continue; - } - if (!dbi_result_next_row(result)) { - dbi_result_free(result); - break; - } - dbi_result_free(result); - } - sprintf(subscriber->extension, "%"PRIu64, try); - DEBUGP(DDB, "Allocated extension %"PRIu64 " for IMSI %s.\n", try, subscriber->imsi); - return db_sync_subscriber(subscriber); -} -/* - * try to allocate a new unique token for this subscriber and return it - * via a parameter. if the subscriber already has a token, return - * an error. - */ - -int db_subscriber_alloc_token(struct gsm_subscriber *subscriber, uint32_t *token) -{ - dbi_result result; - uint32_t try; - - for (;;) { - if (RAND_bytes((uint8_t *) &try, sizeof(try)) != 1) { - LOGP(DDB, LOGL_ERROR, "RAND_bytes failed\n"); - return 1; - } - if (!try) /* 0 is an invalid token */ - continue; - result = dbi_conn_queryf(conn, - "SELECT * FROM AuthToken " - "WHERE subscriber_id = %llu OR token = \"%08X\" ", - subscriber->id, try); - if (!result) { - LOGP(DDB, LOGL_ERROR, "Failed to query AuthToken " - "while allocating new token.\n"); - return 1; - } - if (dbi_result_get_numrows(result)) { - dbi_result_free(result); - continue; - } - if (!dbi_result_next_row(result)) { - dbi_result_free(result); - break; - } - dbi_result_free(result); - } - result = dbi_conn_queryf(conn, - "INSERT INTO AuthToken " - "(subscriber_id, created, token) " - "VALUES " - "(%llu, datetime('now'), \"%08X\") ", - subscriber->id, try); - if (!result) { - LOGP(DDB, LOGL_ERROR, "Failed to create token %08X for " - "IMSI %s.\n", try, subscriber->imsi); - return 1; - } - dbi_result_free(result); - *token = try; - DEBUGP(DDB, "Allocated token %08X for IMSI %s.\n", try, subscriber->imsi); - - return 0; -} - -int db_subscriber_assoc_imei(struct gsm_subscriber *subscriber, char imei[GSM23003_IMEISV_NUM_DIGITS]) -{ - unsigned long long equipment_id, watch_id; - dbi_result result; - - osmo_strlcpy(subscriber->equipment.imei, imei, sizeof(subscriber->equipment.imei)); - - result = dbi_conn_queryf(conn, - "INSERT OR IGNORE INTO Equipment " - "(imei, created, updated) " - "VALUES " - "(%s, datetime('now'), datetime('now')) ", - imei); - if (!result) { - LOGP(DDB, LOGL_ERROR, "Failed to create Equipment by IMEI.\n"); - return 1; - } - - equipment_id = 0; - if (dbi_result_get_numrows_affected(result)) { - equipment_id = dbi_conn_sequence_last(conn, NULL); - } - dbi_result_free(result); - - if (equipment_id) - DEBUGP(DDB, "New Equipment: ID %llu, IMEI %s\n", equipment_id, imei); - else { - result = dbi_conn_queryf(conn, - "SELECT id FROM Equipment " - "WHERE imei = %s ", - imei - ); - if (!result) { - LOGP(DDB, LOGL_ERROR, "Failed to query Equipment by IMEI.\n"); - return 1; - } - if (!dbi_result_next_row(result)) { - LOGP(DDB, LOGL_ERROR, "Failed to find the Equipment.\n"); - dbi_result_free(result); - return 1; - } - equipment_id = dbi_result_get_ulonglong(result, "id"); - dbi_result_free(result); - } - - result = dbi_conn_queryf(conn, - "INSERT OR IGNORE INTO EquipmentWatch " - "(subscriber_id, equipment_id, created, updated) " - "VALUES " - "(%llu, %llu, datetime('now'), datetime('now')) ", - subscriber->id, equipment_id); - if (!result) { - LOGP(DDB, LOGL_ERROR, "Failed to create EquipmentWatch.\n"); - return 1; - } - - watch_id = 0; - if (dbi_result_get_numrows_affected(result)) - watch_id = dbi_conn_sequence_last(conn, NULL); - - dbi_result_free(result); - if (watch_id) - DEBUGP(DDB, "New EquipmentWatch: ID %llu, IMSI %s, IMEI %s\n", - equipment_id, subscriber->imsi, imei); - else { - result = dbi_conn_queryf(conn, - "UPDATE EquipmentWatch " - "SET updated = datetime('now') " - "WHERE subscriber_id = %llu AND equipment_id = %llu ", - subscriber->id, equipment_id); - if (!result) { - LOGP(DDB, LOGL_ERROR, "Failed to update EquipmentWatch.\n"); - return 1; - } - dbi_result_free(result); - DEBUGP(DDB, "Updated EquipmentWatch: ID %llu, IMSI %s, IMEI %s\n", - equipment_id, subscriber->imsi, imei); - } - - return 0; -} - -/* store an [unsent] SMS to the database */ -int db_sms_store(struct gsm_sms *sms) -{ - dbi_result result; - char *q_text, *q_daddr, *q_saddr; - unsigned char *q_udata; - char *validity_timestamp = "2222-2-2"; - - /* FIXME: generate validity timestamp based on validity_minutes */ - - dbi_conn_quote_string_copy(conn, (char *)sms->text, &q_text); - dbi_conn_quote_string_copy(conn, (char *)sms->dst.addr, &q_daddr); - dbi_conn_quote_string_copy(conn, (char *)sms->src.addr, &q_saddr); - dbi_conn_quote_binary_copy(conn, sms->user_data, sms->user_data_len, - &q_udata); - - /* FIXME: correct validity period */ - result = dbi_conn_queryf(conn, - "INSERT INTO SMS " - "(created, valid_until, " - "reply_path_req, status_rep_req, protocol_id, " - "data_coding_scheme, ud_hdr_ind, " - "user_data, text, " - "dest_addr, dest_ton, dest_npi, " - "src_addr, src_ton, src_npi) VALUES " - "(datetime('now'), %u, " - "%u, %u, %u, " - "%u, %u, " - "%s, %s, " - "%s, %u, %u, " - "%s, %u, %u)", - validity_timestamp, - sms->reply_path_req, sms->status_rep_req, sms->protocol_id, - sms->data_coding_scheme, sms->ud_hdr_ind, - q_udata, q_text, - q_daddr, sms->dst.ton, sms->dst.npi, - q_saddr, sms->src.ton, sms->src.npi); - free(q_text); - free(q_udata); - free(q_daddr); - free(q_saddr); - - if (!result) - return -EIO; - - dbi_result_free(result); - return 0; -} - -static struct gsm_sms *sms_from_result(struct gsm_network *net, dbi_result result) -{ - struct gsm_sms *sms = sms_alloc(); - const char *text, *daddr, *saddr; - const unsigned char *user_data; - - if (!sms) - return NULL; - - sms->id = dbi_result_get_ulonglong(result, "id"); - - /* FIXME: validity */ - /* FIXME: those should all be get_uchar, but sqlite3 is braindead */ - sms->reply_path_req = dbi_result_get_ulonglong(result, "reply_path_req"); - sms->status_rep_req = dbi_result_get_ulonglong(result, "status_rep_req"); - sms->ud_hdr_ind = dbi_result_get_ulonglong(result, "ud_hdr_ind"); - sms->protocol_id = dbi_result_get_ulonglong(result, "protocol_id"); - sms->data_coding_scheme = dbi_result_get_ulonglong(result, - "data_coding_scheme"); - /* sms->msg_ref is temporary and not stored in DB */ - - sms->dst.npi = dbi_result_get_ulonglong(result, "dest_npi"); - sms->dst.ton = dbi_result_get_ulonglong(result, "dest_ton"); - daddr = dbi_result_get_string(result, "dest_addr"); - if (daddr) - osmo_strlcpy(sms->dst.addr, daddr, sizeof(sms->dst.addr)); - sms->receiver = subscr_get_by_extension(net->subscr_group, sms->dst.addr); - - sms->src.npi = dbi_result_get_ulonglong(result, "src_npi"); - sms->src.ton = dbi_result_get_ulonglong(result, "src_ton"); - saddr = dbi_result_get_string(result, "src_addr"); - if (saddr) - osmo_strlcpy(sms->src.addr, saddr, sizeof(sms->src.addr)); - - sms->user_data_len = dbi_result_get_field_length(result, "user_data"); - user_data = dbi_result_get_binary(result, "user_data"); - if (sms->user_data_len > sizeof(sms->user_data)) - sms->user_data_len = (uint8_t) sizeof(sms->user_data); - memcpy(sms->user_data, user_data, sms->user_data_len); - - text = dbi_result_get_string(result, "text"); - if (text) - osmo_strlcpy(sms->text, text, sizeof(sms->text)); - return sms; -} - -struct gsm_sms *db_sms_get(struct gsm_network *net, unsigned long long id) -{ - dbi_result result; - struct gsm_sms *sms; - - result = dbi_conn_queryf(conn, - "SELECT * FROM SMS WHERE SMS.id = %llu", id); - if (!result) - return NULL; - - if (!dbi_result_next_row(result)) { - dbi_result_free(result); - return NULL; - } - - sms = sms_from_result(net, result); - - dbi_result_free(result); - - return sms; -} - -/* retrieve the next unsent SMS with ID >= min_id */ -struct gsm_sms *db_sms_get_unsent(struct gsm_network *net, unsigned long long min_id) -{ - dbi_result result; - struct gsm_sms *sms; - - result = dbi_conn_queryf(conn, - "SELECT SMS.* " - "FROM SMS JOIN Subscriber ON " - "SMS.dest_addr = Subscriber.extension " - "WHERE SMS.id >= %llu AND SMS.sent IS NULL " - "AND Subscriber.lac > 0 " - "ORDER BY SMS.id LIMIT 1", - min_id); - if (!result) - return NULL; - - if (!dbi_result_next_row(result)) { - dbi_result_free(result); - return NULL; - } - - sms = sms_from_result(net, result); - - dbi_result_free(result); - - return sms; -} - -struct gsm_sms *db_sms_get_unsent_by_subscr(struct gsm_network *net, - unsigned long long min_subscr_id, - unsigned int failed) -{ - dbi_result result; - struct gsm_sms *sms; - - result = dbi_conn_queryf(conn, - "SELECT SMS.* " - "FROM SMS JOIN Subscriber ON " - "SMS.dest_addr = Subscriber.extension " - "WHERE Subscriber.id >= %llu AND SMS.sent IS NULL " - "AND Subscriber.lac > 0 AND SMS.deliver_attempts < %u " - "ORDER BY Subscriber.id, SMS.id LIMIT 1", - min_subscr_id, failed); - if (!result) - return NULL; - - if (!dbi_result_next_row(result)) { - dbi_result_free(result); - return NULL; - } - - sms = sms_from_result(net, result); - - dbi_result_free(result); - - return sms; -} - -/* retrieve the next unsent SMS for a given subscriber */ -struct gsm_sms *db_sms_get_unsent_for_subscr(struct gsm_subscriber *subscr) -{ - dbi_result result; - struct gsm_sms *sms; - - result = dbi_conn_queryf(conn, - "SELECT SMS.* " - "FROM SMS JOIN Subscriber ON " - "SMS.dest_addr = Subscriber.extension " - "WHERE Subscriber.id = %llu AND SMS.sent IS NULL " - "AND Subscriber.lac > 0 " - "ORDER BY SMS.id LIMIT 1", - subscr->id); - if (!result) - return NULL; - - if (!dbi_result_next_row(result)) { - dbi_result_free(result); - return NULL; - } - - sms = sms_from_result(subscr->group->net, result); - - dbi_result_free(result); - - return sms; -} - -/* mark a given SMS as delivered */ -int db_sms_mark_delivered(struct gsm_sms *sms) -{ - dbi_result result; - - result = dbi_conn_queryf(conn, - "UPDATE SMS " - "SET sent = datetime('now') " - "WHERE id = %llu", sms->id); - if (!result) { - LOGP(DDB, LOGL_ERROR, "Failed to mark SMS %llu as sent.\n", sms->id); - return 1; - } - - dbi_result_free(result); - return 0; -} - -/* increase the number of attempted deliveries */ -int db_sms_inc_deliver_attempts(struct gsm_sms *sms) -{ - dbi_result result; - - result = dbi_conn_queryf(conn, - "UPDATE SMS " - "SET deliver_attempts = deliver_attempts + 1 " - "WHERE id = %llu", sms->id); - if (!result) { - LOGP(DDB, LOGL_ERROR, "Failed to inc deliver attempts for " - "SMS %llu.\n", sms->id); - return 1; - } - - dbi_result_free(result); - return 0; -} - -int db_apdu_blob_store(struct gsm_subscriber *subscr, - uint8_t apdu_id_flags, uint8_t len, - uint8_t *apdu) -{ - dbi_result result; - unsigned char *q_apdu; - - dbi_conn_quote_binary_copy(conn, apdu, len, &q_apdu); - - result = dbi_conn_queryf(conn, - "INSERT INTO ApduBlobs " - "(created,subscriber_id,apdu_id_flags,apdu) VALUES " - "(datetime('now'),%llu,%u,%s)", - subscr->id, apdu_id_flags, q_apdu); - - free(q_apdu); - - if (!result) - return -EIO; - - dbi_result_free(result); - return 0; -} - -int db_store_counter(struct osmo_counter *ctr) -{ - dbi_result result; - char *q_name; - - dbi_conn_quote_string_copy(conn, ctr->name, &q_name); - - result = dbi_conn_queryf(conn, - "INSERT INTO Counters " - "(timestamp,name,value) VALUES " - "(datetime('now'),%s,%lu)", q_name, ctr->value); - - free(q_name); - - if (!result) - return -EIO; - - dbi_result_free(result); - return 0; -} - -static int db_store_rate_ctr(struct rate_ctr_group *ctrg, unsigned int num, - char *q_prefix) -{ - dbi_result result; - char *q_name; - - dbi_conn_quote_string_copy(conn, ctrg->desc->ctr_desc[num].name, - &q_name); - - result = dbi_conn_queryf(conn, - "Insert INTO RateCounters " - "(timestamp,name,idx,value) VALUES " - "(datetime('now'),%s.%s,%u,%"PRIu64")", - q_prefix, q_name, ctrg->idx, ctrg->ctr[num].current); - - free(q_name); - - if (!result) - return -EIO; - - dbi_result_free(result); - return 0; -} - -int db_store_rate_ctr_group(struct rate_ctr_group *ctrg) -{ - unsigned int i; - char *q_prefix; - - dbi_conn_quote_string_copy(conn, ctrg->desc->group_name_prefix, &q_prefix); - - for (i = 0; i < ctrg->desc->num_ctr; i++) - db_store_rate_ctr(ctrg, i, q_prefix); - - free(q_prefix); - - return 0; -} diff --git a/openbsc/src/libmsc/gsm_04_08.c b/openbsc/src/libmsc/gsm_04_08.c deleted file mode 100644 index 89108e46..00000000 --- a/openbsc/src/libmsc/gsm_04_08.c +++ /dev/null @@ -1,4047 +0,0 @@ -/* GSM Mobile Radio Interface Layer 3 messages on the A-bis interface - * 3GPP TS 04.08 version 7.21.0 Release 1998 / ETSI TS 100 940 V7.21.0 */ - -/* (C) 2008-2009 by Harald Welte - * (C) 2008-2012 by Holger Hans Peter Freyther - * - * All Rights Reserved - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation; either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - * - */ - - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "bscconfig.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -void *tall_locop_ctx; -void *tall_authciphop_ctx; - -static int tch_rtp_signal(struct gsm_lchan *lchan, int signal); - -static int gsm0408_loc_upd_acc(struct gsm_subscriber_connection *conn); -static int gsm48_tx_simple(struct gsm_subscriber_connection *conn, - uint8_t pdisc, uint8_t msg_type); -static void schedule_reject(struct gsm_subscriber_connection *conn); -static void release_anchor(struct gsm_subscriber_connection *conn); - -struct gsm_lai { - uint16_t mcc; - uint16_t mnc; - uint16_t lac; -}; - -static int apply_codec_restrictions(struct gsm_bts *bts, - struct gsm_mncc_bearer_cap *bcap) -{ - int i, j; - - /* remove unsupported speech versions from list */ - for (i = 0, j = 0; bcap->speech_ver[i] >= 0; i++) { - if (bcap->speech_ver[i] == GSM48_BCAP_SV_FR) - bcap->speech_ver[j++] = GSM48_BCAP_SV_FR; - if (bcap->speech_ver[i] == GSM48_BCAP_SV_EFR && bts->codec.efr) - bcap->speech_ver[j++] = GSM48_BCAP_SV_EFR; - if (bcap->speech_ver[i] == GSM48_BCAP_SV_AMR_F && bts->codec.amr) - bcap->speech_ver[j++] = GSM48_BCAP_SV_AMR_F; - if (bcap->speech_ver[i] == GSM48_BCAP_SV_HR && bts->codec.hr) - bcap->speech_ver[j++] = GSM48_BCAP_SV_HR; - if (bcap->speech_ver[i] == GSM48_BCAP_SV_AMR_H && bts->codec.amr) - bcap->speech_ver[j++] = GSM48_BCAP_SV_AMR_H; - } - bcap->speech_ver[j] = -1; - - return 0; -} - -static uint32_t new_callref = 0x80000001; - -void cc_tx_to_mncc(struct gsm_network *net, struct msgb *msg) -{ - net->mncc_recv(net, msg); -} - -static int gsm48_conn_sendmsg(struct msgb *msg, struct gsm_subscriber_connection *conn, - struct gsm_trans *trans) -{ - struct gsm48_hdr *gh = (struct gsm48_hdr *) msg->data; - - /* if we get passed a transaction reference, do some common - * work that the caller no longer has to do */ - if (trans) { - gh->proto_discr = trans->protocol | (trans->transaction_id << 4); - msg->lchan = trans->conn->lchan; - } - - if (msg->lchan) { - struct e1inp_sign_link *sign_link = - msg->lchan->ts->trx->rsl_link; - - msg->dst = sign_link; - if (gsm48_hdr_pdisc(gh) == GSM48_PDISC_CC) - DEBUGP(DCC, "(bts %d trx %d ts %d ti %02x) " - "Sending '%s' to MS.\n", - sign_link->trx->bts->nr, - sign_link->trx->nr, msg->lchan->ts->nr, - gh->proto_discr & 0xf0, - gsm48_cc_msg_name(gh->msg_type)); - else - DEBUGP(DCC, "(bts %d trx %d ts %d pd %02x) " - "Sending 0x%02x to MS.\n", - sign_link->trx->bts->nr, - sign_link->trx->nr, msg->lchan->ts->nr, - gh->proto_discr, gh->msg_type); - } - - return gsm0808_submit_dtap(conn, msg, 0, 0); -} - -int gsm48_cc_tx_notify_ss(struct gsm_trans *trans, const char *message) -{ - struct gsm48_hdr *gh; - struct msgb *ss_notify; - - ss_notify = gsm0480_create_notifySS(message); - if (!ss_notify) - return -1; - - gsm0480_wrap_invoke(ss_notify, GSM0480_OP_CODE_NOTIFY_SS, 0); - uint8_t *data = msgb_push(ss_notify, 1); - data[0] = ss_notify->len - 1; - gh = (struct gsm48_hdr *) msgb_push(ss_notify, sizeof(*gh)); - gh->msg_type = GSM48_MT_CC_FACILITY; - return gsm48_conn_sendmsg(ss_notify, trans->conn, trans); -} - -void release_security_operation(struct gsm_subscriber_connection *conn) -{ - if (!conn->sec_operation) - return; - - talloc_free(conn->sec_operation); - conn->sec_operation = NULL; - msc_release_connection(conn); -} - -void allocate_security_operation(struct gsm_subscriber_connection *conn) -{ - conn->sec_operation = talloc_zero(tall_authciphop_ctx, - struct gsm_security_operation); -} - -int gsm48_secure_channel(struct gsm_subscriber_connection *conn, int key_seq, - gsm_cbfn *cb, void *cb_data) -{ - struct gsm_network *net = conn->network; - struct gsm_subscriber *subscr = conn->subscr; - struct gsm_security_operation *op; - struct gsm_auth_tuple atuple; - int status = -1, rc; - - /* Check if we _can_ enable encryption. Cases where we can't: - * - Encryption disabled in config - * - Channel already secured (nothing to do) - * - Subscriber equipment doesn't support configured encryption - */ - if (!net->a5_encryption) { - status = GSM_SECURITY_NOAVAIL; - } else if (conn->lchan->encr.alg_id > RSL_ENC_ALG_A5(0)) { - DEBUGP(DMM, "Requesting to secure an already secure channel"); - status = GSM_SECURITY_ALREADY; - } else if (!ms_cm2_a5n_support(subscr->equipment.classmark2, - net->a5_encryption)) { - DEBUGP(DMM, "Subscriber equipment doesn't support requested encryption"); - status = GSM_SECURITY_NOAVAIL; - } - - /* If not done yet, try to get info for this user */ - if (status < 0) { - rc = auth_get_tuple_for_subscr(&atuple, subscr, key_seq); - if (rc <= 0) - status = GSM_SECURITY_NOAVAIL; - } - - /* Are we done yet ? */ - if (status >= 0) - return cb ? - cb(GSM_HOOK_RR_SECURITY, status, NULL, conn, cb_data) : - 0; - - /* Start an operation (can't have more than one pending !!!) */ - if (conn->sec_operation) - return -EBUSY; - - allocate_security_operation(conn); - op = conn->sec_operation; - op->cb = cb; - op->cb_data = cb_data; - memcpy(&op->atuple, &atuple, sizeof(struct gsm_auth_tuple)); - - /* FIXME: Should start a timer for completion ... */ - - /* Then do whatever is needed ... */ - if (rc == AUTH_DO_AUTH_THEN_CIPH) { - /* Start authentication */ - return gsm48_tx_mm_auth_req(conn, op->atuple.vec.rand, NULL, - op->atuple.key_seq); - } else if (rc == AUTH_DO_CIPH) { - /* Start ciphering directly */ - return gsm0808_cipher_mode(conn, net->a5_encryption, - op->atuple.vec.kc, 8, 0); - } - - return -EINVAL; /* not reached */ -} - -static bool subscr_regexp_check(const struct gsm_network *net, const char *imsi) -{ - if (!net->authorized_reg_str) - return false; - - if (regexec(&net->authorized_regexp, imsi, 0, NULL, 0) != REG_NOMATCH) - return true; - - return false; -} - -static int authorize_subscriber(struct gsm_loc_updating_operation *loc, - struct gsm_subscriber *subscriber) -{ - if (!subscriber) - return 0; - - /* - * Do not send accept yet as more information should arrive. Some - * phones will not send us the information and we will have to check - * what we want to do with that. - */ - if (loc && (loc->waiting_for_imsi || loc->waiting_for_imei)) - return 0; - - switch (subscriber->group->net->auth_policy) { - case GSM_AUTH_POLICY_CLOSED: - return subscriber->authorized; - case GSM_AUTH_POLICY_REGEXP: - if (subscriber->authorized) - return 1; - if (subscr_regexp_check(subscriber->group->net, - subscriber->imsi)) - subscriber->authorized = 1; - return subscriber->authorized; - case GSM_AUTH_POLICY_TOKEN: - if (subscriber->authorized) - return subscriber->authorized; - return (subscriber->flags & GSM_SUBSCRIBER_FIRST_CONTACT); - case GSM_AUTH_POLICY_ACCEPT_ALL: - return 1; - default: - return 0; - } -} - -static void _release_loc_updating_req(struct gsm_subscriber_connection *conn, int release) -{ - if (!conn->loc_operation) - return; - - /* No need to keep the connection up */ - release_anchor(conn); - - osmo_timer_del(&conn->loc_operation->updating_timer); - talloc_free(conn->loc_operation); - conn->loc_operation = NULL; - if (release) - msc_release_connection(conn); -} - -static void loc_updating_failure(struct gsm_subscriber_connection *conn, int release) -{ - if (!conn->loc_operation) - return; - LOGP(DMM, LOGL_ERROR, "Location Updating failed for %s\n", - subscr_name(conn->subscr)); - rate_ctr_inc(&conn->network->msc_ctrs->ctr[MSC_CTR_LOC_UPDATE_FAILED]); - _release_loc_updating_req(conn, release); -} - -static void loc_updating_success(struct gsm_subscriber_connection *conn, int release) -{ - if (!conn->loc_operation) - return; - LOGP(DMM, LOGL_INFO, "Location Updating completed for %s\n", - subscr_name(conn->subscr)); - rate_ctr_inc(&conn->network->msc_ctrs->ctr[MSC_CTR_LOC_UPDATE_COMPLETED]); - _release_loc_updating_req(conn, release); -} - -static void allocate_loc_updating_req(struct gsm_subscriber_connection *conn) -{ - if (conn->loc_operation) - LOGP(DMM, LOGL_ERROR, "Connection already had operation.\n"); - loc_updating_failure(conn, 0); - - conn->loc_operation = talloc_zero(tall_locop_ctx, - struct gsm_loc_updating_operation); -} - -static int finish_lu(struct gsm_subscriber_connection *conn) -{ - int rc = 0; - int avoid_tmsi = conn->network->avoid_tmsi; - - /* We're all good */ - if (avoid_tmsi) { - conn->subscr->tmsi = GSM_RESERVED_TMSI; - db_sync_subscriber(conn->subscr); - } else { - db_subscriber_alloc_tmsi(conn->subscr); - } - - rc = gsm0408_loc_upd_acc(conn); - if (conn->network->send_mm_info) { - /* send MM INFO with network name */ - rc = gsm48_tx_mm_info(conn); - } - - /* call subscr_update after putting the loc_upd_acc - * in the transmit queue, since S_SUBSCR_ATTACHED might - * trigger further action like SMS delivery */ - subscr_update(conn->subscr, conn->bts, - GSM_SUBSCRIBER_UPDATE_ATTACHED); - - /* - * The gsm0408_loc_upd_acc sends a MI with the TMSI. The - * MS needs to respond with a TMSI REALLOCATION COMPLETE - * (even if the TMSI is the same). - * If avoid_tmsi == true, we don't send a TMSI, we don't - * expect a reply and Location Updating is done. - */ - if (avoid_tmsi) - loc_updating_success(conn, 1); - - return rc; -} - -static int _gsm0408_authorize_sec_cb(unsigned int hooknum, unsigned int event, - struct msgb *msg, void *data, void *param) -{ - struct gsm_subscriber_connection *conn = data; - int rc = 0; - - switch (event) { - case GSM_SECURITY_AUTH_FAILED: - loc_updating_failure(conn, 1); - break; - - case GSM_SECURITY_ALREADY: - LOGP(DMM, LOGL_ERROR, "We don't expect LOCATION " - "UPDATING after CM SERVICE REQUEST\n"); - /* fall through */ - - case GSM_SECURITY_NOAVAIL: - case GSM_SECURITY_SUCCEEDED: - rc = finish_lu(conn); - break; - - default: - rc = -EINVAL; - }; - - return rc; -} - -static int gsm0408_authorize(struct gsm_subscriber_connection *conn, struct msgb *msg) -{ - if (!conn->loc_operation) - return 0; - - if (authorize_subscriber(conn->loc_operation, conn->subscr)) - return gsm48_secure_channel(conn, - conn->loc_operation->key_seq, - _gsm0408_authorize_sec_cb, NULL); - return 0; -} - -void gsm0408_clear_request(struct gsm_subscriber_connection *conn, uint32_t cause) -{ - struct gsm_trans *trans, *temp; - - /* avoid someone issuing a clear */ - conn->in_release = 1; - - /* - * Cancel any outstanding location updating request - * operation taking place on the subscriber connection. - */ - loc_updating_failure(conn, 0); - - /* We might need to cancel the paging response or such. */ - if (conn->sec_operation && conn->sec_operation->cb) { - conn->sec_operation->cb(GSM_HOOK_RR_SECURITY, GSM_SECURITY_AUTH_FAILED, - NULL, conn, conn->sec_operation->cb_data); - } - - release_security_operation(conn); - release_anchor(conn); - - /* - * Free all transactions that are associated with the released - * connection. The transaction code will inform the CC or SMS - * facilities that will send the release indications. As part of - * the CC REL_IND the remote leg might be released and this will - * trigger the call to trans_free. This is something the llist - * macro can not handle and we will need to re-iterate the list. - * - * TODO: Move the trans_list into the subscriber connection and - * create a pending list for MT transactions. These exist before - * we have a subscriber connection. - */ -restart: - llist_for_each_entry_safe(trans, temp, &conn->network->trans_list, entry) { - if (trans->conn == conn) { - trans_free(trans); - goto restart; - } - } -} - -void gsm0408_clear_all_trans(struct gsm_network *net, int protocol) -{ - struct gsm_trans *trans, *temp; - - LOGP(DCC, LOGL_NOTICE, "Clearing all currently active transactions!!!\n"); - - llist_for_each_entry_safe(trans, temp, &net->trans_list, entry) { - if (trans->protocol == protocol) { - trans->callref = 0; - trans_free(trans); - } - } -} - -/* Chapter 9.2.14 : Send LOCATION UPDATING REJECT */ -int gsm0408_loc_upd_rej(struct gsm_subscriber_connection *conn, uint8_t cause) -{ - struct gsm_bts *bts = conn->bts; - struct msgb *msg; - - msg = gsm48_create_loc_upd_rej(cause); - if (!msg) { - LOGP(DMM, LOGL_ERROR, "Failed to create msg for LOCATION UPDATING REJECT.\n"); - return -1; - } - - msg->lchan = conn->lchan; - - LOGP(DMM, LOGL_INFO, "Subscriber %s: LOCATION UPDATING REJECT " - "LAC=%u BTS=%u\n", subscr_name(conn->subscr), - bts->location_area_code, bts->nr); - - return gsm48_conn_sendmsg(msg, conn, NULL); -} - -/* Chapter 9.2.13 : Send LOCATION UPDATE ACCEPT */ -static int gsm0408_loc_upd_acc(struct gsm_subscriber_connection *conn) -{ - struct msgb *msg = gsm48_msgb_alloc_name("GSM 04.08 LOC UPD ACC"); - struct gsm48_hdr *gh; - struct gsm48_loc_area_id *lai; - uint8_t *mid; - - msg->lchan = conn->lchan; - - gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh)); - gh->proto_discr = GSM48_PDISC_MM; - gh->msg_type = GSM48_MT_MM_LOC_UPD_ACCEPT; - - lai = (struct gsm48_loc_area_id *) msgb_put(msg, sizeof(*lai)); - gsm48_generate_lai(lai, conn->network->country_code, - conn->network->network_code, - conn->bts->location_area_code); - - if (conn->subscr->tmsi == GSM_RESERVED_TMSI) { - uint8_t mi[10]; - int len; - len = gsm48_generate_mid_from_imsi(mi, conn->subscr->imsi); - mid = msgb_put(msg, len); - memcpy(mid, mi, len); - } else { - mid = msgb_put(msg, GSM48_MID_TMSI_LEN); - gsm48_generate_mid_from_tmsi(mid, conn->subscr->tmsi); - } - - DEBUGP(DMM, "-> LOCATION UPDATE ACCEPT\n"); - - return gsm48_conn_sendmsg(msg, conn, NULL); -} - -/* Transmit Chapter 9.2.10 Identity Request */ -static int mm_tx_identity_req(struct gsm_subscriber_connection *conn, uint8_t id_type) -{ - struct msgb *msg = gsm48_msgb_alloc_name("GSM 04.08 ID REQ"); - struct gsm48_hdr *gh; - - msg->lchan = conn->lchan; - - gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh) + 1); - gh->proto_discr = GSM48_PDISC_MM; - gh->msg_type = GSM48_MT_MM_ID_REQ; - gh->data[0] = id_type; - - return gsm48_conn_sendmsg(msg, conn, NULL); -} - -static struct gsm_subscriber *subscr_create(const struct gsm_network *net, - const char *imsi) -{ - if (!net->auto_create_subscr) - return NULL; - - if (!subscr_regexp_check(net, imsi)) - return NULL; - - return subscr_create_subscriber(net->subscr_group, imsi); -} - -/* Parse Chapter 9.2.11 Identity Response */ -static int mm_rx_id_resp(struct gsm_subscriber_connection *conn, struct msgb *msg) -{ - struct gsm48_hdr *gh = msgb_l3(msg); - struct gsm_network *net = conn->network; - uint8_t mi_type = gh->data[1] & GSM_MI_TYPE_MASK; - char mi_string[GSM48_MI_SIZE]; - - gsm48_mi_to_string(mi_string, sizeof(mi_string), &gh->data[1], gh->data[0]); - DEBUGP(DMM, "IDENTITY RESPONSE: MI(%s)=%s\n", - gsm48_mi_type_name(mi_type), mi_string); - - osmo_signal_dispatch(SS_SUBSCR, S_SUBSCR_IDENTITY, gh->data); - - switch (mi_type) { - case GSM_MI_TYPE_IMSI: - /* look up subscriber based on IMSI, create if not found */ - if (!conn->subscr) { - conn->subscr = subscr_get_by_imsi(net->subscr_group, - mi_string); - if (!conn->subscr) - conn->subscr = subscr_create(net, mi_string); - } - if (!conn->subscr && conn->loc_operation) { - gsm0408_loc_upd_rej(conn, net->reject_cause); - loc_updating_failure(conn, 1); - return 0; - } - if (conn->loc_operation) - conn->loc_operation->waiting_for_imsi = 0; - break; - case GSM_MI_TYPE_IMEI: - case GSM_MI_TYPE_IMEISV: - /* update subscribe <-> IMEI mapping */ - if (conn->subscr) { - db_subscriber_assoc_imei(conn->subscr, mi_string); - db_sync_equipment(&conn->subscr->equipment); - } - if (conn->loc_operation) - conn->loc_operation->waiting_for_imei = 0; - break; - } - - /* Check if we can let the mobile station enter */ - return gsm0408_authorize(conn, msg); -} - - -static void loc_upd_rej_cb(void *data) -{ - struct gsm_subscriber_connection *conn = data; - - LOGP(DMM, LOGL_DEBUG, "Location Updating Request procedure timedout.\n"); - gsm0408_loc_upd_rej(conn, conn->network->reject_cause); - loc_updating_failure(conn, 1); -} - -static void schedule_reject(struct gsm_subscriber_connection *conn) -{ - osmo_timer_setup(&conn->loc_operation->updating_timer, loc_upd_rej_cb, - conn); - osmo_timer_schedule(&conn->loc_operation->updating_timer, 5, 0); -} - -static const struct value_string lupd_names[] = { - { GSM48_LUPD_NORMAL, "NORMAL" }, - { GSM48_LUPD_PERIODIC, "PERIODIC" }, - { GSM48_LUPD_IMSI_ATT, "IMSI ATTACH" }, - { 0, NULL } -}; - -/* Chapter 9.2.15: Receive Location Updating Request */ -static int mm_rx_loc_upd_req(struct gsm_subscriber_connection *conn, struct msgb *msg) -{ - struct gsm48_hdr *gh = msgb_l3(msg); - struct gsm48_loc_upd_req *lu; - struct gsm_subscriber *subscr = NULL; - uint8_t mi_type; - char mi_string[GSM48_MI_SIZE]; - - lu = (struct gsm48_loc_upd_req *) gh->data; - - mi_type = lu->mi[0] & GSM_MI_TYPE_MASK; - - gsm48_mi_to_string(mi_string, sizeof(mi_string), lu->mi, lu->mi_len); - - DEBUGPC(DMM, "MI(%s)=%s type=%s ", gsm48_mi_type_name(mi_type), - mi_string, get_value_string(lupd_names, lu->type)); - - osmo_signal_dispatch(SS_SUBSCR, S_SUBSCR_IDENTITY, &lu->mi_len); - - switch (lu->type) { - case GSM48_LUPD_NORMAL: - rate_ctr_inc(&conn->network->msc_ctrs->ctr[MSC_CTR_LOC_UPDATE_TYPE_NORMAL]); - break; - case GSM48_LUPD_IMSI_ATT: - rate_ctr_inc(&conn->network->msc_ctrs->ctr[MSC_CTR_LOC_UPDATE_TYPE_ATTACH]); - break; - case GSM48_LUPD_PERIODIC: - rate_ctr_inc(&conn->network->msc_ctrs->ctr[MSC_CTR_LOC_UPDATE_TYPE_PERIODIC]); - break; - } - - /* - * Pseudo Spoof detection: Just drop a second/concurrent - * location updating request. - */ - if (conn->loc_operation) { - DEBUGPC(DMM, "ignoring request due an existing one: %p.\n", - conn->loc_operation); - gsm0408_loc_upd_rej(conn, GSM48_REJECT_PROTOCOL_ERROR); - return 0; - } - - allocate_loc_updating_req(conn); - - conn->loc_operation->key_seq = lu->key_seq; - - switch (mi_type) { - case GSM_MI_TYPE_IMSI: - DEBUGPC(DMM, "\n"); - /* we always want the IMEI, too */ - mm_tx_identity_req(conn, GSM_MI_TYPE_IMEI); - conn->loc_operation->waiting_for_imei = 1; - - /* look up subscriber based on IMSI, create if not found */ - subscr = subscr_get_by_imsi(conn->network->subscr_group, mi_string); - if (!subscr) - subscr = subscr_create(conn->network, mi_string); - if (!subscr) { - gsm0408_loc_upd_rej(conn, conn->network->reject_cause); - loc_updating_failure(conn, 0); /* FIXME: set release == true? */ - return 0; - } - break; - case GSM_MI_TYPE_TMSI: - DEBUGPC(DMM, "\n"); - /* look up the subscriber based on TMSI, request IMSI if it fails */ - subscr = subscr_get_by_tmsi(conn->network->subscr_group, - tmsi_from_string(mi_string)); - if (!subscr) { - /* send IDENTITY REQUEST message to get IMSI */ - mm_tx_identity_req(conn, GSM_MI_TYPE_IMSI); - conn->loc_operation->waiting_for_imsi = 1; - } - /* we always want the IMEI, too */ - mm_tx_identity_req(conn, GSM_MI_TYPE_IMEI); - conn->loc_operation->waiting_for_imei = 1; - break; - case GSM_MI_TYPE_IMEI: - case GSM_MI_TYPE_IMEISV: - /* no sim card... FIXME: what to do ? */ - DEBUGPC(DMM, "unimplemented mobile identity type\n"); - break; - default: - DEBUGPC(DMM, "unknown mobile identity type\n"); - break; - } - - /* schedule the reject timer */ - schedule_reject(conn); - - if (!subscr) { - DEBUGPC(DRR, "<- Can't find any subscriber for this ID\n"); - /* FIXME: request id? close channel? */ - return -EINVAL; - } - - conn->subscr = subscr; - conn->subscr->equipment.classmark1 = lu->classmark1; - - /* check if we can let the subscriber into our network immediately - * or if we need to wait for identity responses. */ - return gsm0408_authorize(conn, msg); -} - -/* Turn int into semi-octet representation: 98 => 0x89 */ -static uint8_t bcdify(uint8_t value) -{ - uint8_t ret; - - ret = value / 10; - ret |= (value % 10) << 4; - - return ret; -} - - -/* Section 9.2.15a */ -int gsm48_tx_mm_info(struct gsm_subscriber_connection *conn) -{ - struct msgb *msg = gsm48_msgb_alloc_name("GSM 04.08 MM INF"); - struct gsm48_hdr *gh; - struct gsm_network *net = conn->network; - uint8_t *ptr8; - int name_len, name_pad; - - time_t cur_t; - struct tm* gmt_time; - struct tm* local_time; - int tzunits; - int dst = 0; - - msg->lchan = conn->lchan; - - gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh)); - gh->proto_discr = GSM48_PDISC_MM; - gh->msg_type = GSM48_MT_MM_INFO; - - if (net->name_long) { -#if 0 - name_len = strlen(net->name_long); - /* 10.5.3.5a */ - ptr8 = msgb_put(msg, 3); - ptr8[0] = GSM48_IE_NAME_LONG; - ptr8[1] = name_len*2 +1; - ptr8[2] = 0x90; /* UCS2, no spare bits, no CI */ - - ptr16 = (uint16_t *) msgb_put(msg, name_len*2); - for (i = 0; i < name_len; i++) - ptr16[i] = htons(net->name_long[i]); - - /* FIXME: Use Cell Broadcast, not UCS-2, since - * UCS-2 is only supported by later revisions of the spec */ -#endif - name_len = (strlen(net->name_long)*7)/8; - name_pad = (8 - strlen(net->name_long)*7)%8; - if (name_pad > 0) - name_len++; - /* 10.5.3.5a */ - ptr8 = msgb_put(msg, 3); - ptr8[0] = GSM48_IE_NAME_LONG; - ptr8[1] = name_len +1; - ptr8[2] = 0x80 | name_pad; /* Cell Broadcast DCS, no CI */ - - ptr8 = msgb_put(msg, name_len); - gsm_7bit_encode_n(ptr8, name_len, net->name_long, NULL); - - } - - if (net->name_short) { -#if 0 - name_len = strlen(net->name_short); - /* 10.5.3.5a */ - ptr8 = (uint8_t *) msgb_put(msg, 3); - ptr8[0] = GSM48_IE_NAME_SHORT; - ptr8[1] = name_len*2 + 1; - ptr8[2] = 0x90; /* UCS2, no spare bits, no CI */ - - ptr16 = (uint16_t *) msgb_put(msg, name_len*2); - for (i = 0; i < name_len; i++) - ptr16[i] = htons(net->name_short[i]); -#endif - name_len = (strlen(net->name_short)*7)/8; - name_pad = (8 - strlen(net->name_short)*7)%8; - if (name_pad > 0) - name_len++; - /* 10.5.3.5a */ - ptr8 = (uint8_t *) msgb_put(msg, 3); - ptr8[0] = GSM48_IE_NAME_SHORT; - ptr8[1] = name_len +1; - ptr8[2] = 0x80 | name_pad; /* Cell Broadcast DCS, no CI */ - - ptr8 = msgb_put(msg, name_len); - gsm_7bit_encode_n(ptr8, name_len, net->name_short, NULL); - - } - - /* Section 10.5.3.9 */ - cur_t = time(NULL); - gmt_time = gmtime(&cur_t); - - ptr8 = msgb_put(msg, 8); - ptr8[0] = GSM48_IE_NET_TIME_TZ; - ptr8[1] = bcdify(gmt_time->tm_year % 100); - ptr8[2] = bcdify(gmt_time->tm_mon + 1); - ptr8[3] = bcdify(gmt_time->tm_mday); - ptr8[4] = bcdify(gmt_time->tm_hour); - ptr8[5] = bcdify(gmt_time->tm_min); - ptr8[6] = bcdify(gmt_time->tm_sec); - - if (net->tz.override) { - /* Convert tz.hr and tz.mn to units */ - if (net->tz.hr < 0) { - tzunits = ((net->tz.hr/-1)*4); - tzunits = tzunits + (net->tz.mn/15); - ptr8[7] = bcdify(tzunits); - /* Set negative time */ - ptr8[7] |= 0x08; - } - else { - tzunits = net->tz.hr*4; - tzunits = tzunits + (net->tz.mn/15); - ptr8[7] = bcdify(tzunits); - } - /* Convert DST value */ - if (net->tz.dst >= 0 && net->tz.dst <= 2) - dst = net->tz.dst; - } - else { - /* Need to get GSM offset and convert into 15 min units */ - /* This probably breaks if gmtoff returns a value not evenly divisible by 15? */ - local_time = localtime(&cur_t); -#ifdef HAVE_TM_GMTOFF_IN_TM - tzunits = (local_time->tm_gmtoff/60)/15; -#else -#warning find a portable way to obtain the timezone offset - tzunits = 0; -#endif - if (tzunits < 0) { - tzunits = tzunits/-1; - ptr8[7] = bcdify(tzunits); - /* Flip it to negative */ - ptr8[7] |= 0x08; - } - else - ptr8[7] = bcdify(tzunits); - - /* Does not support DST +2 */ - if (local_time->tm_isdst) - dst = 1; - } - - if (dst) { - ptr8 = msgb_put(msg, 3); - ptr8[0] = GSM48_IE_NET_DST; - ptr8[1] = 1; - ptr8[2] = dst; - } - - DEBUGP(DMM, "-> MM INFO\n"); - - return gsm48_conn_sendmsg(msg, conn, NULL); -} - -/*! Send an Authentication Request to MS on the given subscriber connection - * according to 3GPP/ETSI TS 24.008, Section 9.2.2. - * \param[in] conn Subscriber connection to send on. - * \param[in] rand Random challenge token to send, must be 16 bytes long. - * \param[in] autn r99: In case of UMTS mutual authentication, AUTN token to - * send; must be 16 bytes long, or pass NULL for plain GSM auth. - * \param[in] key_seq auth tuple's sequence number. - */ -int gsm48_tx_mm_auth_req(struct gsm_subscriber_connection *conn, uint8_t *rand, - uint8_t *autn, int key_seq) -{ - struct msgb *msg = gsm48_msgb_alloc_name("GSM 04.08 AUTH REQ"); - struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh)); - struct gsm48_auth_req *ar = (struct gsm48_auth_req *) msgb_put(msg, sizeof(*ar)); - - DEBUGP(DMM, "-> AUTH REQ (rand = %s)\n", osmo_hexdump(rand, 16)); - if (autn) - DEBUGP(DMM, " AUTH REQ (autn = %s)\n", osmo_hexdump(autn, 16)); - - msg->lchan = conn->lchan; - gh->proto_discr = GSM48_PDISC_MM; - gh->msg_type = GSM48_MT_MM_AUTH_REQ; - - ar->key_seq = key_seq; - - /* 16 bytes RAND parameters */ - osmo_static_assert(sizeof(ar->rand) == 16, sizeof_auth_req_r99_rand); - if (rand) - memcpy(ar->rand, rand, 16); - - - /* 16 bytes AUTN */ - if (autn) - msgb_tlv_put(msg, GSM48_IE_AUTN, 16, autn); - - return gsm48_conn_sendmsg(msg, conn, NULL); -} - -/* Section 9.2.1 */ -int gsm48_tx_mm_auth_rej(struct gsm_subscriber_connection *conn) -{ - DEBUGP(DMM, "-> AUTH REJECT\n"); - return gsm48_tx_simple(conn, GSM48_PDISC_MM, GSM48_MT_MM_AUTH_REJ); -} - -/* - * At the 30C3 phones miss their periodic update - * interval a lot and then remain unreachable. In case - * we still know the TMSI we can just attach it again. - */ -static void implit_attach(struct gsm_subscriber_connection *conn) -{ - if (conn->subscr->lac != GSM_LAC_RESERVED_DETACHED) - return; - - subscr_update(conn->subscr, conn->bts, - GSM_SUBSCRIBER_UPDATE_ATTACHED); -} - - -static int _gsm48_rx_mm_serv_req_sec_cb( - unsigned int hooknum, unsigned int event, - struct msgb *msg, void *data, void *param) -{ - struct gsm_subscriber_connection *conn = data; - int rc = 0; - - /* auth failed or succeeded, the timer was stopped */ - conn->expire_timer_stopped = 1; - - switch (event) { - case GSM_SECURITY_AUTH_FAILED: - /* Nothing to do */ - break; - - case GSM_SECURITY_NOAVAIL: - case GSM_SECURITY_ALREADY: - rc = gsm48_tx_mm_serv_ack(conn); - implit_attach(conn); - break; - - case GSM_SECURITY_SUCCEEDED: - /* nothing to do. CIPHER MODE COMMAND is - * implicit CM SERV ACK */ - implit_attach(conn); - break; - - default: - rc = -EINVAL; - }; - - return rc; -} - -/* - * Handle CM Service Requests - * a) Verify that the packet is long enough to contain the information - * we require otherwsie reject with INCORRECT_MESSAGE - * b) Try to parse the TMSI. If we do not have one reject - * c) Check that we know the subscriber with the TMSI otherwise reject - * with a HLR cause - * d) Set the subscriber on the gsm_lchan and accept - */ -static int gsm48_rx_mm_serv_req(struct gsm_subscriber_connection *conn, struct msgb *msg) -{ - uint8_t mi_type; - char mi_string[GSM48_MI_SIZE]; - - struct gsm_network *network = conn->network; - struct gsm_subscriber *subscr; - struct gsm48_hdr *gh = msgb_l3(msg); - struct gsm48_service_request *req = - (struct gsm48_service_request *)gh->data; - /* unfortunately in Phase1 the classmark2 length is variable */ - uint8_t classmark2_len = gh->data[1]; - uint8_t *classmark2 = gh->data+2; - uint8_t mi_len = *(classmark2 + classmark2_len); - uint8_t *mi = (classmark2 + classmark2_len + 1); - - DEBUGP(DMM, "<- CM SERVICE REQUEST "); - if (msg->data_len < sizeof(struct gsm48_service_request*)) { - DEBUGPC(DMM, "wrong sized message\n"); - return gsm48_tx_mm_serv_rej(conn, - GSM48_REJECT_INCORRECT_MESSAGE); - } - - if (msg->data_len < req->mi_len + 6) { - DEBUGPC(DMM, "does not fit in packet\n"); - return gsm48_tx_mm_serv_rej(conn, - GSM48_REJECT_INCORRECT_MESSAGE); - } - - gsm48_mi_to_string(mi_string, sizeof(mi_string), mi, mi_len); - mi_type = mi[0] & GSM_MI_TYPE_MASK; - - if (mi_type == GSM_MI_TYPE_IMSI) { - DEBUGPC(DMM, "serv_type=0x%02x MI(%s)=%s\n", - req->cm_service_type, gsm48_mi_type_name(mi_type), - mi_string); - subscr = subscr_get_by_imsi(network->subscr_group, - mi_string); - } else if (mi_type == GSM_MI_TYPE_TMSI) { - DEBUGPC(DMM, "serv_type=0x%02x MI(%s)=%s\n", - req->cm_service_type, gsm48_mi_type_name(mi_type), - mi_string); - subscr = subscr_get_by_tmsi(network->subscr_group, - tmsi_from_string(mi_string)); - } else { - DEBUGPC(DMM, "mi_type is not expected: %d\n", mi_type); - return gsm48_tx_mm_serv_rej(conn, - GSM48_REJECT_INCORRECT_MESSAGE); - } - - osmo_signal_dispatch(SS_SUBSCR, S_SUBSCR_IDENTITY, (classmark2 + classmark2_len)); - - if (is_siemens_bts(conn->bts)) - send_siemens_mrpci(msg->lchan, classmark2-1); - - - /* FIXME: if we don't know the TMSI, inquire abit IMSI and allocate new TMSI */ - if (!subscr) - return gsm48_tx_mm_serv_rej(conn, - GSM48_REJECT_IMSI_UNKNOWN_IN_VLR); - - if (!conn->subscr) - conn->subscr = subscr; - else if (conn->subscr == subscr) - subscr_put(subscr); /* lchan already has a ref, don't need another one */ - else { - DEBUGP(DMM, "<- CM Channel already owned by someone else?\n"); - subscr_put(subscr); - } - - subscr->equipment.classmark2_len = classmark2_len; - memcpy(subscr->equipment.classmark2, classmark2, classmark2_len); - db_sync_equipment(&subscr->equipment); - - /* we will send a MM message soon */ - conn->expire_timer_stopped = 1; - - return gsm48_secure_channel(conn, req->cipher_key_seq, - _gsm48_rx_mm_serv_req_sec_cb, NULL); -} - -static int gsm48_rx_mm_imsi_detach_ind(struct gsm_subscriber_connection *conn, struct msgb *msg) -{ - struct gsm_network *network = conn->network; - struct gsm48_hdr *gh = msgb_l3(msg); - struct gsm48_imsi_detach_ind *idi = - (struct gsm48_imsi_detach_ind *) gh->data; - uint8_t mi_type = idi->mi[0] & GSM_MI_TYPE_MASK; - char mi_string[GSM48_MI_SIZE]; - struct gsm_subscriber *subscr = NULL; - - gsm48_mi_to_string(mi_string, sizeof(mi_string), idi->mi, idi->mi_len); - DEBUGP(DMM, "IMSI DETACH INDICATION: MI(%s)=%s", - gsm48_mi_type_name(mi_type), mi_string); - - rate_ctr_inc(&network->msc_ctrs->ctr[MSC_CTR_LOC_UPDATE_TYPE_DETACH]); - - switch (mi_type) { - case GSM_MI_TYPE_TMSI: - DEBUGPC(DMM, "\n"); - subscr = subscr_get_by_tmsi(network->subscr_group, - tmsi_from_string(mi_string)); - break; - case GSM_MI_TYPE_IMSI: - DEBUGPC(DMM, "\n"); - subscr = subscr_get_by_imsi(network->subscr_group, - mi_string); - break; - case GSM_MI_TYPE_IMEI: - case GSM_MI_TYPE_IMEISV: - /* no sim card... FIXME: what to do ? */ - DEBUGPC(DMM, ": unimplemented mobile identity type\n"); - break; - default: - DEBUGPC(DMM, ": unknown mobile identity type\n"); - break; - } - - if (subscr) { - subscr_update(subscr, conn->bts, - GSM_SUBSCRIBER_UPDATE_DETACHED); - DEBUGP(DMM, "Subscriber: %s\n", subscr_name(subscr)); - - subscr->equipment.classmark1 = idi->classmark1; - db_sync_equipment(&subscr->equipment); - - subscr_put(subscr); - } else - DEBUGP(DMM, "Unknown Subscriber ?!?\n"); - - /* FIXME: iterate over all transactions and release them, - * imagine an IMSI DETACH happening during an active call! */ - - release_anchor(conn); - return 0; -} - -static int gsm48_rx_mm_status(struct msgb *msg) -{ - struct gsm48_hdr *gh = msgb_l3(msg); - - DEBUGP(DMM, "MM STATUS (reject cause 0x%02x)\n", gh->data[0]); - - return 0; -} - -static int parse_gsm_auth_resp(uint8_t *res, uint8_t *res_len, - struct gsm_subscriber_connection *conn, - struct msgb *msg) -{ - struct gsm48_hdr *gh = msgb_l3(msg); - struct gsm48_auth_resp *ar = (struct gsm48_auth_resp*) gh->data; - - if (msgb_l3len(msg) < sizeof(*gh) + sizeof(*ar)) { - LOGP(DMM, LOGL_ERROR, - "%s: MM AUTHENTICATION RESPONSE:" - " l3 length invalid: %u\n", - subscr_name(conn->subscr), msgb_l3len(msg)); - return -EINVAL; - } - - *res_len = sizeof(ar->sres); - memcpy(res, ar->sres, sizeof(ar->sres)); - return 0; -} - -static int parse_umts_auth_resp(uint8_t *res, uint8_t *res_len, - struct gsm_subscriber_connection *conn, - struct msgb *msg) -{ - struct gsm48_hdr *gh; - uint8_t *data; - uint8_t iei; - uint8_t ie_len; - unsigned int data_len; - - /* First parse the GSM part */ - if (parse_gsm_auth_resp(res, res_len, conn, msg)) - return -EINVAL; - OSMO_ASSERT(*res_len == 4); - - /* Then add the extended res part */ - gh = msgb_l3(msg); - data = gh->data + sizeof(struct gsm48_auth_resp); - data_len = msgb_l3len(msg) - (data - (uint8_t*)msgb_l3(msg)); - - if (data_len < 3) { - LOGP(DMM, LOGL_ERROR, - "%s: MM AUTHENTICATION RESPONSE:" - " l3 length invalid: %u\n", - subscr_name(conn->subscr), msgb_l3len(msg)); - return -EINVAL; - } - - iei = data[0]; - ie_len = data[1]; - if (iei != GSM48_IE_AUTH_RES_EXT) { - LOGP(DMM, LOGL_ERROR, - "%s: MM R99 AUTHENTICATION RESPONSE:" - " expected IEI 0x%02x, got 0x%02x\n", - subscr_name(conn->subscr), - GSM48_IE_AUTH_RES_EXT, iei); - return -EINVAL; - } - - if (ie_len > 12) { - LOGP(DMM, LOGL_ERROR, - "%s: MM R99 AUTHENTICATION RESPONSE:" - " extended Auth Resp IE 0x%02x is too large: %u bytes\n", - subscr_name(conn->subscr), GSM48_IE_AUTH_RES_EXT, ie_len); - return -EINVAL; - } - - *res_len += ie_len; - memcpy(res + 4, &data[2], ie_len); - return 0; -} - -/* Chapter 9.2.3: Authentication Response */ -static int gsm48_rx_mm_auth_resp(struct gsm_subscriber_connection *conn, struct msgb *msg) -{ - struct gsm_network *net = conn->network; - uint8_t res[16]; - uint8_t res_len; - int rc; - bool is_r99; - - if (!conn->subscr) { - LOGP(DMM, LOGL_ERROR, - "MM AUTHENTICATION RESPONSE: invalid: no subscriber\n"); - gsm48_tx_mm_auth_rej(conn); - release_security_operation(conn); - return -EINVAL; - } - - if (msgb_l3len(msg) > - sizeof(struct gsm48_hdr) + sizeof(struct gsm48_auth_resp)) { - rc = parse_umts_auth_resp(res, &res_len, conn, msg); - is_r99 = true; - } else { - rc = parse_gsm_auth_resp(res, &res_len, conn, msg); - is_r99 = false; - } - - if (rc) { - gsm48_tx_mm_auth_rej(conn); - release_security_operation(conn); - return -EINVAL; - } - - DEBUGP(DMM, "%s: MM %s AUTHENTICATION RESPONSE (%s = %s)\n", - subscr_name(conn->subscr), - is_r99 ? "R99" : "GSM", is_r99 ? "res" : "sres", - osmo_hexdump_nospc(res, res_len)); - - /* Future: vlr_sub_rx_auth_resp(conn->vsub, is_r99, - * conn->via_ran == RAN_UTRAN_IU, - * res, res_len); - */ - - if (res_len != 4) { - LOGP(DMM, LOGL_ERROR, - "%s: MM AUTHENTICATION RESPONSE:" - " UMTS authentication not supported\n", - subscr_name(conn->subscr)); - } - - /* Safety check */ - if (!conn->sec_operation) { - DEBUGP(DMM, "No authentication/cipher operation in progress !!!\n"); - return -EIO; - } - - /* Validate SRES */ - if (memcmp(conn->sec_operation->atuple.vec.sres, res, 4)) { - int rc; - gsm_cbfn *cb = conn->sec_operation->cb; - - DEBUGPC(DMM, "Invalid (expected %s)\n", - osmo_hexdump(conn->sec_operation->atuple.vec.sres, 4)); - - if (cb) - cb(GSM_HOOK_RR_SECURITY, GSM_SECURITY_AUTH_FAILED, - NULL, conn, conn->sec_operation->cb_data); - - rc = gsm48_tx_mm_auth_rej(conn); - release_security_operation(conn); - return rc; - } - - DEBUGPC(DMM, "OK\n"); - - /* Start ciphering */ - return gsm0808_cipher_mode(conn, net->a5_encryption, - conn->sec_operation->atuple.vec.kc, 8, 0); -} - -static int gsm48_rx_mm_auth_fail(struct gsm_subscriber_connection *conn, struct msgb *msg) -{ - struct gsm48_hdr *gh = msgb_l3(msg); - uint8_t cause; - uint8_t auts_tag; - uint8_t auts_len; - uint8_t *auts; - int rc; - - if (!conn->sec_operation) { - DEBUGP(DMM, "%s: MM R99 AUTHENTICATION FAILURE:" - " No authentication/cipher operation in progress\n", - subscr_name(conn->subscr)); - return -EINVAL; - } - - if (!conn->subscr) { - LOGP(DMM, LOGL_ERROR, - "MM R99 AUTHENTICATION FAILURE: invalid: no subscriber\n"); - gsm48_tx_mm_auth_rej(conn); - release_security_operation(conn); - return -EINVAL; - } - - if (msgb_l3len(msg) < sizeof(*gh) + 1) { - LOGP(DMM, LOGL_ERROR, - "%s: MM R99 AUTHENTICATION FAILURE:" - " l3 length invalid: %u\n", - subscr_name(conn->subscr), msgb_l3len(msg)); - gsm48_tx_mm_auth_rej(conn); - release_security_operation(conn); - return -EINVAL; - } - - cause = gh->data[0]; - - if (cause != GSM48_REJECT_SYNCH_FAILURE) { - LOGP(DMM, LOGL_INFO, - "%s: MM R99 AUTHENTICATION FAILURE: cause 0x%0x\n", - subscr_name(conn->subscr), cause); - rc = gsm48_tx_mm_auth_rej(conn); - release_security_operation(conn); - return rc; - } - - /* This is a Synch Failure procedure, which should pass an AUTS to - * resynchronize the sequence nr with the HLR. Expecting exactly one - * TLV with 14 bytes of AUTS. */ - - if (msgb_l3len(msg) < sizeof(*gh) + 1 + 2) { - LOGP(DMM, LOGL_INFO, - "%s: MM R99 AUTHENTICATION FAILURE:" - " invalid Synch Failure: missing AUTS IE\n", - subscr_name(conn->subscr)); - gsm48_tx_mm_auth_rej(conn); - release_security_operation(conn); - return -EINVAL; - } - - auts_tag = gh->data[1]; - auts_len = gh->data[2]; - auts = &gh->data[3]; - - if (auts_tag != GSM48_IE_AUTS - || auts_len != 14) { - LOGP(DMM, LOGL_INFO, - "%s: MM R99 AUTHENTICATION FAILURE:" - " invalid Synch Failure:" - " expected AUTS IE 0x%02x of 14 bytes," - " got IE 0x%02x of %u bytes\n", - subscr_name(conn->subscr), - GSM48_IE_AUTS, auts_tag, auts_len); - gsm48_tx_mm_auth_rej(conn); - release_security_operation(conn); - return -EINVAL; - } - - if (msgb_l3len(msg) < sizeof(*gh) + 1 + 2 + auts_len) { - LOGP(DMM, LOGL_INFO, - "%s: MM R99 AUTHENTICATION FAILURE:" - " invalid Synch Failure msg: message truncated (%u)\n", - subscr_name(conn->subscr), msgb_l3len(msg)); - gsm48_tx_mm_auth_rej(conn); - release_security_operation(conn); - return -EINVAL; - } - - /* We have an AUTS IE with exactly 14 bytes of AUTS and the msgb is - * large enough. */ - - DEBUGP(DMM, "%s: MM R99 AUTHENTICATION SYNCH (AUTS = %s)\n", - subscr_name(conn->subscr), osmo_hexdump_nospc(auts, 14)); - - /* Future: vlr_sub_rx_auth_fail(conn->vsub, auts); */ - - LOGP(DMM, LOGL_ERROR, "%s: MM R99 AUTHENTICATION not supported\n", - subscr_name(conn->subscr)); - rc = gsm48_tx_mm_auth_rej(conn); - release_security_operation(conn); - return rc; -} - -/* Receive a GSM 04.08 Mobility Management (MM) message */ -static int gsm0408_rcv_mm(struct gsm_subscriber_connection *conn, struct msgb *msg) -{ - struct gsm48_hdr *gh = msgb_l3(msg); - int rc = 0; - - switch (gsm48_hdr_msg_type(gh)) { - case GSM48_MT_MM_LOC_UPD_REQUEST: - DEBUGP(DMM, "LOCATION UPDATING REQUEST: "); - rc = mm_rx_loc_upd_req(conn, msg); - break; - case GSM48_MT_MM_ID_RESP: - rc = mm_rx_id_resp(conn, msg); - break; - case GSM48_MT_MM_CM_SERV_REQ: - rc = gsm48_rx_mm_serv_req(conn, msg); - break; - case GSM48_MT_MM_STATUS: - rc = gsm48_rx_mm_status(msg); - break; - case GSM48_MT_MM_TMSI_REALL_COMPL: - DEBUGP(DMM, "TMSI Reallocation Completed. Subscriber: %s\n", - subscr_name(conn->subscr)); - loc_updating_success(conn, 1); - break; - case GSM48_MT_MM_IMSI_DETACH_IND: - rc = gsm48_rx_mm_imsi_detach_ind(conn, msg); - break; - case GSM48_MT_MM_CM_REEST_REQ: - DEBUGP(DMM, "CM REESTABLISH REQUEST: Not implemented\n"); - break; - case GSM48_MT_MM_AUTH_RESP: - rc = gsm48_rx_mm_auth_resp(conn, msg); - break; - case GSM48_MT_MM_AUTH_FAIL: - rc = gsm48_rx_mm_auth_fail(conn, msg); - break; - default: - LOGP(DMM, LOGL_NOTICE, "Unknown GSM 04.08 MM msg type 0x%02x\n", - gh->msg_type); - break; - } - - return rc; -} - -/* Receive a PAGING RESPONSE message from the MS */ -static int gsm48_rx_rr_pag_resp(struct gsm_subscriber_connection *conn, struct msgb *msg) -{ - struct gsm48_hdr *gh = msgb_l3(msg); - struct gsm48_pag_resp *resp; - uint8_t *classmark2_lv = gh->data + 1; - uint8_t mi_type; - char mi_string[GSM48_MI_SIZE]; - struct gsm_subscriber *subscr = NULL; - struct bsc_subscr *bsub; - uint32_t tmsi; - int rc = 0; - - resp = (struct gsm48_pag_resp *) &gh->data[0]; - gsm48_paging_extract_mi(resp, msgb_l3len(msg) - sizeof(*gh), - mi_string, &mi_type); - DEBUGP(DRR, "PAGING RESPONSE: MI(%s)=%s\n", - gsm48_mi_type_name(mi_type), mi_string); - - switch (mi_type) { - case GSM_MI_TYPE_TMSI: - tmsi = tmsi_from_string(mi_string); - subscr = subscr_get_by_tmsi(conn->network->subscr_group, tmsi); - break; - case GSM_MI_TYPE_IMSI: - subscr = subscr_get_by_imsi(conn->network->subscr_group, - mi_string); - break; - } - - if (!subscr) { - DEBUGP(DRR, "<- Can't find any subscriber for this ID\n"); - /* FIXME: request id? close channel? */ - return -EINVAL; - } - - if (!conn->subscr) { - conn->subscr = subscr; - } else if (conn->subscr != subscr) { - LOGP(DRR, LOGL_ERROR, "<- Channel already owned by someone else?\n"); - subscr_put(subscr); - return -EINVAL; - } else { - DEBUGP(DRR, "<- Channel already owned by us\n"); - subscr_put(subscr); - subscr = conn->subscr; - } - - log_set_context(LOG_CTX_VLR_SUBSCR, subscr); - DEBUGP(DRR, "<- Channel was requested by %s\n", - subscr->name && strlen(subscr->name) ? subscr->name : subscr->imsi); - - subscr->equipment.classmark2_len = *classmark2_lv; - memcpy(subscr->equipment.classmark2, classmark2_lv+1, *classmark2_lv); - db_sync_equipment(&subscr->equipment); - - /* TODO MSC split -- creating a BSC subscriber directly from MSC data - * structures in RAM. At some point the MSC will send a message to the - * BSC instead. */ - bsub = bsc_subscr_find_or_create_by_imsi(conn->network->bsc_subscribers, - subscr->imsi); - bsub->tmsi = subscr->tmsi; - bsub->lac = subscr->lac; - - /* We received a paging */ - conn->expire_timer_stopped = 1; - - rc = gsm48_handle_paging_resp(conn, msg, bsub); - return rc; -} - -static int gsm48_rx_rr_app_info(struct gsm_subscriber_connection *conn, struct msgb *msg) -{ - struct gsm48_hdr *gh = msgb_l3(msg); - uint8_t apdu_id_flags; - uint8_t apdu_len; - uint8_t *apdu_data; - - apdu_id_flags = gh->data[0]; - apdu_len = gh->data[1]; - apdu_data = gh->data+2; - - DEBUGP(DRR, "RX APPLICATION INFO id/flags=0x%02x apdu_len=%u apdu=%s\n", - apdu_id_flags, apdu_len, osmo_hexdump(apdu_data, apdu_len)); - - return db_apdu_blob_store(conn->subscr, apdu_id_flags, apdu_len, apdu_data); -} - -/* Receive a GSM 04.08 Radio Resource (RR) message */ -static int gsm0408_rcv_rr(struct gsm_subscriber_connection *conn, struct msgb *msg) -{ - struct gsm48_hdr *gh = msgb_l3(msg); - int rc = 0; - - switch (gh->msg_type) { - case GSM48_MT_RR_PAG_RESP: - rc = gsm48_rx_rr_pag_resp(conn, msg); - break; - case GSM48_MT_RR_APP_INFO: - rc = gsm48_rx_rr_app_info(conn, msg); - break; - default: - LOGP(DRR, LOGL_NOTICE, "MSC: Unimplemented %s GSM 04.08 RR " - "message\n", gsm48_rr_msg_name(gh->msg_type)); - break; - } - - return rc; -} - -int gsm48_send_rr_app_info(struct gsm_subscriber_connection *conn, uint8_t apdu_id, - uint8_t apdu_len, const uint8_t *apdu) -{ - struct msgb *msg = gsm48_msgb_alloc_name("GSM 04.08 APP INF"); - struct gsm48_hdr *gh; - - msg->lchan = conn->lchan; - - DEBUGP(DRR, "TX APPLICATION INFO id=0x%02x, len=%u\n", - apdu_id, apdu_len); - - gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh) + 2 + apdu_len); - gh->proto_discr = GSM48_PDISC_RR; - gh->msg_type = GSM48_MT_RR_APP_INFO; - gh->data[0] = apdu_id; - gh->data[1] = apdu_len; - memcpy(gh->data+2, apdu, apdu_len); - - return gsm48_conn_sendmsg(msg, conn, NULL); -} - -/* FIXME: this count_statistics is a state machine behaviour. we should convert - * the complete call control into a state machine. Afterwards we can move this - * code into state transitions. - */ -static void count_statistics(struct gsm_trans *trans, int new_state) -{ - int old_state = trans->cc.state; - struct rate_ctr_group *msc = trans->net->msc_ctrs; - - if (old_state == new_state) - return; - - /* state incoming */ - switch (new_state) { - case GSM_CSTATE_ACTIVE: - osmo_counter_inc(trans->net->active_calls); - rate_ctr_inc(&msc->ctr[MSC_CTR_CALL_ACTIVE]); - break; - } - - /* state outgoing */ - switch (old_state) { - case GSM_CSTATE_ACTIVE: - osmo_counter_dec(trans->net->active_calls); - if (new_state == GSM_CSTATE_DISCONNECT_REQ || - new_state == GSM_CSTATE_DISCONNECT_IND) - rate_ctr_inc(&msc->ctr[MSC_CTR_CALL_COMPLETE]); - else - rate_ctr_inc(&msc->ctr[MSC_CTR_CALL_INCOMPLETE]); - break; - } -} - -/* Call Control */ - -/* The entire call control code is written in accordance with Figure 7.10c - * for 'very early assignment', i.e. we allocate a TCH/F during IMMEDIATE - * ASSIGN, then first use that TCH/F for signalling and later MODE MODIFY - * it for voice */ - -static void new_cc_state(struct gsm_trans *trans, int state) -{ - if (state > 31 || state < 0) - return; - - DEBUGP(DCC, "new state %s -> %s\n", - gsm48_cc_state_name(trans->cc.state), - gsm48_cc_state_name(state)); - - count_statistics(trans, state); - trans->cc.state = state; -} - -static int gsm48_cc_tx_status(struct gsm_trans *trans, void *arg) -{ - struct msgb *msg = gsm48_msgb_alloc_name("GSM 04.08 CC STATUS"); - struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh)); - uint8_t *cause, *call_state; - - gh->msg_type = GSM48_MT_CC_STATUS; - - cause = msgb_put(msg, 3); - cause[0] = 2; - cause[1] = GSM48_CAUSE_CS_GSM | GSM48_CAUSE_LOC_USER; - cause[2] = 0x80 | 30; /* response to status inquiry */ - - call_state = msgb_put(msg, 1); - call_state[0] = 0xc0 | 0x00; - - return gsm48_conn_sendmsg(msg, trans->conn, trans); -} - -static int gsm48_tx_simple(struct gsm_subscriber_connection *conn, - uint8_t pdisc, uint8_t msg_type) -{ - struct msgb *msg = gsm48_msgb_alloc_name("GSM 04.08 TX SIMPLE"); - struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh)); - - msg->lchan = conn->lchan; - - gh->proto_discr = pdisc; - gh->msg_type = msg_type; - - return gsm48_conn_sendmsg(msg, conn, NULL); -} - -static void gsm48_stop_cc_timer(struct gsm_trans *trans) -{ - if (osmo_timer_pending(&trans->cc.timer)) { - DEBUGP(DCC, "stopping pending timer T%x\n", trans->cc.Tcurrent); - osmo_timer_del(&trans->cc.timer); - trans->cc.Tcurrent = 0; - } -} - -static int mncc_recvmsg(struct gsm_network *net, struct gsm_trans *trans, - int msg_type, struct gsm_mncc *mncc) -{ - struct msgb *msg; - unsigned char *data; - - if (trans) - if (trans->conn && trans->conn->lchan) - DEBUGP(DCC, "(bts %d trx %d ts %d ti %x sub %s) " - "Sending '%s' to MNCC.\n", - trans->conn->lchan->ts->trx->bts->nr, - trans->conn->lchan->ts->trx->nr, - trans->conn->lchan->ts->nr, trans->transaction_id, - (trans->subscr)?(trans->subscr->extension):"-", - get_mncc_name(msg_type)); - else - DEBUGP(DCC, "(bts - trx - ts - ti -- sub %s) " - "Sending '%s' to MNCC.\n", - (trans->subscr)?(trans->subscr->extension):"-", - get_mncc_name(msg_type)); - else - DEBUGP(DCC, "(bts - trx - ts - ti -- sub -) " - "Sending '%s' to MNCC.\n", get_mncc_name(msg_type)); - - mncc->msg_type = msg_type; - - msg = msgb_alloc(sizeof(struct gsm_mncc), "MNCC"); - if (!msg) - return -ENOMEM; - - data = msgb_put(msg, sizeof(struct gsm_mncc)); - memcpy(data, mncc, sizeof(struct gsm_mncc)); - - cc_tx_to_mncc(net, msg); - - return 0; -} - -int mncc_release_ind(struct gsm_network *net, struct gsm_trans *trans, - uint32_t callref, int location, int value) -{ - struct gsm_mncc rel; - - memset(&rel, 0, sizeof(rel)); - rel.callref = callref; - mncc_set_cause(&rel, location, value); - if (trans && trans->cc.state == GSM_CSTATE_RELEASE_REQ) - return mncc_recvmsg(net, trans, MNCC_REL_CNF, &rel); - return mncc_recvmsg(net, trans, MNCC_REL_IND, &rel); -} - -/* Call Control Specific transaction release. - * gets called by trans_free, DO NOT CALL YOURSELF! */ -void _gsm48_cc_trans_free(struct gsm_trans *trans) -{ - gsm48_stop_cc_timer(trans); - - /* send release to L4, if callref still exists */ - if (trans->callref) { - /* Ressource unavailable */ - mncc_release_ind(trans->net, trans, trans->callref, - GSM48_CAUSE_LOC_PRN_S_LU, - GSM48_CC_CAUSE_RESOURCE_UNAVAIL); - } - if (trans->cc.state != GSM_CSTATE_NULL) - new_cc_state(trans, GSM_CSTATE_NULL); - if (trans->conn) - trau_mux_unmap(&trans->conn->lchan->ts->e1_link, trans->callref); -} - -static int gsm48_cc_tx_setup(struct gsm_trans *trans, void *arg); - -/* call-back from paging the B-end of the connection */ -static int setup_trig_pag_evt(unsigned int hooknum, unsigned int event, - struct msgb *msg, void *_conn, void *_transt) -{ - struct gsm_subscriber_connection *conn = _conn; - struct gsm_trans *transt = _transt; - - OSMO_ASSERT(!transt->conn); - - /* check all tranactions (without lchan) for subscriber */ - switch (event) { - case GSM_PAGING_SUCCEEDED: - DEBUGP(DCC, "Paging subscr %s succeeded!\n", transt->subscr->extension); - OSMO_ASSERT(conn); - /* Assign lchan */ - transt->conn = conn; - /* send SETUP request to called party */ - gsm48_cc_tx_setup(transt, &transt->cc.msg); - break; - case GSM_PAGING_EXPIRED: - case GSM_PAGING_BUSY: - DEBUGP(DCC, "Paging subscr %s expired!\n", - transt->subscr->extension); - /* Temporarily out of order */ - mncc_release_ind(transt->net, transt, - transt->callref, - GSM48_CAUSE_LOC_PRN_S_LU, - GSM48_CC_CAUSE_DEST_OOO); - transt->callref = 0; - transt->paging_request = NULL; - trans_free(transt); - break; - default: - LOGP(DCC, LOGL_ERROR, "Unknown paging event %d\n", event); - break; - } - - transt->paging_request = NULL; - return 0; -} - -static int tch_recv_mncc(struct gsm_network *net, uint32_t callref, int enable); - -/* handle audio path for handover */ -static int switch_for_handover(struct gsm_lchan *old_lchan, - struct gsm_lchan *new_lchan) -{ - struct rtp_socket *old_rs, *new_rs, *other_rs; - - /* Ask the new socket to send to the already known port. */ - if (new_lchan->conn->mncc_rtp_bridge) { - LOGP(DHO, LOGL_DEBUG, "Forwarding RTP\n"); - rsl_ipacc_mdcx(new_lchan, - old_lchan->abis_ip.connect_ip, - old_lchan->abis_ip.connect_port, 0); - return 0; - } - - if (ipacc_rtp_direct) { - LOGP(DHO, LOGL_ERROR, "unable to handover in direct RTP mode\n"); - return 0; - } - - /* RTP Proxy mode */ - new_rs = new_lchan->abis_ip.rtp_socket; - old_rs = old_lchan->abis_ip.rtp_socket; - - if (!new_rs) { - LOGP(DHO, LOGL_ERROR, "no RTP socket for new_lchan\n"); - return -EIO; - } - - rsl_ipacc_mdcx_to_rtpsock(new_lchan); - - if (!old_rs) { - LOGP(DHO, LOGL_ERROR, "no RTP socket for old_lchan\n"); - return -EIO; - } - - /* copy rx_action and reference to other sock */ - new_rs->rx_action = old_rs->rx_action; - new_rs->tx_action = old_rs->tx_action; - new_rs->transmit = old_rs->transmit; - - switch (old_lchan->abis_ip.rtp_socket->rx_action) { - case RTP_PROXY: - other_rs = old_rs->proxy.other_sock; - rtp_socket_proxy(new_rs, other_rs); - /* delete reference to other end socket to prevent - * rtp_socket_free() from removing the inverse reference */ - old_rs->proxy.other_sock = NULL; - break; - case RTP_RECV_UPSTREAM: - new_rs->receive = old_rs->receive; - break; - case RTP_NONE: - break; - } - - return 0; -} - -static void maybe_switch_for_handover(struct gsm_lchan *lchan) -{ - struct gsm_lchan *old_lchan; - old_lchan = bsc_handover_pending(lchan); - if (old_lchan) - switch_for_handover(old_lchan, lchan); -} - -/* some other part of the code sends us a signal */ -static int handle_abisip_signal(unsigned int subsys, unsigned int signal, - void *handler_data, void *signal_data) -{ - struct gsm_lchan *lchan = signal_data; - int rc; - struct gsm_network *net; - struct gsm_trans *trans; - - if (subsys != SS_ABISIP) - return 0; - - /* RTP bridge handling */ - if (lchan->conn && lchan->conn->mncc_rtp_bridge) - return tch_rtp_signal(lchan, signal); - - /* in case we use direct BTS-to-BTS RTP */ - if (ipacc_rtp_direct) - return 0; - - switch (signal) { - case S_ABISIP_CRCX_ACK: - /* in case we don't use direct BTS-to-BTS RTP */ - /* the BTS has successfully bound a TCH to a local ip/port, - * which means we can connect our UDP socket to it */ - if (lchan->abis_ip.rtp_socket) { - rtp_socket_free(lchan->abis_ip.rtp_socket); - lchan->abis_ip.rtp_socket = NULL; - } - - lchan->abis_ip.rtp_socket = rtp_socket_create(); - if (!lchan->abis_ip.rtp_socket) - return -EIO; - - rc = rtp_socket_connect(lchan->abis_ip.rtp_socket, - lchan->abis_ip.bound_ip, - lchan->abis_ip.bound_port); - if (rc < 0) - return -EIO; - - /* check if any transactions on this lchan still have - * a tch_recv_mncc request pending */ - net = lchan->ts->trx->bts->network; - llist_for_each_entry(trans, &net->trans_list, entry) { - if (trans->conn && trans->conn->lchan == lchan && trans->tch_recv) { - DEBUGP(DCC, "pending tch_recv_mncc request\n"); - tch_recv_mncc(net, trans->callref, 1); - } - } - - /* - * TODO: this appears to be too early? Why not until after - * the handover detect or the handover complete? - * - * Do we have a handover pending for this new lchan? In that - * case re-route the audio from the old channel to the new one. - */ - maybe_switch_for_handover(lchan); - break; - case S_ABISIP_DLCX_IND: - /* the BTS tells us a RTP stream has been disconnected */ - if (lchan->abis_ip.rtp_socket) { - rtp_socket_free(lchan->abis_ip.rtp_socket); - lchan->abis_ip.rtp_socket = NULL; - } - - break; - } - - return 0; -} - -/* map two ipaccess RTP streams onto each other */ -static int tch_map(struct gsm_lchan *lchan, struct gsm_lchan *remote_lchan) -{ - struct gsm_bts *bts = lchan->ts->trx->bts; - struct gsm_bts *remote_bts = remote_lchan->ts->trx->bts; - enum gsm_chan_t lt = lchan->type, rt = remote_lchan->type; - enum gsm48_chan_mode lm = lchan->tch_mode, rm = remote_lchan->tch_mode; - int rc; - - DEBUGP(DCC, "Setting up TCH map between (bts=%u,trx=%u,ts=%u,%s) and " - "(bts=%u,trx=%u,ts=%u,%s)\n", - bts->nr, lchan->ts->trx->nr, lchan->ts->nr, - get_value_string(gsm_chan_t_names, lt), - remote_bts->nr, remote_lchan->ts->trx->nr, remote_lchan->ts->nr, - get_value_string(gsm_chan_t_names, rt)); - - if (bts->type != remote_bts->type) { - LOGP(DCC, LOGL_ERROR, "Cannot switch calls between different BTS types yet\n"); - return -EINVAL; - } - - if (lt != rt) { - LOGP(DCC, LOGL_ERROR, "Cannot patch through call with different" - " channel types: local = %s, remote = %s\n", - get_value_string(gsm_chan_t_names, lt), - get_value_string(gsm_chan_t_names, rt)); - return -EBADSLT; - } - - if (lm != rm) { - LOGP(DCC, LOGL_ERROR, "Cannot patch through call with different" - " channel modes: local = %s, remote = %s\n", - get_value_string(gsm48_chan_mode_names, lm), - get_value_string(gsm48_chan_mode_names, rm)); - return -EMEDIUMTYPE; - } - - // todo: map between different bts types - switch (bts->type) { - case GSM_BTS_TYPE_NANOBTS: - case GSM_BTS_TYPE_OSMOBTS: - if (!ipacc_rtp_direct) { - if (!lchan->abis_ip.rtp_socket) { - LOGP(DHO, LOGL_ERROR, "no RTP socket for " - "lchan\n"); - return -EIO; - } - if (!remote_lchan->abis_ip.rtp_socket) { - LOGP(DHO, LOGL_ERROR, "no RTP socket for " - "remote_lchan\n"); - return -EIO; - } - - /* connect the TCH's to our RTP proxy */ - rc = rsl_ipacc_mdcx_to_rtpsock(lchan); - if (rc < 0) - return rc; - rc = rsl_ipacc_mdcx_to_rtpsock(remote_lchan); - if (rc < 0) - return rc; - /* connect them with each other */ - rtp_socket_proxy(lchan->abis_ip.rtp_socket, - remote_lchan->abis_ip.rtp_socket); - } else { - /* directly connect TCH RTP streams to each other */ - rc = rsl_ipacc_mdcx(lchan, remote_lchan->abis_ip.bound_ip, - remote_lchan->abis_ip.bound_port, - remote_lchan->abis_ip.rtp_payload2); - if (rc < 0) - return rc; - rc = rsl_ipacc_mdcx(remote_lchan, lchan->abis_ip.bound_ip, - lchan->abis_ip.bound_port, - lchan->abis_ip.rtp_payload2); - } - break; - case GSM_BTS_TYPE_BS11: - case GSM_BTS_TYPE_RBS2000: - case GSM_BTS_TYPE_NOKIA_SITE: - trau_mux_map_lchan(lchan, remote_lchan); - break; - default: - LOGP(DCC, LOGL_ERROR, "Unknown BTS type %u\n", bts->type); - return -EINVAL; - } - - return 0; -} - -/* bridge channels of two transactions */ -static int tch_bridge(struct gsm_network *net, struct gsm_mncc_bridge *bridge) -{ - struct gsm_trans *trans1 = trans_find_by_callref(net, bridge->callref[0]); - struct gsm_trans *trans2 = trans_find_by_callref(net, bridge->callref[1]); - - if (!trans1 || !trans2) - return -EIO; - - if (!trans1->conn || !trans2->conn) - return -EIO; - - /* Which subscriber do we want to track trans1 or trans2? */ - log_set_context(LOG_CTX_VLR_SUBSCR, trans1->subscr); - - /* through-connect channel */ - return tch_map(trans1->conn->lchan, trans2->conn->lchan); -} - -/* enable receive of channels to MNCC upqueue */ -static int tch_recv_mncc(struct gsm_network *net, uint32_t callref, int enable) -{ - struct gsm_trans *trans; - struct gsm_lchan *lchan; - struct gsm_bts *bts; - int rc; - - /* Find callref */ - trans = trans_find_by_callref(net, callref); - if (!trans) - return -EIO; - if (!trans->conn) - return 0; - - log_set_context(LOG_CTX_VLR_SUBSCR, trans->subscr); - lchan = trans->conn->lchan; - bts = lchan->ts->trx->bts; - - /* store receive state */ - trans->tch_recv = enable; - - switch (bts->type) { - case GSM_BTS_TYPE_NANOBTS: - case GSM_BTS_TYPE_OSMOBTS: - if (ipacc_rtp_direct) { - LOGP(DCC, LOGL_ERROR, "Error: RTP proxy is disabled\n"); - return -EINVAL; - } - /* In case, we don't have a RTP socket to the BTS yet, the BTS - * will not be connected to our RTP proxy and the socket will - * not be assigned to the application interface. This method - * will be called again, once the audio socket is created and - * connected. */ - if (!lchan->abis_ip.rtp_socket) { - DEBUGP(DCC, "queue tch_recv_mncc request (%d)\n", enable); - return 0; - } - if (enable) { - /* connect the TCH's to our RTP proxy */ - rc = rsl_ipacc_mdcx_to_rtpsock(lchan); - if (rc < 0) - return rc; - /* assign socket to application interface */ - rtp_socket_upstream(lchan->abis_ip.rtp_socket, - net, callref); - } else - rtp_socket_upstream(lchan->abis_ip.rtp_socket, - net, 0); - break; - case GSM_BTS_TYPE_BS11: - case GSM_BTS_TYPE_RBS2000: - case GSM_BTS_TYPE_NOKIA_SITE: - /* In case we don't have a TCH with correct mode, the TRAU muxer - * will not be asigned to the application interface. This is - * performed by switch_trau_mux() after successful handover or - * assignment. */ - if (lchan->tch_mode == GSM48_CMODE_SIGN) { - DEBUGP(DCC, "queue tch_recv_mncc request (%d)\n", enable); - return 0; - } - if (enable) - return trau_recv_lchan(lchan, callref); - return trau_mux_unmap(NULL, callref); - break; - default: - LOGP(DCC, LOGL_ERROR, "Unknown BTS type %u\n", bts->type); - return -EINVAL; - } - - return 0; -} - -static int gsm48_cc_rx_status_enq(struct gsm_trans *trans, struct msgb *msg) -{ - DEBUGP(DCC, "-> STATUS ENQ\n"); - return gsm48_cc_tx_status(trans, msg); -} - -static int gsm48_cc_tx_release(struct gsm_trans *trans, void *arg); -static int gsm48_cc_tx_disconnect(struct gsm_trans *trans, void *arg); - -static void gsm48_cc_timeout(void *arg) -{ - struct gsm_trans *trans = arg; - int disconnect = 0, release = 0; - int mo_cause = GSM48_CC_CAUSE_RECOVERY_TIMER; - int mo_location = GSM48_CAUSE_LOC_USER; - int l4_cause = GSM48_CC_CAUSE_NORMAL_UNSPEC; - int l4_location = GSM48_CAUSE_LOC_PRN_S_LU; - struct gsm_mncc mo_rel, l4_rel; - - memset(&mo_rel, 0, sizeof(struct gsm_mncc)); - mo_rel.callref = trans->callref; - memset(&l4_rel, 0, sizeof(struct gsm_mncc)); - l4_rel.callref = trans->callref; - - switch(trans->cc.Tcurrent) { - case 0x303: - release = 1; - l4_cause = GSM48_CC_CAUSE_USER_NOTRESPOND; - break; - case 0x310: - disconnect = 1; - l4_cause = GSM48_CC_CAUSE_USER_NOTRESPOND; - break; - case 0x313: - disconnect = 1; - /* unknown, did not find it in the specs */ - break; - case 0x301: - disconnect = 1; - l4_cause = GSM48_CC_CAUSE_USER_NOTRESPOND; - break; - case 0x308: - if (!trans->cc.T308_second) { - /* restart T308 a second time */ - gsm48_cc_tx_release(trans, &trans->cc.msg); - trans->cc.T308_second = 1; - break; /* stay in release state */ - } - trans_free(trans); - return; -// release = 1; -// l4_cause = 14; -// break; - case 0x306: - release = 1; - mo_cause = trans->cc.msg.cause.value; - mo_location = trans->cc.msg.cause.location; - break; - case 0x323: - disconnect = 1; - break; - default: - release = 1; - } - - if (release && trans->callref) { - /* process release towards layer 4 */ - mncc_release_ind(trans->net, trans, trans->callref, - l4_location, l4_cause); - trans->callref = 0; - } - - if (disconnect && trans->callref) { - /* process disconnect towards layer 4 */ - mncc_set_cause(&l4_rel, l4_location, l4_cause); - mncc_recvmsg(trans->net, trans, MNCC_DISC_IND, &l4_rel); - } - - /* process disconnect towards mobile station */ - if (disconnect || release) { - mncc_set_cause(&mo_rel, mo_location, mo_cause); - mo_rel.cause.diag[0] = ((trans->cc.Tcurrent & 0xf00) >> 8) + '0'; - mo_rel.cause.diag[1] = ((trans->cc.Tcurrent & 0x0f0) >> 4) + '0'; - mo_rel.cause.diag[2] = (trans->cc.Tcurrent & 0x00f) + '0'; - mo_rel.cause.diag_len = 3; - - if (disconnect) - gsm48_cc_tx_disconnect(trans, &mo_rel); - if (release) - gsm48_cc_tx_release(trans, &mo_rel); - } - -} - -/* disconnect both calls from the bridge */ -static inline void disconnect_bridge(struct gsm_network *net, - struct gsm_mncc_bridge *bridge, int err) -{ - struct gsm_trans *trans0 = trans_find_by_callref(net, bridge->callref[0]); - struct gsm_trans *trans1 = trans_find_by_callref(net, bridge->callref[1]); - struct gsm_mncc mx_rel; - if (!trans0 || !trans1) - return; - - DEBUGP(DCC, "Failed to bridge TCH for calls %x <-> %x :: %s \n", - trans0->callref, trans1->callref, strerror(err)); - - memset(&mx_rel, 0, sizeof(struct gsm_mncc)); - mncc_set_cause(&mx_rel, GSM48_CAUSE_LOC_INN_NET, - GSM48_CC_CAUSE_CHAN_UNACCEPT); - - mx_rel.callref = trans0->callref; - gsm48_cc_tx_disconnect(trans0, &mx_rel); - - mx_rel.callref = trans1->callref; - gsm48_cc_tx_disconnect(trans1, &mx_rel); -} - -static void gsm48_start_cc_timer(struct gsm_trans *trans, int current, - int sec, int micro) -{ - DEBUGP(DCC, "starting timer T%x with %d seconds\n", current, sec); - osmo_timer_setup(&trans->cc.timer, gsm48_cc_timeout, trans); - osmo_timer_schedule(&trans->cc.timer, sec, micro); - trans->cc.Tcurrent = current; -} - -static int gsm48_cc_rx_setup(struct gsm_trans *trans, struct msgb *msg) -{ - struct gsm48_hdr *gh = msgb_l3(msg); - uint8_t msg_type = gsm48_hdr_msg_type(gh); - unsigned int payload_len = msgb_l3len(msg) - sizeof(*gh); - struct tlv_parsed tp; - struct gsm_mncc setup; - - memset(&setup, 0, sizeof(struct gsm_mncc)); - setup.callref = trans->callref; - setup.lchan_type = trans->conn->lchan->type; - tlv_parse(&tp, &gsm48_att_tlvdef, gh->data, payload_len, 0, 0); - /* emergency setup is identified by msg_type */ - if (msg_type == GSM48_MT_CC_EMERG_SETUP) - setup.emergency = 1; - - /* use subscriber as calling party number */ - setup.fields |= MNCC_F_CALLING; - osmo_strlcpy(setup.calling.number, trans->subscr->extension, - sizeof(setup.calling.number)); - osmo_strlcpy(setup.imsi, trans->subscr->imsi, sizeof(setup.imsi)); - - /* bearer capability */ - if (TLVP_PRESENT(&tp, GSM48_IE_BEARER_CAP)) { - setup.fields |= MNCC_F_BEARER_CAP; - gsm48_decode_bearer_cap(&setup.bearer_cap, - TLVP_VAL(&tp, GSM48_IE_BEARER_CAP)-1); - apply_codec_restrictions(trans->conn->bts, &setup.bearer_cap); - } - /* facility */ - if (TLVP_PRESENT(&tp, GSM48_IE_FACILITY)) { - setup.fields |= MNCC_F_FACILITY; - gsm48_decode_facility(&setup.facility, - TLVP_VAL(&tp, GSM48_IE_FACILITY)-1); - } - /* called party bcd number */ - if (TLVP_PRESENT(&tp, GSM48_IE_CALLED_BCD)) { - setup.fields |= MNCC_F_CALLED; - gsm48_decode_called(&setup.called, - TLVP_VAL(&tp, GSM48_IE_CALLED_BCD)-1); - } - /* user-user */ - if (TLVP_PRESENT(&tp, GSM48_IE_USER_USER)) { - setup.fields |= MNCC_F_USERUSER; - gsm48_decode_useruser(&setup.useruser, - TLVP_VAL(&tp, GSM48_IE_USER_USER)-1); - } - /* ss-version */ - if (TLVP_PRESENT(&tp, GSM48_IE_SS_VERS)) { - setup.fields |= MNCC_F_SSVERSION; - gsm48_decode_ssversion(&setup.ssversion, - TLVP_VAL(&tp, GSM48_IE_SS_VERS)-1); - } - /* CLIR suppression */ - if (TLVP_PRESENT(&tp, GSM48_IE_CLIR_SUPP)) - setup.clir.sup = 1; - /* CLIR invocation */ - if (TLVP_PRESENT(&tp, GSM48_IE_CLIR_INVOC)) - setup.clir.inv = 1; - /* cc cap */ - if (TLVP_PRESENT(&tp, GSM48_IE_CC_CAP)) { - setup.fields |= MNCC_F_CCCAP; - gsm48_decode_cccap(&setup.cccap, - TLVP_VAL(&tp, GSM48_IE_CC_CAP)-1); - } - - new_cc_state(trans, GSM_CSTATE_INITIATED); - - LOGP(DCC, LOGL_INFO, "Subscriber %s (%s) sends SETUP to %s\n", - subscr_name(trans->subscr), trans->subscr->extension, - setup.called.number); - - rate_ctr_inc(&trans->net->msc_ctrs->ctr[MSC_CTR_CALL_MO_SETUP]); - - /* indicate setup to MNCC */ - mncc_recvmsg(trans->net, trans, MNCC_SETUP_IND, &setup); - - /* MNCC code will modify the channel asynchronously, we should - * ipaccess-bind only after the modification has been made to the - * lchan->tch_mode */ - return 0; -} - -static int gsm48_cc_tx_setup(struct gsm_trans *trans, void *arg) -{ - struct msgb *msg = gsm48_msgb_alloc_name("GSM 04.08 CC STUP"); - struct gsm48_hdr *gh; - struct gsm_mncc *setup = arg; - int rc, trans_id; - - gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh)); - - /* transaction id must not be assigned */ - if (trans->transaction_id != 0xff) { /* unasssigned */ - DEBUGP(DCC, "TX Setup with assigned transaction. " - "This is not allowed!\n"); - /* Temporarily out of order */ - rc = mncc_release_ind(trans->net, trans, trans->callref, - GSM48_CAUSE_LOC_PRN_S_LU, - GSM48_CC_CAUSE_RESOURCE_UNAVAIL); - trans->callref = 0; - trans_free(trans); - return rc; - } - - /* Get free transaction_id */ - trans_id = trans_assign_trans_id(trans->net, trans->subscr, - GSM48_PDISC_CC, 0); - if (trans_id < 0) { - /* no free transaction ID */ - rc = mncc_release_ind(trans->net, trans, trans->callref, - GSM48_CAUSE_LOC_PRN_S_LU, - GSM48_CC_CAUSE_RESOURCE_UNAVAIL); - trans->callref = 0; - trans_free(trans); - return rc; - } - trans->transaction_id = trans_id; - - gh->msg_type = GSM48_MT_CC_SETUP; - - gsm48_start_cc_timer(trans, 0x303, GSM48_T303); - - /* bearer capability */ - if (setup->fields & MNCC_F_BEARER_CAP) - gsm48_encode_bearer_cap(msg, 0, &setup->bearer_cap); - /* facility */ - if (setup->fields & MNCC_F_FACILITY) - gsm48_encode_facility(msg, 0, &setup->facility); - /* progress */ - if (setup->fields & MNCC_F_PROGRESS) - gsm48_encode_progress(msg, 0, &setup->progress); - /* calling party BCD number */ - if (setup->fields & MNCC_F_CALLING) - gsm48_encode_calling(msg, &setup->calling); - /* called party BCD number */ - if (setup->fields & MNCC_F_CALLED) - gsm48_encode_called(msg, &setup->called); - /* user-user */ - if (setup->fields & MNCC_F_USERUSER) - gsm48_encode_useruser(msg, 0, &setup->useruser); - /* redirecting party BCD number */ - if (setup->fields & MNCC_F_REDIRECTING) - gsm48_encode_redirecting(msg, &setup->redirecting); - /* signal */ - if (setup->fields & MNCC_F_SIGNAL) - gsm48_encode_signal(msg, setup->signal); - - new_cc_state(trans, GSM_CSTATE_CALL_PRESENT); - - rate_ctr_inc(&trans->net->msc_ctrs->ctr[MSC_CTR_CALL_MT_SETUP]); - - return gsm48_conn_sendmsg(msg, trans->conn, trans); -} - -static int gsm48_cc_rx_call_conf(struct gsm_trans *trans, struct msgb *msg) -{ - struct gsm48_hdr *gh = msgb_l3(msg); - unsigned int payload_len = msgb_l3len(msg) - sizeof(*gh); - struct tlv_parsed tp; - struct gsm_mncc call_conf; - - gsm48_stop_cc_timer(trans); - gsm48_start_cc_timer(trans, 0x310, GSM48_T310); - - memset(&call_conf, 0, sizeof(struct gsm_mncc)); - call_conf.callref = trans->callref; - call_conf.lchan_type = trans->conn->lchan->type; - tlv_parse(&tp, &gsm48_att_tlvdef, gh->data, payload_len, 0, 0); -#if 0 - /* repeat */ - if (TLVP_PRESENT(&tp, GSM48_IE_REPEAT_CIR)) - call_conf.repeat = 1; - if (TLVP_PRESENT(&tp, GSM48_IE_REPEAT_SEQ)) - call_conf.repeat = 2; -#endif - /* bearer capability */ - if (TLVP_PRESENT(&tp, GSM48_IE_BEARER_CAP)) { - call_conf.fields |= MNCC_F_BEARER_CAP; - gsm48_decode_bearer_cap(&call_conf.bearer_cap, - TLVP_VAL(&tp, GSM48_IE_BEARER_CAP)-1); - apply_codec_restrictions(trans->conn->bts, &call_conf.bearer_cap); - } - /* cause */ - if (TLVP_PRESENT(&tp, GSM48_IE_CAUSE)) { - call_conf.fields |= MNCC_F_CAUSE; - gsm48_decode_cause(&call_conf.cause, - TLVP_VAL(&tp, GSM48_IE_CAUSE)-1); - } - /* cc cap */ - if (TLVP_PRESENT(&tp, GSM48_IE_CC_CAP)) { - call_conf.fields |= MNCC_F_CCCAP; - gsm48_decode_cccap(&call_conf.cccap, - TLVP_VAL(&tp, GSM48_IE_CC_CAP)-1); - } - - /* IMSI of called subscriber */ - osmo_strlcpy(call_conf.imsi, trans->subscr->imsi, - sizeof(call_conf.imsi)); - - new_cc_state(trans, GSM_CSTATE_MO_TERM_CALL_CONF); - - return mncc_recvmsg(trans->net, trans, MNCC_CALL_CONF_IND, - &call_conf); -} - -static int gsm48_cc_tx_call_proc(struct gsm_trans *trans, void *arg) -{ - struct gsm_mncc *proceeding = arg; - struct msgb *msg = gsm48_msgb_alloc_name("GSM 04.08 CC PROC"); - struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh)); - - gh->msg_type = GSM48_MT_CC_CALL_PROC; - - new_cc_state(trans, GSM_CSTATE_MO_CALL_PROC); - - /* bearer capability */ - if (proceeding->fields & MNCC_F_BEARER_CAP) - gsm48_encode_bearer_cap(msg, 0, &proceeding->bearer_cap); - /* facility */ - if (proceeding->fields & MNCC_F_FACILITY) - gsm48_encode_facility(msg, 0, &proceeding->facility); - /* progress */ - if (proceeding->fields & MNCC_F_PROGRESS) - gsm48_encode_progress(msg, 0, &proceeding->progress); - - return gsm48_conn_sendmsg(msg, trans->conn, trans); -} - -static int gsm48_cc_rx_alerting(struct gsm_trans *trans, struct msgb *msg) -{ - struct gsm48_hdr *gh = msgb_l3(msg); - unsigned int payload_len = msgb_l3len(msg) - sizeof(*gh); - struct tlv_parsed tp; - struct gsm_mncc alerting; - - gsm48_stop_cc_timer(trans); - gsm48_start_cc_timer(trans, 0x301, GSM48_T301); - - memset(&alerting, 0, sizeof(struct gsm_mncc)); - alerting.callref = trans->callref; - tlv_parse(&tp, &gsm48_att_tlvdef, gh->data, payload_len, 0, 0); - /* facility */ - if (TLVP_PRESENT(&tp, GSM48_IE_FACILITY)) { - alerting.fields |= MNCC_F_FACILITY; - gsm48_decode_facility(&alerting.facility, - TLVP_VAL(&tp, GSM48_IE_FACILITY)-1); - } - - /* progress */ - if (TLVP_PRESENT(&tp, GSM48_IE_PROGR_IND)) { - alerting.fields |= MNCC_F_PROGRESS; - gsm48_decode_progress(&alerting.progress, - TLVP_VAL(&tp, GSM48_IE_PROGR_IND)-1); - } - /* ss-version */ - if (TLVP_PRESENT(&tp, GSM48_IE_SS_VERS)) { - alerting.fields |= MNCC_F_SSVERSION; - gsm48_decode_ssversion(&alerting.ssversion, - TLVP_VAL(&tp, GSM48_IE_SS_VERS)-1); - } - - new_cc_state(trans, GSM_CSTATE_CALL_RECEIVED); - - return mncc_recvmsg(trans->net, trans, MNCC_ALERT_IND, - &alerting); -} - -static int gsm48_cc_tx_alerting(struct gsm_trans *trans, void *arg) -{ - struct gsm_mncc *alerting = arg; - struct msgb *msg = gsm48_msgb_alloc_name("GSM 04.08 CC ALERT"); - struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh)); - - gh->msg_type = GSM48_MT_CC_ALERTING; - - /* facility */ - if (alerting->fields & MNCC_F_FACILITY) - gsm48_encode_facility(msg, 0, &alerting->facility); - /* progress */ - if (alerting->fields & MNCC_F_PROGRESS) - gsm48_encode_progress(msg, 0, &alerting->progress); - /* user-user */ - if (alerting->fields & MNCC_F_USERUSER) - gsm48_encode_useruser(msg, 0, &alerting->useruser); - - new_cc_state(trans, GSM_CSTATE_CALL_DELIVERED); - - return gsm48_conn_sendmsg(msg, trans->conn, trans); -} - -static int gsm48_cc_tx_progress(struct gsm_trans *trans, void *arg) -{ - struct gsm_mncc *progress = arg; - struct msgb *msg = gsm48_msgb_alloc_name("GSM 04.08 CC PROGRESS"); - struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh)); - - gh->msg_type = GSM48_MT_CC_PROGRESS; - - /* progress */ - gsm48_encode_progress(msg, 1, &progress->progress); - /* user-user */ - if (progress->fields & MNCC_F_USERUSER) - gsm48_encode_useruser(msg, 0, &progress->useruser); - - return gsm48_conn_sendmsg(msg, trans->conn, trans); -} - -static int gsm48_cc_tx_connect(struct gsm_trans *trans, void *arg) -{ - struct gsm_mncc *connect = arg; - struct msgb *msg = gsm48_msgb_alloc_name("GSN 04.08 CC CON"); - struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh)); - - gh->msg_type = GSM48_MT_CC_CONNECT; - - gsm48_stop_cc_timer(trans); - gsm48_start_cc_timer(trans, 0x313, GSM48_T313); - - /* facility */ - if (connect->fields & MNCC_F_FACILITY) - gsm48_encode_facility(msg, 0, &connect->facility); - /* progress */ - if (connect->fields & MNCC_F_PROGRESS) - gsm48_encode_progress(msg, 0, &connect->progress); - /* connected number */ - if (connect->fields & MNCC_F_CONNECTED) - gsm48_encode_connected(msg, &connect->connected); - /* user-user */ - if (connect->fields & MNCC_F_USERUSER) - gsm48_encode_useruser(msg, 0, &connect->useruser); - - new_cc_state(trans, GSM_CSTATE_CONNECT_IND); - - return gsm48_conn_sendmsg(msg, trans->conn, trans); -} - -static int gsm48_cc_rx_connect(struct gsm_trans *trans, struct msgb *msg) -{ - struct gsm48_hdr *gh = msgb_l3(msg); - unsigned int payload_len = msgb_l3len(msg) - sizeof(*gh); - struct tlv_parsed tp; - struct gsm_mncc connect; - - gsm48_stop_cc_timer(trans); - - memset(&connect, 0, sizeof(struct gsm_mncc)); - connect.callref = trans->callref; - tlv_parse(&tp, &gsm48_att_tlvdef, gh->data, payload_len, 0, 0); - /* use subscriber as connected party number */ - connect.fields |= MNCC_F_CONNECTED; - osmo_strlcpy(connect.connected.number, trans->subscr->extension, - sizeof(connect.connected.number)); - osmo_strlcpy(connect.imsi, trans->subscr->imsi, sizeof(connect.imsi)); - - /* facility */ - if (TLVP_PRESENT(&tp, GSM48_IE_FACILITY)) { - connect.fields |= MNCC_F_FACILITY; - gsm48_decode_facility(&connect.facility, - TLVP_VAL(&tp, GSM48_IE_FACILITY)-1); - } - /* user-user */ - if (TLVP_PRESENT(&tp, GSM48_IE_USER_USER)) { - connect.fields |= MNCC_F_USERUSER; - gsm48_decode_useruser(&connect.useruser, - TLVP_VAL(&tp, GSM48_IE_USER_USER)-1); - } - /* ss-version */ - if (TLVP_PRESENT(&tp, GSM48_IE_SS_VERS)) { - connect.fields |= MNCC_F_SSVERSION; - gsm48_decode_ssversion(&connect.ssversion, - TLVP_VAL(&tp, GSM48_IE_SS_VERS)-1); - } - - new_cc_state(trans, GSM_CSTATE_CONNECT_REQUEST); - rate_ctr_inc(&trans->net->msc_ctrs->ctr[MSC_CTR_CALL_MT_CONNECT]); - - return mncc_recvmsg(trans->net, trans, MNCC_SETUP_CNF, &connect); -} - - -static int gsm48_cc_rx_connect_ack(struct gsm_trans *trans, struct msgb *msg) -{ - struct gsm_mncc connect_ack; - - gsm48_stop_cc_timer(trans); - - new_cc_state(trans, GSM_CSTATE_ACTIVE); - rate_ctr_inc(&trans->net->msc_ctrs->ctr[MSC_CTR_CALL_MO_CONNECT_ACK]); - - memset(&connect_ack, 0, sizeof(struct gsm_mncc)); - connect_ack.callref = trans->callref; - - return mncc_recvmsg(trans->net, trans, MNCC_SETUP_COMPL_IND, - &connect_ack); -} - -static int gsm48_cc_tx_connect_ack(struct gsm_trans *trans, void *arg) -{ - struct msgb *msg = gsm48_msgb_alloc_name("GSM 04.08 CC CON ACK"); - struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh)); - - gh->msg_type = GSM48_MT_CC_CONNECT_ACK; - - new_cc_state(trans, GSM_CSTATE_ACTIVE); - - return gsm48_conn_sendmsg(msg, trans->conn, trans); -} - -static int gsm48_cc_rx_disconnect(struct gsm_trans *trans, struct msgb *msg) -{ - struct gsm48_hdr *gh = msgb_l3(msg); - unsigned int payload_len = msgb_l3len(msg) - sizeof(*gh); - struct tlv_parsed tp; - struct gsm_mncc disc; - - gsm48_stop_cc_timer(trans); - - new_cc_state(trans, GSM_CSTATE_DISCONNECT_REQ); - - memset(&disc, 0, sizeof(struct gsm_mncc)); - disc.callref = trans->callref; - tlv_parse(&tp, &gsm48_att_tlvdef, gh->data, payload_len, GSM48_IE_CAUSE, 0); - /* cause */ - if (TLVP_PRESENT(&tp, GSM48_IE_CAUSE)) { - disc.fields |= MNCC_F_CAUSE; - gsm48_decode_cause(&disc.cause, - TLVP_VAL(&tp, GSM48_IE_CAUSE)-1); - } - /* facility */ - if (TLVP_PRESENT(&tp, GSM48_IE_FACILITY)) { - disc.fields |= MNCC_F_FACILITY; - gsm48_decode_facility(&disc.facility, - TLVP_VAL(&tp, GSM48_IE_FACILITY)-1); - } - /* user-user */ - if (TLVP_PRESENT(&tp, GSM48_IE_USER_USER)) { - disc.fields |= MNCC_F_USERUSER; - gsm48_decode_useruser(&disc.useruser, - TLVP_VAL(&tp, GSM48_IE_USER_USER)-1); - } - /* ss-version */ - if (TLVP_PRESENT(&tp, GSM48_IE_SS_VERS)) { - disc.fields |= MNCC_F_SSVERSION; - gsm48_decode_ssversion(&disc.ssversion, - TLVP_VAL(&tp, GSM48_IE_SS_VERS)-1); - } - - return mncc_recvmsg(trans->net, trans, MNCC_DISC_IND, &disc); - -} - -static struct gsm_mncc_cause default_cause = { - .location = GSM48_CAUSE_LOC_PRN_S_LU, - .coding = 0, - .rec = 0, - .rec_val = 0, - .value = GSM48_CC_CAUSE_NORMAL_UNSPEC, - .diag_len = 0, - .diag = { 0 }, -}; - -static int gsm48_cc_tx_disconnect(struct gsm_trans *trans, void *arg) -{ - struct gsm_mncc *disc = arg; - struct msgb *msg = gsm48_msgb_alloc_name("GSM 04.08 CC DISC"); - struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh)); - - gh->msg_type = GSM48_MT_CC_DISCONNECT; - - gsm48_stop_cc_timer(trans); - gsm48_start_cc_timer(trans, 0x306, GSM48_T306); - - /* cause */ - if (disc->fields & MNCC_F_CAUSE) - gsm48_encode_cause(msg, 1, &disc->cause); - else - gsm48_encode_cause(msg, 1, &default_cause); - - /* facility */ - if (disc->fields & MNCC_F_FACILITY) - gsm48_encode_facility(msg, 0, &disc->facility); - /* progress */ - if (disc->fields & MNCC_F_PROGRESS) - gsm48_encode_progress(msg, 0, &disc->progress); - /* user-user */ - if (disc->fields & MNCC_F_USERUSER) - gsm48_encode_useruser(msg, 0, &disc->useruser); - - /* store disconnect cause for T306 expiry */ - memcpy(&trans->cc.msg, disc, sizeof(struct gsm_mncc)); - - new_cc_state(trans, GSM_CSTATE_DISCONNECT_IND); - - return gsm48_conn_sendmsg(msg, trans->conn, trans); -} - -static int gsm48_cc_rx_release(struct gsm_trans *trans, struct msgb *msg) -{ - struct gsm48_hdr *gh = msgb_l3(msg); - unsigned int payload_len = msgb_l3len(msg) - sizeof(*gh); - struct tlv_parsed tp; - struct gsm_mncc rel; - int rc; - - gsm48_stop_cc_timer(trans); - - memset(&rel, 0, sizeof(struct gsm_mncc)); - rel.callref = trans->callref; - tlv_parse(&tp, &gsm48_att_tlvdef, gh->data, payload_len, 0, 0); - /* cause */ - if (TLVP_PRESENT(&tp, GSM48_IE_CAUSE)) { - rel.fields |= MNCC_F_CAUSE; - gsm48_decode_cause(&rel.cause, - TLVP_VAL(&tp, GSM48_IE_CAUSE)-1); - } - /* facility */ - if (TLVP_PRESENT(&tp, GSM48_IE_FACILITY)) { - rel.fields |= MNCC_F_FACILITY; - gsm48_decode_facility(&rel.facility, - TLVP_VAL(&tp, GSM48_IE_FACILITY)-1); - } - /* user-user */ - if (TLVP_PRESENT(&tp, GSM48_IE_USER_USER)) { - rel.fields |= MNCC_F_USERUSER; - gsm48_decode_useruser(&rel.useruser, - TLVP_VAL(&tp, GSM48_IE_USER_USER)-1); - } - /* ss-version */ - if (TLVP_PRESENT(&tp, GSM48_IE_SS_VERS)) { - rel.fields |= MNCC_F_SSVERSION; - gsm48_decode_ssversion(&rel.ssversion, - TLVP_VAL(&tp, GSM48_IE_SS_VERS)-1); - } - - if (trans->cc.state == GSM_CSTATE_RELEASE_REQ) { - /* release collision 5.4.5 */ - rc = mncc_recvmsg(trans->net, trans, MNCC_REL_CNF, &rel); - } else { - rc = gsm48_tx_simple(trans->conn, - GSM48_PDISC_CC | (trans->transaction_id << 4), - GSM48_MT_CC_RELEASE_COMPL); - rc = mncc_recvmsg(trans->net, trans, MNCC_REL_IND, &rel); - } - - new_cc_state(trans, GSM_CSTATE_NULL); - - trans->callref = 0; - trans_free(trans); - - return rc; -} - -static int gsm48_cc_tx_release(struct gsm_trans *trans, void *arg) -{ - struct gsm_mncc *rel = arg; - struct msgb *msg = gsm48_msgb_alloc_name("GSM 04.08 CC REL"); - struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh)); - - gh->msg_type = GSM48_MT_CC_RELEASE; - - gsm48_stop_cc_timer(trans); - gsm48_start_cc_timer(trans, 0x308, GSM48_T308); - - /* cause */ - if (rel->fields & MNCC_F_CAUSE) - gsm48_encode_cause(msg, 0, &rel->cause); - /* facility */ - if (rel->fields & MNCC_F_FACILITY) - gsm48_encode_facility(msg, 0, &rel->facility); - /* user-user */ - if (rel->fields & MNCC_F_USERUSER) - gsm48_encode_useruser(msg, 0, &rel->useruser); - - trans->cc.T308_second = 0; - memcpy(&trans->cc.msg, rel, sizeof(struct gsm_mncc)); - - if (trans->cc.state != GSM_CSTATE_RELEASE_REQ) - new_cc_state(trans, GSM_CSTATE_RELEASE_REQ); - - return gsm48_conn_sendmsg(msg, trans->conn, trans); -} - -static int gsm48_cc_rx_release_compl(struct gsm_trans *trans, struct msgb *msg) -{ - struct gsm48_hdr *gh = msgb_l3(msg); - unsigned int payload_len = msgb_l3len(msg) - sizeof(*gh); - struct tlv_parsed tp; - struct gsm_mncc rel; - int rc = 0; - - gsm48_stop_cc_timer(trans); - - memset(&rel, 0, sizeof(struct gsm_mncc)); - rel.callref = trans->callref; - tlv_parse(&tp, &gsm48_att_tlvdef, gh->data, payload_len, 0, 0); - /* cause */ - if (TLVP_PRESENT(&tp, GSM48_IE_CAUSE)) { - rel.fields |= MNCC_F_CAUSE; - gsm48_decode_cause(&rel.cause, - TLVP_VAL(&tp, GSM48_IE_CAUSE)-1); - } - /* facility */ - if (TLVP_PRESENT(&tp, GSM48_IE_FACILITY)) { - rel.fields |= MNCC_F_FACILITY; - gsm48_decode_facility(&rel.facility, - TLVP_VAL(&tp, GSM48_IE_FACILITY)-1); - } - /* user-user */ - if (TLVP_PRESENT(&tp, GSM48_IE_USER_USER)) { - rel.fields |= MNCC_F_USERUSER; - gsm48_decode_useruser(&rel.useruser, - TLVP_VAL(&tp, GSM48_IE_USER_USER)-1); - } - /* ss-version */ - if (TLVP_PRESENT(&tp, GSM48_IE_SS_VERS)) { - rel.fields |= MNCC_F_SSVERSION; - gsm48_decode_ssversion(&rel.ssversion, - TLVP_VAL(&tp, GSM48_IE_SS_VERS)-1); - } - - if (trans->callref) { - switch (trans->cc.state) { - case GSM_CSTATE_CALL_PRESENT: - rc = mncc_recvmsg(trans->net, trans, - MNCC_REJ_IND, &rel); - break; - case GSM_CSTATE_RELEASE_REQ: - rc = mncc_recvmsg(trans->net, trans, - MNCC_REL_CNF, &rel); - break; - default: - rc = mncc_recvmsg(trans->net, trans, - MNCC_REL_IND, &rel); - } - } - - trans->callref = 0; - trans_free(trans); - - return rc; -} - -static int gsm48_cc_tx_release_compl(struct gsm_trans *trans, void *arg) -{ - struct gsm_mncc *rel = arg; - struct msgb *msg = gsm48_msgb_alloc_name("GSM 04.08 CC REL COMPL"); - struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh)); - int ret; - - gh->msg_type = GSM48_MT_CC_RELEASE_COMPL; - - trans->callref = 0; - - gsm48_stop_cc_timer(trans); - - /* cause */ - if (rel->fields & MNCC_F_CAUSE) - gsm48_encode_cause(msg, 0, &rel->cause); - /* facility */ - if (rel->fields & MNCC_F_FACILITY) - gsm48_encode_facility(msg, 0, &rel->facility); - /* user-user */ - if (rel->fields & MNCC_F_USERUSER) - gsm48_encode_useruser(msg, 0, &rel->useruser); - - ret = gsm48_conn_sendmsg(msg, trans->conn, trans); - - trans_free(trans); - - return ret; -} - -static int gsm48_cc_rx_facility(struct gsm_trans *trans, struct msgb *msg) -{ - struct gsm48_hdr *gh = msgb_l3(msg); - unsigned int payload_len = msgb_l3len(msg) - sizeof(*gh); - struct tlv_parsed tp; - struct gsm_mncc fac; - - memset(&fac, 0, sizeof(struct gsm_mncc)); - fac.callref = trans->callref; - tlv_parse(&tp, &gsm48_att_tlvdef, gh->data, payload_len, GSM48_IE_FACILITY, 0); - /* facility */ - if (TLVP_PRESENT(&tp, GSM48_IE_FACILITY)) { - fac.fields |= MNCC_F_FACILITY; - gsm48_decode_facility(&fac.facility, - TLVP_VAL(&tp, GSM48_IE_FACILITY)-1); - } - /* ss-version */ - if (TLVP_PRESENT(&tp, GSM48_IE_SS_VERS)) { - fac.fields |= MNCC_F_SSVERSION; - gsm48_decode_ssversion(&fac.ssversion, - TLVP_VAL(&tp, GSM48_IE_SS_VERS)-1); - } - - return mncc_recvmsg(trans->net, trans, MNCC_FACILITY_IND, &fac); -} - -static int gsm48_cc_tx_facility(struct gsm_trans *trans, void *arg) -{ - struct gsm_mncc *fac = arg; - struct msgb *msg = gsm48_msgb_alloc_name("GSM 04.08 CC FAC"); - struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh)); - - gh->msg_type = GSM48_MT_CC_FACILITY; - - /* facility */ - gsm48_encode_facility(msg, 1, &fac->facility); - - return gsm48_conn_sendmsg(msg, trans->conn, trans); -} - -static int gsm48_cc_rx_hold(struct gsm_trans *trans, struct msgb *msg) -{ - struct gsm_mncc hold; - - memset(&hold, 0, sizeof(struct gsm_mncc)); - hold.callref = trans->callref; - return mncc_recvmsg(trans->net, trans, MNCC_HOLD_IND, &hold); -} - -static int gsm48_cc_tx_hold_ack(struct gsm_trans *trans, void *arg) -{ - struct msgb *msg = gsm48_msgb_alloc_name("GSM 04.08 CC HLD ACK"); - struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh)); - - gh->msg_type = GSM48_MT_CC_HOLD_ACK; - - return gsm48_conn_sendmsg(msg, trans->conn, trans); -} - -static int gsm48_cc_tx_hold_rej(struct gsm_trans *trans, void *arg) -{ - struct gsm_mncc *hold_rej = arg; - struct msgb *msg = gsm48_msgb_alloc_name("GSM 04.08 CC HLD REJ"); - struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh)); - - gh->msg_type = GSM48_MT_CC_HOLD_REJ; - - /* cause */ - if (hold_rej->fields & MNCC_F_CAUSE) - gsm48_encode_cause(msg, 1, &hold_rej->cause); - else - gsm48_encode_cause(msg, 1, &default_cause); - - return gsm48_conn_sendmsg(msg, trans->conn, trans); -} - -static int gsm48_cc_rx_retrieve(struct gsm_trans *trans, struct msgb *msg) -{ - struct gsm_mncc retrieve; - - memset(&retrieve, 0, sizeof(struct gsm_mncc)); - retrieve.callref = trans->callref; - return mncc_recvmsg(trans->net, trans, MNCC_RETRIEVE_IND, - &retrieve); -} - -static int gsm48_cc_tx_retrieve_ack(struct gsm_trans *trans, void *arg) -{ - struct msgb *msg = gsm48_msgb_alloc_name("GSM 04.08 CC RETR ACK"); - struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh)); - - gh->msg_type = GSM48_MT_CC_RETR_ACK; - - return gsm48_conn_sendmsg(msg, trans->conn, trans); -} - -static int gsm48_cc_tx_retrieve_rej(struct gsm_trans *trans, void *arg) -{ - struct gsm_mncc *retrieve_rej = arg; - struct msgb *msg = gsm48_msgb_alloc_name("GSM 04.08 CC RETR REJ"); - struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh)); - - gh->msg_type = GSM48_MT_CC_RETR_REJ; - - /* cause */ - if (retrieve_rej->fields & MNCC_F_CAUSE) - gsm48_encode_cause(msg, 1, &retrieve_rej->cause); - else - gsm48_encode_cause(msg, 1, &default_cause); - - return gsm48_conn_sendmsg(msg, trans->conn, trans); -} - -static int gsm48_cc_rx_start_dtmf(struct gsm_trans *trans, struct msgb *msg) -{ - struct gsm48_hdr *gh = msgb_l3(msg); - unsigned int payload_len = msgb_l3len(msg) - sizeof(*gh); - struct tlv_parsed tp; - struct gsm_mncc dtmf; - - memset(&dtmf, 0, sizeof(struct gsm_mncc)); - dtmf.callref = trans->callref; - tlv_parse(&tp, &gsm48_att_tlvdef, gh->data, payload_len, 0, 0); - /* keypad facility */ - if (TLVP_PRESENT(&tp, GSM48_IE_KPD_FACILITY)) { - dtmf.fields |= MNCC_F_KEYPAD; - gsm48_decode_keypad(&dtmf.keypad, - TLVP_VAL(&tp, GSM48_IE_KPD_FACILITY)-1); - } - - return mncc_recvmsg(trans->net, trans, MNCC_START_DTMF_IND, &dtmf); -} - -static int gsm48_cc_tx_start_dtmf_ack(struct gsm_trans *trans, void *arg) -{ - struct gsm_mncc *dtmf = arg; - struct msgb *msg = gsm48_msgb_alloc_name("GSM 04.08 DTMF ACK"); - struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh)); - - gh->msg_type = GSM48_MT_CC_START_DTMF_ACK; - - /* keypad */ - if (dtmf->fields & MNCC_F_KEYPAD) - gsm48_encode_keypad(msg, dtmf->keypad); - - return gsm48_conn_sendmsg(msg, trans->conn, trans); -} - -static int gsm48_cc_tx_start_dtmf_rej(struct gsm_trans *trans, void *arg) -{ - struct gsm_mncc *dtmf = arg; - struct msgb *msg = gsm48_msgb_alloc_name("GSM 04.08 DTMF REJ"); - struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh)); - - gh->msg_type = GSM48_MT_CC_START_DTMF_REJ; - - /* cause */ - if (dtmf->fields & MNCC_F_CAUSE) - gsm48_encode_cause(msg, 1, &dtmf->cause); - else - gsm48_encode_cause(msg, 1, &default_cause); - - return gsm48_conn_sendmsg(msg, trans->conn, trans); -} - -static int gsm48_cc_tx_stop_dtmf_ack(struct gsm_trans *trans, void *arg) -{ - struct msgb *msg = gsm48_msgb_alloc_name("GSM 04.08 DTMF STP ACK"); - struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh)); - - gh->msg_type = GSM48_MT_CC_STOP_DTMF_ACK; - - return gsm48_conn_sendmsg(msg, trans->conn, trans); -} - -static int gsm48_cc_rx_stop_dtmf(struct gsm_trans *trans, struct msgb *msg) -{ - struct gsm_mncc dtmf; - - memset(&dtmf, 0, sizeof(struct gsm_mncc)); - dtmf.callref = trans->callref; - - return mncc_recvmsg(trans->net, trans, MNCC_STOP_DTMF_IND, &dtmf); -} - -static int gsm48_cc_rx_modify(struct gsm_trans *trans, struct msgb *msg) -{ - struct gsm48_hdr *gh = msgb_l3(msg); - unsigned int payload_len = msgb_l3len(msg) - sizeof(*gh); - struct tlv_parsed tp; - struct gsm_mncc modify; - - memset(&modify, 0, sizeof(struct gsm_mncc)); - modify.callref = trans->callref; - tlv_parse(&tp, &gsm48_att_tlvdef, gh->data, payload_len, GSM48_IE_BEARER_CAP, 0); - /* bearer capability */ - if (TLVP_PRESENT(&tp, GSM48_IE_BEARER_CAP)) { - modify.fields |= MNCC_F_BEARER_CAP; - gsm48_decode_bearer_cap(&modify.bearer_cap, - TLVP_VAL(&tp, GSM48_IE_BEARER_CAP)-1); - apply_codec_restrictions(trans->conn->bts, &modify.bearer_cap); - } - - new_cc_state(trans, GSM_CSTATE_MO_ORIG_MODIFY); - - return mncc_recvmsg(trans->net, trans, MNCC_MODIFY_IND, &modify); -} - -static int gsm48_cc_tx_modify(struct gsm_trans *trans, void *arg) -{ - struct gsm_mncc *modify = arg; - struct msgb *msg = gsm48_msgb_alloc_name("GSM 04.08 CC MOD"); - struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh)); - - gh->msg_type = GSM48_MT_CC_MODIFY; - - gsm48_start_cc_timer(trans, 0x323, GSM48_T323); - - /* bearer capability */ - gsm48_encode_bearer_cap(msg, 1, &modify->bearer_cap); - - new_cc_state(trans, GSM_CSTATE_MO_TERM_MODIFY); - - return gsm48_conn_sendmsg(msg, trans->conn, trans); -} - -static int gsm48_cc_rx_modify_complete(struct gsm_trans *trans, struct msgb *msg) -{ - struct gsm48_hdr *gh = msgb_l3(msg); - unsigned int payload_len = msgb_l3len(msg) - sizeof(*gh); - struct tlv_parsed tp; - struct gsm_mncc modify; - - gsm48_stop_cc_timer(trans); - - memset(&modify, 0, sizeof(struct gsm_mncc)); - modify.callref = trans->callref; - tlv_parse(&tp, &gsm48_att_tlvdef, gh->data, payload_len, GSM48_IE_BEARER_CAP, 0); - /* bearer capability */ - if (TLVP_PRESENT(&tp, GSM48_IE_BEARER_CAP)) { - modify.fields |= MNCC_F_BEARER_CAP; - gsm48_decode_bearer_cap(&modify.bearer_cap, - TLVP_VAL(&tp, GSM48_IE_BEARER_CAP)-1); - apply_codec_restrictions(trans->conn->bts, &modify.bearer_cap); - } - - new_cc_state(trans, GSM_CSTATE_ACTIVE); - - return mncc_recvmsg(trans->net, trans, MNCC_MODIFY_CNF, &modify); -} - -static int gsm48_cc_tx_modify_complete(struct gsm_trans *trans, void *arg) -{ - struct gsm_mncc *modify = arg; - struct msgb *msg = gsm48_msgb_alloc_name("GSM 04.08 CC MOD COMPL"); - struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh)); - - gh->msg_type = GSM48_MT_CC_MODIFY_COMPL; - - /* bearer capability */ - gsm48_encode_bearer_cap(msg, 1, &modify->bearer_cap); - - new_cc_state(trans, GSM_CSTATE_ACTIVE); - - return gsm48_conn_sendmsg(msg, trans->conn, trans); -} - -static int gsm48_cc_rx_modify_reject(struct gsm_trans *trans, struct msgb *msg) -{ - struct gsm48_hdr *gh = msgb_l3(msg); - unsigned int payload_len = msgb_l3len(msg) - sizeof(*gh); - struct tlv_parsed tp; - struct gsm_mncc modify; - - gsm48_stop_cc_timer(trans); - - memset(&modify, 0, sizeof(struct gsm_mncc)); - modify.callref = trans->callref; - tlv_parse(&tp, &gsm48_att_tlvdef, gh->data, payload_len, GSM48_IE_BEARER_CAP, GSM48_IE_CAUSE); - /* bearer capability */ - if (TLVP_PRESENT(&tp, GSM48_IE_BEARER_CAP)) { - modify.fields |= GSM48_IE_BEARER_CAP; - gsm48_decode_bearer_cap(&modify.bearer_cap, - TLVP_VAL(&tp, GSM48_IE_BEARER_CAP)-1); - apply_codec_restrictions(trans->conn->bts, &modify.bearer_cap); - } - /* cause */ - if (TLVP_PRESENT(&tp, GSM48_IE_CAUSE)) { - modify.fields |= MNCC_F_CAUSE; - gsm48_decode_cause(&modify.cause, - TLVP_VAL(&tp, GSM48_IE_CAUSE)-1); - } - - new_cc_state(trans, GSM_CSTATE_ACTIVE); - - return mncc_recvmsg(trans->net, trans, MNCC_MODIFY_REJ, &modify); -} - -static int gsm48_cc_tx_modify_reject(struct gsm_trans *trans, void *arg) -{ - struct gsm_mncc *modify = arg; - struct msgb *msg = gsm48_msgb_alloc_name("GSM 04.08 CC MOD REJ"); - struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh)); - - gh->msg_type = GSM48_MT_CC_MODIFY_REJECT; - - /* bearer capability */ - gsm48_encode_bearer_cap(msg, 1, &modify->bearer_cap); - /* cause */ - gsm48_encode_cause(msg, 1, &modify->cause); - - new_cc_state(trans, GSM_CSTATE_ACTIVE); - - return gsm48_conn_sendmsg(msg, trans->conn, trans); -} - -static int gsm48_cc_tx_notify(struct gsm_trans *trans, void *arg) -{ - struct gsm_mncc *notify = arg; - struct msgb *msg = gsm48_msgb_alloc_name("GSM 04.08 CC NOT"); - struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh)); - - gh->msg_type = GSM48_MT_CC_NOTIFY; - - /* notify */ - gsm48_encode_notify(msg, notify->notify); - - return gsm48_conn_sendmsg(msg, trans->conn, trans); -} - -static int gsm48_cc_rx_notify(struct gsm_trans *trans, struct msgb *msg) -{ - struct gsm48_hdr *gh = msgb_l3(msg); - unsigned int payload_len = msgb_l3len(msg) - sizeof(*gh); -// struct tlv_parsed tp; - struct gsm_mncc notify; - - memset(¬ify, 0, sizeof(struct gsm_mncc)); - notify.callref = trans->callref; -// tlv_parse(&tp, &gsm48_att_tlvdef, gh->data, payload_len); - if (payload_len >= 1) - gsm48_decode_notify(¬ify.notify, gh->data); - - return mncc_recvmsg(trans->net, trans, MNCC_NOTIFY_IND, ¬ify); -} - -static int gsm48_cc_tx_userinfo(struct gsm_trans *trans, void *arg) -{ - struct gsm_mncc *user = arg; - struct msgb *msg = gsm48_msgb_alloc_name("GSM 04.08 USR INFO"); - struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh)); - - gh->msg_type = GSM48_MT_CC_USER_INFO; - - /* user-user */ - if (user->fields & MNCC_F_USERUSER) - gsm48_encode_useruser(msg, 1, &user->useruser); - /* more data */ - if (user->more) - gsm48_encode_more(msg); - - return gsm48_conn_sendmsg(msg, trans->conn, trans); -} - -static int gsm48_cc_rx_userinfo(struct gsm_trans *trans, struct msgb *msg) -{ - struct gsm48_hdr *gh = msgb_l3(msg); - unsigned int payload_len = msgb_l3len(msg) - sizeof(*gh); - struct tlv_parsed tp; - struct gsm_mncc user; - - memset(&user, 0, sizeof(struct gsm_mncc)); - user.callref = trans->callref; - tlv_parse(&tp, &gsm48_att_tlvdef, gh->data, payload_len, GSM48_IE_USER_USER, 0); - /* user-user */ - if (TLVP_PRESENT(&tp, GSM48_IE_USER_USER)) { - user.fields |= MNCC_F_USERUSER; - gsm48_decode_useruser(&user.useruser, - TLVP_VAL(&tp, GSM48_IE_USER_USER)-1); - } - /* more data */ - if (TLVP_PRESENT(&tp, GSM48_IE_MORE_DATA)) - user.more = 1; - - return mncc_recvmsg(trans->net, trans, MNCC_USERINFO_IND, &user); -} - -static int _gsm48_lchan_modify(struct gsm_trans *trans, void *arg) -{ - struct gsm_mncc *mode = arg; - struct gsm_lchan *lchan = trans->conn->lchan; - - /* - * We were forced to make an assignment a lot earlier and - * we should avoid sending another assignment that might - * even lead to a different kind of lchan (TCH/F vs. TCH/H). - * In case of rtp-bridge it is too late to change things - * here. - */ - if (trans->conn->mncc_rtp_bridge && lchan->tch_mode != GSM48_CMODE_SIGN) - return 0; - - return gsm0808_assign_req(trans->conn, mode->lchan_mode, - trans->conn->lchan->type != GSM_LCHAN_TCH_H); -} - -static void mncc_recv_rtp(struct gsm_network *net, uint32_t callref, - int cmd, uint32_t addr, uint16_t port, uint32_t payload_type, - uint32_t payload_msg_type) -{ - uint8_t data[sizeof(struct gsm_mncc)]; - struct gsm_mncc_rtp *rtp; - - memset(&data, 0, sizeof(data)); - rtp = (struct gsm_mncc_rtp *) &data[0]; - - rtp->callref = callref; - rtp->msg_type = cmd; - rtp->ip = addr; - rtp->port = port; - rtp->payload_type = payload_type; - rtp->payload_msg_type = payload_msg_type; - mncc_recvmsg(net, NULL, cmd, (struct gsm_mncc *)data); -} - -static void mncc_recv_rtp_sock(struct gsm_network *net, struct gsm_trans *trans, int cmd) -{ - struct gsm_lchan *lchan; - int msg_type; - - lchan = trans->conn->lchan; - switch (lchan->abis_ip.rtp_payload) { - case RTP_PT_GSM_FULL: - msg_type = GSM_TCHF_FRAME; - break; - case RTP_PT_GSM_EFR: - msg_type = GSM_TCHF_FRAME_EFR; - break; - case RTP_PT_GSM_HALF: - msg_type = GSM_TCHH_FRAME; - break; - case RTP_PT_AMR: - msg_type = GSM_TCH_FRAME_AMR; - break; - default: - LOGP(DMNCC, LOGL_ERROR, "%s unknown payload type %d\n", - gsm_lchan_name(lchan), lchan->abis_ip.rtp_payload); - msg_type = 0; - break; - } - - return mncc_recv_rtp(net, trans->callref, cmd, - lchan->abis_ip.bound_ip, - lchan->abis_ip.bound_port, - lchan->abis_ip.rtp_payload, - msg_type); -} - -static void mncc_recv_rtp_err(struct gsm_network *net, uint32_t callref, int cmd) -{ - return mncc_recv_rtp(net, callref, cmd, 0, 0, 0, 0); -} - -static int tch_rtp_create(struct gsm_network *net, uint32_t callref) -{ - struct gsm_bts *bts; - struct gsm_lchan *lchan; - struct gsm_trans *trans; - enum gsm48_chan_mode m; - - /* Find callref */ - trans = trans_find_by_callref(net, callref); - if (!trans) { - LOGP(DMNCC, LOGL_ERROR, "RTP create for non-existing trans\n"); - mncc_recv_rtp_err(net, callref, MNCC_RTP_CREATE); - return -EIO; - } - log_set_context(LOG_CTX_VLR_SUBSCR, trans->subscr); - if (!trans->conn) { - LOGP(DMNCC, LOGL_NOTICE, "RTP create for trans without conn\n"); - mncc_recv_rtp_err(net, callref, MNCC_RTP_CREATE); - return 0; - } - - lchan = trans->conn->lchan; - bts = lchan->ts->trx->bts; - if (!is_ipaccess_bts(bts)) { - /* - * I want this to be straight forward and have no audio flow - * through the nitb/osmo-mss system. This currently means that - * this will not work with BS11/Nokia type BTS. We would need - * to have a trau<->rtp bridge for these but still preferable - * in another process. - */ - LOGP(DMNCC, LOGL_ERROR, "RTP create only works with IP systems\n"); - mncc_recv_rtp_err(net, callref, MNCC_RTP_CREATE); - return -EINVAL; - } - - trans->conn->mncc_rtp_bridge = 1; - /* - * *sigh* we need to pick a codec now. Pick the most generic one - * right now and hope we could fix that later on. This is very - * similiar to the routine above. - * Fallback to the internal MNCC mode to select a route. - */ - if (lchan->tch_mode == GSM48_CMODE_SIGN) { - trans->conn->mncc_rtp_create_pending = 1; - m = mncc_codec_for_mode(lchan->type); - LOGP(DMNCC, LOGL_DEBUG, "RTP create: codec=%s, chan_type=%s\n", - get_value_string(gsm48_chan_mode_names, m), - get_value_string(gsm_chan_t_names, lchan->type)); - return gsm0808_assign_req(trans->conn, m, - lchan->type != GSM_LCHAN_TCH_H); - } - - mncc_recv_rtp_sock(trans->net, trans, MNCC_RTP_CREATE); - return 0; -} - -static int tch_rtp_connect(struct gsm_network *net, void *arg) -{ - struct gsm_lchan *lchan; - struct gsm_trans *trans; - struct gsm_mncc_rtp *rtp = arg; - - /* Find callref */ - trans = trans_find_by_callref(net, rtp->callref); - if (!trans) { - LOGP(DMNCC, LOGL_ERROR, "RTP connect for non-existing trans\n"); - mncc_recv_rtp_err(net, rtp->callref, MNCC_RTP_CONNECT); - return -EIO; - } - log_set_context(LOG_CTX_VLR_SUBSCR, trans->subscr); - if (!trans->conn) { - LOGP(DMNCC, LOGL_ERROR, "RTP connect for trans without conn\n"); - mncc_recv_rtp_err(net, rtp->callref, MNCC_RTP_CONNECT); - return 0; - } - - lchan = trans->conn->lchan; - LOGP(DMNCC, LOGL_DEBUG, "RTP connect: codec=%s, chan_type=%s\n", - get_value_string(gsm48_chan_mode_names, - mncc_codec_for_mode(lchan->type)), - get_value_string(gsm_chan_t_names, lchan->type)); - - /* TODO: Check if payload_msg_type is compatible with what we have */ - if (rtp->payload_type != lchan->abis_ip.rtp_payload) { - LOGP(DMNCC, LOGL_ERROR, "RTP connect with different RTP payload\n"); - mncc_recv_rtp_err(net, rtp->callref, MNCC_RTP_CONNECT); - } - - /* - * FIXME: payload2 can't be sent with MDCX as the osmo-bts code - * complains about both rtp and rtp payload2 being present in the - * same package! - */ - trans->conn->mncc_rtp_connect_pending = 1; - return rsl_ipacc_mdcx(lchan, rtp->ip, rtp->port, 0); -} - -static int tch_rtp_signal(struct gsm_lchan *lchan, int signal) -{ - struct gsm_network *net; - struct gsm_trans *tmp, *trans = NULL; - - net = lchan->ts->trx->bts->network; - llist_for_each_entry(tmp, &net->trans_list, entry) { - if (!tmp->conn) - continue; - if (tmp->conn->lchan != lchan && tmp->conn->ho_lchan != lchan) - continue; - trans = tmp; - break; - } - - if (!trans) { - LOGP(DMNCC, LOGL_ERROR, "%s IPA abis signal but no transaction.\n", - gsm_lchan_name(lchan)); - return 0; - } - - switch (signal) { - case S_ABISIP_CRCX_ACK: - if (lchan->conn->mncc_rtp_create_pending) { - lchan->conn->mncc_rtp_create_pending = 0; - LOGP(DMNCC, LOGL_NOTICE, "%s sending pending RTP create ind.\n", - gsm_lchan_name(lchan)); - mncc_recv_rtp_sock(net, trans, MNCC_RTP_CREATE); - } - /* - * TODO: this appears to be too early? Why not until after - * the handover detect or the handover complete? - */ - maybe_switch_for_handover(lchan); - break; - case S_ABISIP_MDCX_ACK: - if (lchan->conn->mncc_rtp_connect_pending) { - lchan->conn->mncc_rtp_connect_pending = 0; - LOGP(DMNCC, LOGL_NOTICE, "%s sending pending RTP connect ind.\n", - gsm_lchan_name(lchan)); - mncc_recv_rtp_sock(net, trans, MNCC_RTP_CONNECT); - } - break; - } - - return 0; -} - - -static struct downstate { - uint32_t states; - int type; - int (*rout) (struct gsm_trans *trans, void *arg); -} downstatelist[] = { - /* mobile originating call establishment */ - {SBIT(GSM_CSTATE_INITIATED), /* 5.2.1.2 */ - MNCC_CALL_PROC_REQ, gsm48_cc_tx_call_proc}, - {SBIT(GSM_CSTATE_INITIATED) | SBIT(GSM_CSTATE_MO_CALL_PROC), /* 5.2.1.2 | 5.2.1.5 */ - MNCC_ALERT_REQ, gsm48_cc_tx_alerting}, - {SBIT(GSM_CSTATE_INITIATED) | SBIT(GSM_CSTATE_MO_CALL_PROC) | SBIT(GSM_CSTATE_CALL_DELIVERED), /* 5.2.1.2 | 5.2.1.6 | 5.2.1.6 */ - MNCC_SETUP_RSP, gsm48_cc_tx_connect}, - {SBIT(GSM_CSTATE_MO_CALL_PROC), /* 5.2.1.4.2 */ - MNCC_PROGRESS_REQ, gsm48_cc_tx_progress}, - /* mobile terminating call establishment */ - {SBIT(GSM_CSTATE_NULL), /* 5.2.2.1 */ - MNCC_SETUP_REQ, gsm48_cc_tx_setup}, - {SBIT(GSM_CSTATE_CONNECT_REQUEST), - MNCC_SETUP_COMPL_REQ, gsm48_cc_tx_connect_ack}, - /* signalling during call */ - {SBIT(GSM_CSTATE_ACTIVE), - MNCC_NOTIFY_REQ, gsm48_cc_tx_notify}, - {ALL_STATES - SBIT(GSM_CSTATE_NULL) - SBIT(GSM_CSTATE_RELEASE_REQ), - MNCC_FACILITY_REQ, gsm48_cc_tx_facility}, - {ALL_STATES, - MNCC_START_DTMF_RSP, gsm48_cc_tx_start_dtmf_ack}, - {ALL_STATES, - MNCC_START_DTMF_REJ, gsm48_cc_tx_start_dtmf_rej}, - {ALL_STATES, - MNCC_STOP_DTMF_RSP, gsm48_cc_tx_stop_dtmf_ack}, - {SBIT(GSM_CSTATE_ACTIVE), - MNCC_HOLD_CNF, gsm48_cc_tx_hold_ack}, - {SBIT(GSM_CSTATE_ACTIVE), - MNCC_HOLD_REJ, gsm48_cc_tx_hold_rej}, - {SBIT(GSM_CSTATE_ACTIVE), - MNCC_RETRIEVE_CNF, gsm48_cc_tx_retrieve_ack}, - {SBIT(GSM_CSTATE_ACTIVE), - MNCC_RETRIEVE_REJ, gsm48_cc_tx_retrieve_rej}, - {SBIT(GSM_CSTATE_ACTIVE), - MNCC_MODIFY_REQ, gsm48_cc_tx_modify}, - {SBIT(GSM_CSTATE_MO_ORIG_MODIFY), - MNCC_MODIFY_RSP, gsm48_cc_tx_modify_complete}, - {SBIT(GSM_CSTATE_MO_ORIG_MODIFY), - MNCC_MODIFY_REJ, gsm48_cc_tx_modify_reject}, - {SBIT(GSM_CSTATE_ACTIVE), - MNCC_USERINFO_REQ, gsm48_cc_tx_userinfo}, - /* clearing */ - {SBIT(GSM_CSTATE_INITIATED), - MNCC_REJ_REQ, gsm48_cc_tx_release_compl}, - {ALL_STATES - SBIT(GSM_CSTATE_NULL) - SBIT(GSM_CSTATE_DISCONNECT_IND) - SBIT(GSM_CSTATE_RELEASE_REQ) - SBIT(GSM_CSTATE_DISCONNECT_REQ), /* 5.4.4 */ - MNCC_DISC_REQ, gsm48_cc_tx_disconnect}, - {ALL_STATES - SBIT(GSM_CSTATE_NULL) - SBIT(GSM_CSTATE_RELEASE_REQ), /* 5.4.3.2 */ - MNCC_REL_REQ, gsm48_cc_tx_release}, - /* special */ - {ALL_STATES, - MNCC_LCHAN_MODIFY, _gsm48_lchan_modify}, -}; - -#define DOWNSLLEN \ - (sizeof(downstatelist) / sizeof(struct downstate)) - - -int mncc_tx_to_cc(struct gsm_network *net, int msg_type, void *arg) -{ - int i, rc = 0; - struct gsm_trans *trans = NULL, *transt; - struct gsm_subscriber_connection *conn = NULL; - struct gsm_bts *bts = NULL; - struct gsm_mncc *data = arg, rel; - - DEBUGP(DMNCC, "receive message %s\n", get_mncc_name(msg_type)); - - /* handle special messages */ - switch(msg_type) { - case MNCC_BRIDGE: - rc = tch_bridge(net, arg); - if (rc < 0) - disconnect_bridge(net, arg, -rc); - return rc; - case MNCC_FRAME_DROP: - return tch_recv_mncc(net, data->callref, 0); - case MNCC_FRAME_RECV: - return tch_recv_mncc(net, data->callref, 1); - case MNCC_RTP_CREATE: - return tch_rtp_create(net, data->callref); - case MNCC_RTP_CONNECT: - return tch_rtp_connect(net, arg); - case MNCC_RTP_FREE: - /* unused right now */ - return -EIO; - case GSM_TCHF_FRAME: - case GSM_TCHF_FRAME_EFR: - case GSM_TCHH_FRAME: - case GSM_TCH_FRAME_AMR: - /* Find callref */ - trans = trans_find_by_callref(net, data->callref); - if (!trans) { - LOGP(DMNCC, LOGL_ERROR, "TCH frame for non-existing trans\n"); - return -EIO; - } - log_set_context(LOG_CTX_VLR_SUBSCR, trans->subscr); - if (!trans->conn) { - LOGP(DMNCC, LOGL_NOTICE, "TCH frame for trans without conn\n"); - return 0; - } - if (!trans->conn->lchan) { - LOGP(DMNCC, LOGL_NOTICE, "TCH frame for trans without lchan\n"); - return 0; - } - if (trans->conn->lchan->type != GSM_LCHAN_TCH_F - && trans->conn->lchan->type != GSM_LCHAN_TCH_H) { - /* This should be LOGL_ERROR or NOTICE, but - * unfortuantely it happens for a couple of frames at - * the beginning of every RTP connection */ - LOGP(DMNCC, LOGL_DEBUG, "TCH frame for lchan != TCH_F/TCH_H\n"); - return 0; - } - bts = trans->conn->lchan->ts->trx->bts; - switch (bts->type) { - case GSM_BTS_TYPE_NANOBTS: - case GSM_BTS_TYPE_OSMOBTS: - if (!trans->conn->lchan->abis_ip.rtp_socket) { - DEBUGP(DMNCC, "TCH frame to lchan without RTP connection\n"); - return 0; - } - return rtp_send_frame(trans->conn->lchan->abis_ip.rtp_socket, arg); - case GSM_BTS_TYPE_BS11: - case GSM_BTS_TYPE_RBS2000: - case GSM_BTS_TYPE_NOKIA_SITE: - return trau_send_frame(trans->conn->lchan, arg); - default: - LOGP(DCC, LOGL_ERROR, "Unknown BTS type %u\n", bts->type); - } - return -EINVAL; - } - - memset(&rel, 0, sizeof(struct gsm_mncc)); - rel.callref = data->callref; - - /* Find callref */ - trans = trans_find_by_callref(net, data->callref); - - /* Callref unknown */ - if (!trans) { - struct gsm_subscriber *subscr; - - if (msg_type != MNCC_SETUP_REQ) { - DEBUGP(DCC, "(bts - trx - ts - ti -- sub %s) " - "Received '%s' from MNCC with " - "unknown callref %d\n", data->called.number, - get_mncc_name(msg_type), data->callref); - /* Invalid call reference */ - return mncc_release_ind(net, NULL, data->callref, - GSM48_CAUSE_LOC_PRN_S_LU, - GSM48_CC_CAUSE_INVAL_TRANS_ID); - } - if (!data->called.number[0] && !data->imsi[0]) { - DEBUGP(DCC, "(bts - trx - ts - ti) " - "Received '%s' from MNCC with " - "no number or IMSI\n", get_mncc_name(msg_type)); - /* Invalid number */ - return mncc_release_ind(net, NULL, data->callref, - GSM48_CAUSE_LOC_PRN_S_LU, - GSM48_CC_CAUSE_INV_NR_FORMAT); - } - /* New transaction due to setup, find subscriber */ - if (data->called.number[0]) - subscr = subscr_get_by_extension(net->subscr_group, - data->called.number); - else - subscr = subscr_get_by_imsi(net->subscr_group, - data->imsi); - - /* update the subscriber we deal with */ - log_set_context(LOG_CTX_VLR_SUBSCR, subscr); - - /* If subscriber is not found */ - if (!subscr) { - DEBUGP(DCC, "(bts - trx - ts - ti -- sub %s) " - "Received '%s' from MNCC with " - "unknown subscriber %s\n", data->called.number, - get_mncc_name(msg_type), data->called.number); - /* Unknown subscriber */ - return mncc_release_ind(net, NULL, data->callref, - GSM48_CAUSE_LOC_PRN_S_LU, - GSM48_CC_CAUSE_UNASSIGNED_NR); - } - /* If subscriber is not "attached" */ - if (!subscr->lac) { - DEBUGP(DCC, "(bts - trx - ts - ti -- sub %s) " - "Received '%s' from MNCC with " - "detached subscriber %s\n", data->called.number, - get_mncc_name(msg_type), data->called.number); - subscr_put(subscr); - /* Temporarily out of order */ - return mncc_release_ind(net, NULL, data->callref, - GSM48_CAUSE_LOC_PRN_S_LU, - GSM48_CC_CAUSE_DEST_OOO); - } - /* Create transaction */ - trans = trans_alloc(net, subscr, GSM48_PDISC_CC, 0xff, data->callref); - if (!trans) { - DEBUGP(DCC, "No memory for trans.\n"); - subscr_put(subscr); - /* Ressource unavailable */ - mncc_release_ind(net, NULL, data->callref, - GSM48_CAUSE_LOC_PRN_S_LU, - GSM48_CC_CAUSE_RESOURCE_UNAVAIL); - return -ENOMEM; - } - /* Find lchan */ - conn = connection_for_subscr(subscr); - - /* If subscriber has no lchan */ - if (!conn) { - /* find transaction with this subscriber already paging */ - llist_for_each_entry(transt, &net->trans_list, entry) { - /* Transaction of our lchan? */ - if (transt == trans || - transt->subscr != subscr) - continue; - DEBUGP(DCC, "(bts - trx - ts - ti -- sub %s) " - "Received '%s' from MNCC with " - "unallocated channel, paging already " - "started for lac %d.\n", - data->called.number, - get_mncc_name(msg_type), subscr->lac); - subscr_put(subscr); - trans_free(trans); - return 0; - } - /* store setup informations until paging was successfull */ - memcpy(&trans->cc.msg, data, sizeof(struct gsm_mncc)); - - /* Request a channel */ - trans->paging_request = subscr_request_channel(subscr, - RSL_CHANNEED_TCH_F, setup_trig_pag_evt, - trans); - if (!trans->paging_request) { - LOGP(DCC, LOGL_ERROR, "Failed to allocate paging token.\n"); - subscr_put(subscr); - trans_free(trans); - return 0; - } - subscr_put(subscr); - return 0; - } - /* Assign lchan */ - trans->conn = conn; - subscr_put(subscr); - } else { - /* update the subscriber we deal with */ - log_set_context(LOG_CTX_VLR_SUBSCR, trans->subscr); - } - - if (trans->conn) - conn = trans->conn; - - /* if paging did not respond yet */ - if (!conn) { - DEBUGP(DCC, "(bts - trx - ts - ti -- sub %s) " - "Received '%s' from MNCC in paging state\n", - (trans->subscr)?(trans->subscr->extension):"-", - get_mncc_name(msg_type)); - mncc_set_cause(&rel, GSM48_CAUSE_LOC_PRN_S_LU, - GSM48_CC_CAUSE_NORM_CALL_CLEAR); - if (msg_type == MNCC_REL_REQ) - rc = mncc_recvmsg(net, trans, MNCC_REL_CNF, &rel); - else - rc = mncc_recvmsg(net, trans, MNCC_REL_IND, &rel); - trans->callref = 0; - trans_free(trans); - return rc; - } - - DEBUGP(DCC, "(bts %d trx %d ts %d ti %02x sub %s) " - "Received '%s' from MNCC in state %d (%s)\n", - conn->bts->nr, conn->lchan->ts->trx->nr, conn->lchan->ts->nr, - trans->transaction_id, - (trans->conn->subscr)?(trans->conn->subscr->extension):"-", - get_mncc_name(msg_type), trans->cc.state, - gsm48_cc_state_name(trans->cc.state)); - - /* Find function for current state and message */ - for (i = 0; i < DOWNSLLEN; i++) - if ((msg_type == downstatelist[i].type) - && ((1 << trans->cc.state) & downstatelist[i].states)) - break; - if (i == DOWNSLLEN) { - DEBUGP(DCC, "Message unhandled at this state.\n"); - return 0; - } - - rc = downstatelist[i].rout(trans, arg); - - return rc; -} - - -static struct datastate { - uint32_t states; - int type; - int (*rout) (struct gsm_trans *trans, struct msgb *msg); -} datastatelist[] = { - /* mobile originating call establishment */ - {SBIT(GSM_CSTATE_NULL), /* 5.2.1.2 */ - GSM48_MT_CC_SETUP, gsm48_cc_rx_setup}, - {SBIT(GSM_CSTATE_NULL), /* 5.2.1.2 */ - GSM48_MT_CC_EMERG_SETUP, gsm48_cc_rx_setup}, - {SBIT(GSM_CSTATE_CONNECT_IND), /* 5.2.1.2 */ - GSM48_MT_CC_CONNECT_ACK, gsm48_cc_rx_connect_ack}, - /* mobile terminating call establishment */ - {SBIT(GSM_CSTATE_CALL_PRESENT), /* 5.2.2.3.2 */ - GSM48_MT_CC_CALL_CONF, gsm48_cc_rx_call_conf}, - {SBIT(GSM_CSTATE_CALL_PRESENT) | SBIT(GSM_CSTATE_MO_TERM_CALL_CONF), /* ???? | 5.2.2.3.2 */ - GSM48_MT_CC_ALERTING, gsm48_cc_rx_alerting}, - {SBIT(GSM_CSTATE_CALL_PRESENT) | SBIT(GSM_CSTATE_MO_TERM_CALL_CONF) | SBIT(GSM_CSTATE_CALL_RECEIVED), /* (5.2.2.6) | 5.2.2.6 | 5.2.2.6 */ - GSM48_MT_CC_CONNECT, gsm48_cc_rx_connect}, - /* signalling during call */ - {ALL_STATES - SBIT(GSM_CSTATE_NULL), - GSM48_MT_CC_FACILITY, gsm48_cc_rx_facility}, - {SBIT(GSM_CSTATE_ACTIVE), - GSM48_MT_CC_NOTIFY, gsm48_cc_rx_notify}, - {ALL_STATES, - GSM48_MT_CC_START_DTMF, gsm48_cc_rx_start_dtmf}, - {ALL_STATES, - GSM48_MT_CC_STOP_DTMF, gsm48_cc_rx_stop_dtmf}, - {ALL_STATES, - GSM48_MT_CC_STATUS_ENQ, gsm48_cc_rx_status_enq}, - {SBIT(GSM_CSTATE_ACTIVE), - GSM48_MT_CC_HOLD, gsm48_cc_rx_hold}, - {SBIT(GSM_CSTATE_ACTIVE), - GSM48_MT_CC_RETR, gsm48_cc_rx_retrieve}, - {SBIT(GSM_CSTATE_ACTIVE), - GSM48_MT_CC_MODIFY, gsm48_cc_rx_modify}, - {SBIT(GSM_CSTATE_MO_TERM_MODIFY), - GSM48_MT_CC_MODIFY_COMPL, gsm48_cc_rx_modify_complete}, - {SBIT(GSM_CSTATE_MO_TERM_MODIFY), - GSM48_MT_CC_MODIFY_REJECT, gsm48_cc_rx_modify_reject}, - {SBIT(GSM_CSTATE_ACTIVE), - GSM48_MT_CC_USER_INFO, gsm48_cc_rx_userinfo}, - /* clearing */ - {ALL_STATES - SBIT(GSM_CSTATE_NULL) - SBIT(GSM_CSTATE_RELEASE_REQ), /* 5.4.3.2 */ - GSM48_MT_CC_DISCONNECT, gsm48_cc_rx_disconnect}, - {ALL_STATES - SBIT(GSM_CSTATE_NULL), /* 5.4.4.1.2.2 */ - GSM48_MT_CC_RELEASE, gsm48_cc_rx_release}, - {ALL_STATES, /* 5.4.3.4 */ - GSM48_MT_CC_RELEASE_COMPL, gsm48_cc_rx_release_compl}, -}; - -#define DATASLLEN \ - (sizeof(datastatelist) / sizeof(struct datastate)) - -static int gsm0408_rcv_cc(struct gsm_subscriber_connection *conn, struct msgb *msg) -{ - struct gsm48_hdr *gh = msgb_l3(msg); - uint8_t msg_type = gsm48_hdr_msg_type(gh); - uint8_t transaction_id = gsm48_hdr_trans_id_flip_ti(gh); - struct gsm_trans *trans = NULL; - int i, rc = 0; - - if (msg_type & 0x80) { - DEBUGP(DCC, "MSG 0x%2x not defined for PD error\n", msg_type); - return -EINVAL; - } - - if (!conn->subscr) { - LOGP(DCC, LOGL_ERROR, "Invalid conn, no subscriber\n"); - return -EINVAL; - } - - /* Find transaction */ - trans = trans_find_by_id(conn, GSM48_PDISC_CC, transaction_id); - - DEBUGP(DCC, "(bts %d trx %d ts %d ti %x sub %s) " - "Received '%s' from MS in state %d (%s)\n", - conn->bts->nr, conn->lchan->ts->trx->nr, conn->lchan->ts->nr, - transaction_id, (conn->subscr)?(conn->subscr->extension):"-", - gsm48_cc_msg_name(msg_type), trans?(trans->cc.state):0, - gsm48_cc_state_name(trans?(trans->cc.state):0)); - - /* Create transaction */ - if (!trans) { - DEBUGP(DCC, "Unknown transaction ID %x, " - "creating new trans.\n", transaction_id); - /* Create transaction */ - trans = trans_alloc(conn->network, conn->subscr, - GSM48_PDISC_CC, - transaction_id, new_callref++); - if (!trans) { - DEBUGP(DCC, "No memory for trans.\n"); - rc = gsm48_tx_simple(conn, - GSM48_PDISC_CC | (transaction_id << 4), - GSM48_MT_CC_RELEASE_COMPL); - return -ENOMEM; - } - /* Assign transaction */ - trans->conn = conn; - } - - /* find function for current state and message */ - for (i = 0; i < DATASLLEN; i++) - if ((msg_type == datastatelist[i].type) - && ((1 << trans->cc.state) & datastatelist[i].states)) - break; - if (i == DATASLLEN) { - DEBUGP(DCC, "Message unhandled at this state.\n"); - return 0; - } - - assert(trans->subscr); - - rc = datastatelist[i].rout(trans, msg); - - return rc; -} - -/* Create a dummy to wait five seconds */ -static void release_anchor(struct gsm_subscriber_connection *conn) -{ - if (!conn->anch_operation) - return; - - osmo_timer_del(&conn->anch_operation->timeout); - talloc_free(conn->anch_operation); - conn->anch_operation = NULL; -} - -static void anchor_timeout(void *_data) -{ - struct gsm_subscriber_connection *con = _data; - - release_anchor(con); - msc_release_connection(con); -} - -int gsm0408_new_conn(struct gsm_subscriber_connection *conn) -{ - conn->anch_operation = talloc_zero(conn, struct gsm_anchor_operation); - if (!conn->anch_operation) - return -1; - - osmo_timer_setup(&conn->anch_operation->timeout, anchor_timeout, conn); - osmo_timer_schedule(&conn->anch_operation->timeout, 5, 0); - return 0; -} - -struct gsm_subscriber_connection *msc_subscr_con_allocate(struct gsm_network *network) -{ - struct gsm_subscriber_connection *conn; - - conn = talloc_zero(network, struct gsm_subscriber_connection); - if (!conn) - return NULL; - - conn->network = network; - llist_add_tail(&conn->entry, &network->subscr_conns); - return conn; -} - -void msc_subscr_con_free(struct gsm_subscriber_connection *conn) -{ - if (!conn) - return; - - if (conn->subscr) { - subscr_put(conn->subscr); - conn->subscr = NULL; - } - - llist_del(&conn->entry); - talloc_free(conn); -} - -/* Main entry point for GSM 04.08/44.008 Layer 3 data (e.g. from the BSC). */ -int gsm0408_dispatch(struct gsm_subscriber_connection *conn, struct msgb *msg) -{ - struct gsm48_hdr *gh = msgb_l3(msg); - uint8_t pdisc = gsm48_hdr_pdisc(gh); - int rc = 0; - - OSMO_ASSERT(conn); - OSMO_ASSERT(msg); - - LOGP(DRLL, LOGL_DEBUG, "Dispatching 04.08 message, pdisc=%d\n", pdisc); -#if 0 - if (silent_call_reroute(conn, msg)) - return silent_call_rx(conn, msg); -#endif - - switch (pdisc) { - case GSM48_PDISC_CC: - release_anchor(conn); - rc = gsm0408_rcv_cc(conn, msg); - break; - case GSM48_PDISC_MM: - rc = gsm0408_rcv_mm(conn, msg); - break; - case GSM48_PDISC_RR: - rc = gsm0408_rcv_rr(conn, msg); - break; - case GSM48_PDISC_SMS: - release_anchor(conn); - rc = gsm0411_rcv_sms(conn, msg); - break; - case GSM48_PDISC_MM_GPRS: - case GSM48_PDISC_SM_GPRS: - LOGP(DRLL, LOGL_NOTICE, "Unimplemented " - "GSM 04.08 discriminator 0x%02x\n", pdisc); - rc = -ENOTSUP; - break; - case GSM48_PDISC_NC_SS: - release_anchor(conn); - rc = handle_rcv_ussd(conn, msg); - break; - default: - LOGP(DRLL, LOGL_NOTICE, "Unknown " - "GSM 04.08 discriminator 0x%02x\n", pdisc); - rc = -EINVAL; - break; - } - - return rc; -} - -/* - * This will be run by the linker when loading the DSO. We use it to - * do system initialization, e.g. registration of signal handlers. - */ -static __attribute__((constructor)) void on_dso_load_0408(void) -{ - osmo_signal_register_handler(SS_ABISIP, handle_abisip_signal, NULL); -} diff --git a/openbsc/src/libmsc/gsm_04_11.c b/openbsc/src/libmsc/gsm_04_11.c deleted file mode 100644 index aa2030f8..00000000 --- a/openbsc/src/libmsc/gsm_04_11.c +++ /dev/null @@ -1,1070 +0,0 @@ -/* Point-to-Point (PP) Short Message Service (SMS) - * Support on Mobile Radio Interface - * 3GPP TS 04.11 version 7.1.0 Release 1998 / ETSI TS 100 942 V7.1.0 */ - -/* (C) 2008 by Daniel Willmann - * (C) 2009 by Harald Welte - * (C) 2010-2012 by Holger Hans Peter Freyther - * (C) 2010 by On-Waves - * (C) 2011 by Andreas Eversberg - * - * All Rights Reserved - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation; either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - * - */ - - -#include -#include -#include -#include -#include -#include - -#include "bscconfig.h" - -#include -#include -#include - -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#ifdef BUILD_SMPP -#include "smpp_smsc.h" -#endif - -void *tall_gsms_ctx; -static uint32_t new_callref = 0x40000001; - - -struct gsm_sms *sms_alloc(void) -{ - return talloc_zero(tall_gsms_ctx, struct gsm_sms); -} - -void sms_free(struct gsm_sms *sms) -{ - /* drop references to subscriber structure */ - if (sms->receiver) - subscr_put(sms->receiver); -#ifdef BUILD_SMPP - if (sms->smpp.esme) - smpp_esme_put(sms->smpp.esme); -#endif - - talloc_free(sms); -} - -struct gsm_sms *sms_from_text(struct gsm_subscriber *receiver, - struct gsm_subscriber *sender, - int dcs, const char *text) -{ - struct gsm_sms *sms = sms_alloc(); - - if (!sms) - return NULL; - - sms->receiver = subscr_get(receiver); - osmo_strlcpy(sms->text, text, sizeof(sms->text)); - - osmo_strlcpy(sms->src.addr, sender->extension, sizeof(sms->src.addr)); - sms->reply_path_req = 0; - sms->status_rep_req = 0; - sms->ud_hdr_ind = 0; - sms->protocol_id = 0; /* implicit */ - sms->data_coding_scheme = dcs; - osmo_strlcpy(sms->dst.addr, receiver->extension, sizeof(sms->dst.addr)); - /* Generate user_data */ - sms->user_data_len = gsm_7bit_encode_n(sms->user_data, sizeof(sms->user_data), - sms->text, NULL); - - return sms; -} - - -static void send_signal(int sig_no, - struct gsm_trans *trans, - struct gsm_sms *sms, - int paging_result) -{ - struct sms_signal_data sig; - sig.trans = trans; - sig.sms = sms; - sig.paging_result = paging_result; - osmo_signal_dispatch(SS_SMS, sig_no, &sig); -} - -static int gsm411_sendmsg(struct gsm_subscriber_connection *conn, struct msgb *msg) -{ - DEBUGP(DLSMS, "GSM4.11 TX %s\n", osmo_hexdump(msg->data, msg->len)); - msg->l3h = msg->data; - return gsm0808_submit_dtap(conn, msg, UM_SAPI_SMS, 1); -} - -/* Prefix msg with a 04.08/04.11 CP header */ -static int gsm411_cp_sendmsg(struct msgb *msg, struct gsm_trans *trans, - uint8_t msg_type) -{ - struct gsm48_hdr *gh; - - gh = (struct gsm48_hdr *) msgb_push(msg, sizeof(*gh)); - /* Outgoing needs the highest bit set */ - gh->proto_discr = trans->protocol | (trans->transaction_id<<4); - gh->msg_type = msg_type; - - DEBUGP(DLSMS, "sending CP message (trans=%x)\n", trans->transaction_id); - - return gsm411_sendmsg(trans->conn, msg); -} - -/* mm_send: receive MMCCSMS sap message from SMC */ -static int gsm411_mm_send(struct gsm411_smc_inst *inst, int msg_type, - struct msgb *msg, int cp_msg_type) -{ - struct gsm_trans *trans = - container_of(inst, struct gsm_trans, sms.smc_inst); - int rc = 0; - - switch (msg_type) { - case GSM411_MMSMS_EST_REQ: - /* recycle msg */ - rc = gsm411_smc_recv(inst, GSM411_MMSMS_EST_CNF, msg, 0); - msgb_free(msg); /* upper layer does not free msg */ - break; - case GSM411_MMSMS_DATA_REQ: - rc = gsm411_cp_sendmsg(msg, trans, cp_msg_type); - break; - case GSM411_MMSMS_REL_REQ: - DEBUGP(DLSMS, "Got MMSMS_REL_REQ, destroying transaction.\n"); - msgb_free(msg); - trans_free(trans); - break; - default: - LOGP(DLSMS, LOGL_NOTICE, "Unhandled MMCCSMS msg 0x%x\n", msg_type); - msgb_free(msg); - rc = -EINVAL; - } - - return rc; -} - -/* mm_send: receive MNCCSMS sap message from SMR */ -int gsm411_mn_send(struct gsm411_smr_inst *inst, int msg_type, - struct msgb *msg) -{ - struct gsm_trans *trans = - container_of(inst, struct gsm_trans, sms.smr_inst); - - /* forward to SMC */ - return gsm411_smc_send(&trans->sms.smc_inst, msg_type, msg); -} - -static int gsm340_rx_sms_submit(struct msgb *msg, struct gsm_sms *gsms) -{ - if (db_sms_store(gsms) != 0) { - LOGP(DLSMS, LOGL_ERROR, "Failed to store SMS in Database\n"); - return GSM411_RP_CAUSE_MO_NET_OUT_OF_ORDER; - } - /* dispatch a signal to tell higher level about it */ - send_signal(S_SMS_SUBMITTED, NULL, gsms, 0); - - return 0; -} - -/* generate a TPDU address field compliant with 03.40 sec. 9.1.2.5 */ -static int gsm340_gen_oa_sub(uint8_t *oa, unsigned int oa_len, - const struct gsm_sms_addr *src) -{ - /* network specific, private numbering plan */ - return gsm340_gen_oa(oa, oa_len, src->ton, src->npi, src->addr); -} - -/* generate a msgb containing an 03.40 9.2.2.1 SMS-DELIVER TPDU derived from - * struct gsm_sms, returns total size of TPDU */ -static int gsm340_gen_sms_deliver_tpdu(struct msgb *msg, struct gsm_sms *sms) -{ - uint8_t *smsp; - uint8_t oa[12]; /* max len per 03.40 */ - uint8_t oa_len = 0; - uint8_t octet_len; - unsigned int old_msg_len = msg->len; - - /* generate first octet with masked bits */ - smsp = msgb_put(msg, 1); - /* TP-MTI (message type indicator) */ - *smsp = GSM340_SMS_DELIVER_SC2MS; - /* TP-MMS (more messages to send) */ - if (0 /* FIXME */) - *smsp |= 0x04; - /* TP-SRI(deliver)/SRR(submit) */ - if (sms->status_rep_req) - *smsp |= 0x20; - /* TP-UDHI (indicating TP-UD contains a header) */ - if (sms->ud_hdr_ind) - *smsp |= 0x40; - - /* generate originator address */ - oa_len = gsm340_gen_oa_sub(oa, sizeof(oa), &sms->src); - smsp = msgb_put(msg, oa_len); - memcpy(smsp, oa, oa_len); - - /* generate TP-PID */ - smsp = msgb_put(msg, 1); - *smsp = sms->protocol_id; - - /* generate TP-DCS */ - smsp = msgb_put(msg, 1); - *smsp = sms->data_coding_scheme; - - /* generate TP-SCTS */ - smsp = msgb_put(msg, 7); - gsm340_gen_scts(smsp, time(NULL)); - - /* generate TP-UDL */ - smsp = msgb_put(msg, 1); - *smsp = sms->user_data_len; - - /* generate TP-UD */ - switch (gsm338_get_sms_alphabet(sms->data_coding_scheme)) { - case DCS_7BIT_DEFAULT: - octet_len = sms->user_data_len*7/8; - if (sms->user_data_len*7%8 != 0) - octet_len++; - /* Warning, user_data_len indicates the amount of septets - * (characters), we need amount of octets occupied */ - smsp = msgb_put(msg, octet_len); - memcpy(smsp, sms->user_data, octet_len); - break; - case DCS_UCS2: - case DCS_8BIT_DATA: - smsp = msgb_put(msg, sms->user_data_len); - memcpy(smsp, sms->user_data, sms->user_data_len); - break; - default: - LOGP(DLSMS, LOGL_NOTICE, "Unhandled Data Coding Scheme: 0x%02X\n", - sms->data_coding_scheme); - break; - } - - return msg->len - old_msg_len; -} - -int sms_route_mt_sms(struct gsm_subscriber_connection *conn, struct msgb *msg, - struct gsm_sms *gsms, uint8_t sms_mti, bool *deferred) -{ - int rc; - -#ifdef BUILD_SMPP - int smpp_first = smpp_route_smpp_first(gsms, conn); - - /* - * Route through SMPP first before going to the local database. In case - * of a unroutable message and no local subscriber, SMPP will be tried - * twice. In case of an unknown subscriber continue with the normal - * delivery of the SMS. - */ - if (smpp_first) { - rc = smpp_try_deliver(gsms, conn, deferred); - if (rc == GSM411_RP_CAUSE_MO_NUM_UNASSIGNED) - goto try_local; - if (rc < 0) { - LOGP(DLSMS, LOGL_ERROR, "%s: SMS delivery error: %d.", - subscr_name(conn->subscr), rc); - rc = GSM411_RP_CAUSE_MO_TEMP_FAIL; - /* rc will be logged by gsm411_send_rp_error() */ - rate_ctr_inc(&conn->bts->network->msc_ctrs->ctr[ - MSC_CTR_SMS_DELIVER_UNKNOWN_ERROR]); - } - return rc; - } - -try_local: -#endif - - /* determine gsms->receiver based on dialled number */ - gsms->receiver = subscr_get_by_extension(conn->network->subscr_group, - gsms->dst.addr); - if (!gsms->receiver) { -#ifdef BUILD_SMPP - /* Avoid a second look-up */ - if (smpp_first) { - rate_ctr_inc(&conn->network->msc_ctrs->ctr[MSC_CTR_SMS_NO_RECEIVER]); - return GSM411_RP_CAUSE_MO_NUM_UNASSIGNED; - } - - rc = smpp_try_deliver(gsms, conn, deferred); - if (rc == GSM411_RP_CAUSE_MO_NUM_UNASSIGNED) { - rate_ctr_inc(&conn->network->msc_ctrs->ctr[MSC_CTR_SMS_NO_RECEIVER]); - } else if (rc < 0) { - LOGP(DLSMS, LOGL_ERROR, "%s: SMS delivery error: %d.", - subscr_name(conn->subscr), rc); - rc = GSM411_RP_CAUSE_MO_TEMP_FAIL; - /* rc will be logged by gsm411_send_rp_error() */ - rate_ctr_inc(&conn->bts->network->msc_ctrs->ctr[ - MSC_CTR_SMS_DELIVER_UNKNOWN_ERROR]); - } -#else - rc = GSM411_RP_CAUSE_MO_NUM_UNASSIGNED; - rate_ctr_inc(&conn->network->msc_ctrs->ctr[MSC_CTR_SMS_NO_RECEIVER]); -#endif - return rc; - } - - switch (sms_mti) { - case GSM340_SMS_SUBMIT_MS2SC: - /* MS is submitting a SMS */ - rc = gsm340_rx_sms_submit(msg, gsms); - break; - case GSM340_SMS_COMMAND_MS2SC: - case GSM340_SMS_DELIVER_REP_MS2SC: - LOGP(DLSMS, LOGL_NOTICE, "Unimplemented MTI 0x%02x\n", sms_mti); - rc = GSM411_RP_CAUSE_IE_NOTEXIST; - break; - default: - LOGP(DLSMS, LOGL_NOTICE, "Undefined MTI 0x%02x\n", sms_mti); - rc = GSM411_RP_CAUSE_IE_NOTEXIST; - break; - } - - if (!rc && !gsms->receiver) - rc = GSM411_RP_CAUSE_MO_NUM_UNASSIGNED; - - return rc; -} - - -/* process an incoming TPDU (called from RP-DATA) - * return value > 0: RP CAUSE for ERROR; < 0: silent error; 0 = success */ -static int gsm340_rx_tpdu(struct gsm_trans *trans, struct msgb *msg, - uint32_t gsm411_msg_ref, bool *deferred) -{ - struct gsm_subscriber_connection *conn = trans->conn; - uint8_t *smsp = msgb_sms(msg); - struct gsm_sms *gsms; - unsigned int sms_alphabet; - uint8_t sms_mti, sms_vpf; - uint8_t *sms_vp; - uint8_t da_len_bytes; - uint8_t address_lv[12]; /* according to 03.40 / 9.1.2.5 */ - int rc = 0; - - rate_ctr_inc(&conn->network->msc_ctrs->ctr[MSC_CTR_SMS_SUBMITTED]); - - gsms = sms_alloc(); - if (!gsms) - return GSM411_RP_CAUSE_MO_NET_OUT_OF_ORDER; - - /* invert those fields where 0 means active/present */ - sms_mti = *smsp & 0x03; - sms_vpf = (*smsp & 0x18) >> 3; - gsms->status_rep_req = (*smsp & 0x20); - gsms->ud_hdr_ind = (*smsp & 0x40); - /* - * Not evaluating MMS (More Messages to Send) because the - * lchan stays open anyway. - * Not evaluating RP (Reply Path) because we're not aware of its - * benefits. - */ - - smsp++; - gsms->msg_ref = *smsp++; - - gsms->gsm411.transaction_id = trans->transaction_id; - gsms->gsm411.msg_ref = gsm411_msg_ref; - - /* length in bytes of the destination address */ - da_len_bytes = 2 + *smsp/2 + *smsp%2; - if (da_len_bytes > 12) { - LOGP(DLSMS, LOGL_ERROR, "Destination Address > 12 bytes ?!?\n"); - rc = GSM411_RP_CAUSE_SEMANT_INC_MSG; - goto out; - } else if (da_len_bytes < 4) { - LOGP(DLSMS, LOGL_ERROR, "Destination Address < 4 bytes ?!?\n"); - rc = GSM411_RP_CAUSE_SEMANT_INC_MSG; - goto out; - } - memset(address_lv, 0, sizeof(address_lv)); - memcpy(address_lv, smsp, da_len_bytes); - /* mangle first byte to reflect length in bytes, not digits */ - address_lv[0] = da_len_bytes - 1; - - gsms->dst.ton = (address_lv[1] >> 4) & 7; - gsms->dst.npi = address_lv[1] & 0xF; - /* convert to real number */ - gsm48_decode_bcd_number(gsms->dst.addr, - sizeof(gsms->dst.addr), address_lv, 1); - smsp += da_len_bytes; - - gsms->protocol_id = *smsp++; - gsms->data_coding_scheme = *smsp++; - - sms_alphabet = gsm338_get_sms_alphabet(gsms->data_coding_scheme); - if (sms_alphabet == 0xffffffff) { - rc = GSM411_RP_CAUSE_MO_NET_OUT_OF_ORDER; - goto out; - } - - switch (sms_vpf) { - case GSM340_TP_VPF_RELATIVE: - sms_vp = smsp++; - break; - case GSM340_TP_VPF_ABSOLUTE: - case GSM340_TP_VPF_ENHANCED: - sms_vp = smsp; - /* the additional functionality indicator... */ - if (sms_vpf == GSM340_TP_VPF_ENHANCED && *smsp & (1<<7)) - smsp++; - smsp += 7; - break; - case GSM340_TP_VPF_NONE: - sms_vp = 0; - break; - default: - LOGP(DLSMS, LOGL_NOTICE, - "SMS Validity period not implemented: 0x%02x\n", sms_vpf); - rc = GSM411_RP_CAUSE_MO_NET_OUT_OF_ORDER; - goto out; - } - gsms->user_data_len = *smsp++; - if (gsms->user_data_len) { - memcpy(gsms->user_data, smsp, gsms->user_data_len); - - switch (sms_alphabet) { - case DCS_7BIT_DEFAULT: - gsm_7bit_decode_n(gsms->text, sizeof(gsms->text), smsp, - gsms->user_data_len); - break; - case DCS_8BIT_DATA: - case DCS_UCS2: - case DCS_NONE: - break; - } - } - - osmo_strlcpy(gsms->src.addr, conn->subscr->extension, - sizeof(gsms->src.addr)); - - LOGP(DLSMS, LOGL_INFO, "RX SMS: Sender: %s, MTI: 0x%02x, VPF: 0x%02x, " - "MR: 0x%02x PID: 0x%02x, DCS: 0x%02x, DA: %s, " - "UserDataLength: 0x%02x, UserData: \"%s\"\n", - subscr_name(conn->subscr), sms_mti, sms_vpf, gsms->msg_ref, - gsms->protocol_id, gsms->data_coding_scheme, gsms->dst.addr, - gsms->user_data_len, - sms_alphabet == DCS_7BIT_DEFAULT ? gsms->text : - osmo_hexdump(gsms->user_data, gsms->user_data_len)); - - gsms->validity_minutes = gsm340_validity_period(sms_vpf, sms_vp); - - /* FIXME: This looks very wrong */ - send_signal(0, NULL, gsms, 0); - - rc = sms_route_mt_sms(conn, msg, gsms, sms_mti, deferred); -out: - if (!deferred) - sms_free(gsms); - - return rc; -} - -/* Prefix msg with a RP-DATA header and send as SMR DATA */ -static int gsm411_rp_sendmsg(struct gsm411_smr_inst *inst, struct msgb *msg, - uint8_t rp_msg_type, uint8_t rp_msg_ref, - int rl_msg_type) -{ - struct gsm411_rp_hdr *rp; - uint8_t len = msg->len; - - /* GSM 04.11 RP-DATA header */ - rp = (struct gsm411_rp_hdr *)msgb_push(msg, sizeof(*rp)); - rp->len = len + 2; - rp->msg_type = rp_msg_type; - rp->msg_ref = rp_msg_ref; - - return gsm411_smr_send(inst, rl_msg_type, msg); -} - -int gsm411_send_rp_ack(struct gsm_trans *trans, uint8_t msg_ref) -{ - struct msgb *msg = gsm411_msgb_alloc(); - - DEBUGP(DLSMS, "TX: SMS RP ACK\n"); - - return gsm411_rp_sendmsg(&trans->sms.smr_inst, msg, GSM411_MT_RP_ACK_MT, - msg_ref, GSM411_SM_RL_REPORT_REQ); -} - -int gsm411_send_rp_error(struct gsm_trans *trans, uint8_t msg_ref, - uint8_t cause) -{ - struct msgb *msg = gsm411_msgb_alloc(); - - msgb_tv_put(msg, 1, cause); - - LOGP(DLSMS, LOGL_NOTICE, "TX: SMS RP ERROR, cause %d (%s)\n", cause, - get_value_string(gsm411_rp_cause_strs, cause)); - - return gsm411_rp_sendmsg(&trans->sms.smr_inst, msg, - GSM411_MT_RP_ERROR_MT, msg_ref, GSM411_SM_RL_REPORT_REQ); -} - -/* Receive a 04.11 TPDU inside RP-DATA / user data */ -static int gsm411_rx_rp_ud(struct msgb *msg, struct gsm_trans *trans, - struct gsm411_rp_hdr *rph, - uint8_t src_len, uint8_t *src, - uint8_t dst_len, uint8_t *dst, - uint8_t tpdu_len, uint8_t *tpdu) -{ - bool deferred = false; - int rc = 0; - - if (src_len && src) - LOGP(DLSMS, LOGL_ERROR, "RP-DATA (MO) with SRC ?!?\n"); - - if (!dst_len || !dst || !tpdu_len || !tpdu) { - LOGP(DLSMS, LOGL_ERROR, - "RP-DATA (MO) without DST or TPDU ?!?\n"); - gsm411_send_rp_error(trans, rph->msg_ref, - GSM411_RP_CAUSE_INV_MAND_INF); - return -EIO; - } - msg->l4h = tpdu; - - DEBUGP(DLSMS, "DST(%u,%s)\n", dst_len, osmo_hexdump(dst, dst_len)); - - rc = gsm340_rx_tpdu(trans, msg, rph->msg_ref, &deferred); - if (rc == 0 && !deferred) - return gsm411_send_rp_ack(trans, rph->msg_ref); - else if (rc > 0) - return gsm411_send_rp_error(trans, rph->msg_ref, rc); - else - return rc; -} - -/* Receive a 04.11 RP-DATA message in accordance with Section 7.3.1.2 */ -static int gsm411_rx_rp_data(struct msgb *msg, struct gsm_trans *trans, - struct gsm411_rp_hdr *rph) -{ - uint8_t src_len, dst_len, rpud_len; - uint8_t *src = NULL, *dst = NULL , *rp_ud = NULL; - - /* in the MO case, this should always be zero length */ - src_len = rph->data[0]; - if (src_len) - src = &rph->data[1]; - - dst_len = rph->data[1+src_len]; - if (dst_len) - dst = &rph->data[1+src_len+1]; - - rpud_len = rph->data[1+src_len+1+dst_len]; - if (rpud_len) - rp_ud = &rph->data[1+src_len+1+dst_len+1]; - - DEBUGP(DLSMS, "RX_RP-DATA: src_len=%u, dst_len=%u ud_len=%u\n", - src_len, dst_len, rpud_len); - return gsm411_rx_rp_ud(msg, trans, rph, src_len, src, dst_len, dst, - rpud_len, rp_ud); -} - -/* Receive a 04.11 RP-ACK message (response to RP-DATA from us) */ -static int gsm411_rx_rp_ack(struct msgb *msg, struct gsm_trans *trans, - struct gsm411_rp_hdr *rph) -{ - struct gsm_sms *sms = trans->sms.sms; - - /* Acnkowledgement to MT RP_DATA, i.e. the MS confirms it - * successfully received a SMS. We can now safely mark it as - * transmitted */ - - if (!sms) { - LOGP(DLSMS, LOGL_ERROR, "RX RP-ACK but no sms in transaction?!?\n"); - return gsm411_send_rp_error(trans, rph->msg_ref, - GSM411_RP_CAUSE_PROTOCOL_ERR); - } - - /* mark this SMS as sent in database */ - db_sms_mark_delivered(sms); - - send_signal(S_SMS_DELIVERED, trans, sms, 0); - - sms_free(sms); - trans->sms.sms = NULL; - - return 0; -} - -static int gsm411_rx_rp_error(struct msgb *msg, struct gsm_trans *trans, - struct gsm411_rp_hdr *rph) -{ - struct gsm_network *net = trans->conn->network; - struct gsm_sms *sms = trans->sms.sms; - uint8_t cause_len = rph->data[0]; - uint8_t cause = rph->data[1]; - - /* Error in response to MT RP_DATA, i.e. the MS did not - * successfully receive the SMS. We need to investigate - * the cause and take action depending on it */ - - LOGP(DLSMS, LOGL_NOTICE, "%s: RX SMS RP-ERROR, cause %d:%d (%s)\n", - subscr_name(trans->conn->subscr), cause_len, cause, - get_value_string(gsm411_rp_cause_strs, cause)); - - if (!sms) { - LOGP(DLSMS, LOGL_ERROR, - "RX RP-ERR, but no sms in transaction?!?\n"); - return -EINVAL; -#if 0 - return gsm411_send_rp_error(trans, rph->msg_ref, - GSM411_RP_CAUSE_PROTOCOL_ERR); -#endif - } - - if (cause == GSM411_RP_CAUSE_MT_MEM_EXCEEDED) { - /* MS has not enough memory to store the message. We need - * to store this in our database and wait for a SMMA message */ - /* FIXME */ - send_signal(S_SMS_MEM_EXCEEDED, trans, sms, 0); - rate_ctr_inc(&net->msc_ctrs->ctr[MSC_CTR_SMS_RP_ERR_MEM]); - } else { - send_signal(S_SMS_UNKNOWN_ERROR, trans, sms, 0); - rate_ctr_inc(&net->msc_ctrs->ctr[MSC_CTR_SMS_RP_ERR_OTHER]); - } - - sms_free(sms); - trans->sms.sms = NULL; - - return 0; -} - -static int gsm411_rx_rp_smma(struct msgb *msg, struct gsm_trans *trans, - struct gsm411_rp_hdr *rph) -{ - int rc; - - rc = gsm411_send_rp_ack(trans, rph->msg_ref); - - /* MS tells us that it has memory for more SMS, we need - * to check if we have any pending messages for it and then - * transfer those */ - send_signal(S_SMS_SMMA, trans, NULL, 0); - - return rc; -} - -/* receive RL DATA */ -static int gsm411_rx_rl_data(struct msgb *msg, struct gsm48_hdr *gh, - struct gsm_trans *trans) -{ - struct gsm411_rp_hdr *rp_data = (struct gsm411_rp_hdr*)&gh->data; - uint8_t msg_type = rp_data->msg_type & 0x07; - int rc = 0; - - switch (msg_type) { - case GSM411_MT_RP_DATA_MO: - DEBUGP(DLSMS, "RX SMS RP-DATA (MO)\n"); - rc = gsm411_rx_rp_data(msg, trans, rp_data); - break; - case GSM411_MT_RP_SMMA_MO: - DEBUGP(DLSMS, "RX SMS RP-SMMA\n"); - rc = gsm411_rx_rp_smma(msg, trans, rp_data); - break; - default: - LOGP(DLSMS, LOGL_NOTICE, "Invalid RP type 0x%02x\n", msg_type); - rc = -EINVAL; - break; - } - - return rc; -} - -/* receive RL REPORT */ -static int gsm411_rx_rl_report(struct msgb *msg, struct gsm48_hdr *gh, - struct gsm_trans *trans) -{ - struct gsm411_rp_hdr *rp_data = (struct gsm411_rp_hdr*)&gh->data; - uint8_t msg_type = rp_data->msg_type & 0x07; - int rc = 0; - - switch (msg_type) { - case GSM411_MT_RP_ACK_MO: - DEBUGP(DLSMS, "RX SMS RP-ACK (MO)\n"); - rc = gsm411_rx_rp_ack(msg, trans, rp_data); - break; - case GSM411_MT_RP_ERROR_MO: - DEBUGP(DLSMS, "RX SMS RP-ERROR (MO)\n"); - rc = gsm411_rx_rp_error(msg, trans, rp_data); - break; - default: - LOGP(DLSMS, LOGL_NOTICE, "Invalid RP type 0x%02x\n", msg_type); - rc = -EINVAL; - break; - } - - return rc; -} - -/* receive SM-RL sap message from SMR - * NOTE: Message is freed by sender - */ -int gsm411_rl_recv(struct gsm411_smr_inst *inst, int msg_type, - struct msgb *msg) -{ - struct gsm_trans *trans = - container_of(inst, struct gsm_trans, sms.smr_inst); - struct gsm48_hdr *gh = msgb_l3(msg); - int rc = 0; - - switch (msg_type) { - case GSM411_SM_RL_DATA_IND: - rc = gsm411_rx_rl_data(msg, gh, trans); - break; - case GSM411_SM_RL_REPORT_IND: - if (gh) - rc = gsm411_rx_rl_report(msg, gh, trans); - break; - default: - LOGP(DLSMS, LOGL_NOTICE, "Unhandled SM-RL message 0x%x\n", msg_type); - rc = -EINVAL; - } - - return rc; -} - -/* receive MNCCSMS sap message from SMC - * NOTE: Message is freed by sender - */ -static int gsm411_mn_recv(struct gsm411_smc_inst *inst, int msg_type, - struct msgb *msg) -{ - struct gsm_trans *trans = - container_of(inst, struct gsm_trans, sms.smc_inst); - struct gsm48_hdr *gh = msgb_l3(msg); - int rc = 0; - - switch (msg_type) { - case GSM411_MNSMS_EST_IND: - case GSM411_MNSMS_DATA_IND: - DEBUGP(DLSMS, "MNSMS-DATA/EST-IND\n"); - rc = gsm411_smr_recv(&trans->sms.smr_inst, msg_type, msg); - break; - case GSM411_MNSMS_ERROR_IND: - if (gh) - DEBUGP(DLSMS, "MNSMS-ERROR-IND, cause %d (%s)\n", - gh->data[0], - get_value_string(gsm411_cp_cause_strs, - gh->data[0])); - else - DEBUGP(DLSMS, "MNSMS-ERROR-IND, no cause\n"); - rc = gsm411_smr_recv(&trans->sms.smr_inst, msg_type, msg); - break; - default: - LOGP(DLSMS, LOGL_NOTICE, "Unhandled MNCCSMS msg 0x%x\n", msg_type); - rc = -EINVAL; - } - - return rc; -} - -/* Entry point for incoming GSM48_PDISC_SMS from abis_rsl.c */ -int gsm0411_rcv_sms(struct gsm_subscriber_connection *conn, - struct msgb *msg) -{ - struct gsm48_hdr *gh = msgb_l3(msg); - uint8_t msg_type = gh->msg_type; - uint8_t transaction_id = gsm48_hdr_trans_id_flip_ti(gh); - struct gsm_trans *trans; - int new_trans = 0; - int rc = 0; - - if (!conn->subscr) - return -EIO; - /* FIXME: send some error message */ - - DEBUGP(DLSMS, "receiving data (trans_id=%x)\n", transaction_id); - trans = trans_find_by_id(conn, GSM48_PDISC_SMS, transaction_id); - - /* - * A transaction we created but don't know about? - */ - if (!trans && (transaction_id & 0x8) == 0) { - LOGP(DLSMS, LOGL_ERROR, "trans_id=%x allocated by us but known " - "to us anymore. We are ignoring it, maybe a CP-ERROR " - "from a MS?\n", - transaction_id); - return -EINVAL; - } - - if (!trans) { - DEBUGP(DLSMS, " -> (new transaction)\n"); - trans = trans_alloc(conn->network, conn->subscr, - GSM48_PDISC_SMS, - transaction_id, new_callref++); - if (!trans) { - DEBUGP(DLSMS, " -> No memory for trans\n"); - /* FIXME: send some error message */ - return -ENOMEM; - } - gsm411_smc_init(&trans->sms.smc_inst, 0, 1, - gsm411_mn_recv, gsm411_mm_send); - gsm411_smr_init(&trans->sms.smr_inst, 0, 1, - gsm411_rl_recv, gsm411_mn_send); - - trans->conn = conn; - - new_trans = 1; - } - - /* 5.4: For MO, if a CP-DATA is received for a new - * transaction, equals reception of an implicit - * last CP-ACK for previous transaction */ - if (trans->sms.smc_inst.cp_state == GSM411_CPS_IDLE - && msg_type == GSM411_MT_CP_DATA) { - int i; - struct gsm_trans *ptrans; - - /* Scan through all remote initiated transactions */ - for (i=8; i<15; i++) { - if (i == transaction_id) - continue; - - ptrans = trans_find_by_id(conn, GSM48_PDISC_SMS, i); - if (!ptrans) - continue; - - DEBUGP(DLSMS, "Implicit CP-ACK for trans_id=%x\n", i); - - /* Finish it for good */ - trans_free(ptrans); - } - } - - gsm411_smc_recv(&trans->sms.smc_inst, - (new_trans) ? GSM411_MMSMS_EST_IND : GSM411_MMSMS_DATA_IND, - msg, msg_type); - - return rc; -} - -/* Take a SMS in gsm_sms structure and send it through an already - * existing lchan. We also assume that the caller ensured this lchan already - * has a SAPI3 RLL connection! */ -int gsm411_send_sms(struct gsm_subscriber_connection *conn, struct gsm_sms *sms) -{ - struct msgb *msg = gsm411_msgb_alloc(); - struct gsm_trans *trans; - uint8_t *data, *rp_ud_len; - uint8_t msg_ref = sms_next_rp_msg_ref(&conn->next_rp_ref); - int transaction_id; - int rc; - - transaction_id = - trans_assign_trans_id(conn->network, conn->subscr, - GSM48_PDISC_SMS, 0); - if (transaction_id == -1) { - LOGP(DLSMS, LOGL_ERROR, "No available transaction ids\n"); - send_signal(S_SMS_UNKNOWN_ERROR, NULL, sms, 0); - sms_free(sms); - msgb_free(msg); - return -EBUSY; - } - - DEBUGP(DLSMS, "%s()\n", __func__); - - /* FIXME: allocate transaction with message reference */ - trans = trans_alloc(conn->network, conn->subscr, - GSM48_PDISC_SMS, - transaction_id, new_callref++); - if (!trans) { - LOGP(DLSMS, LOGL_ERROR, "No memory for trans\n"); - send_signal(S_SMS_UNKNOWN_ERROR, NULL, sms, 0); - sms_free(sms); - msgb_free(msg); - /* FIXME: send some error message */ - return -ENOMEM; - } - gsm411_smc_init(&trans->sms.smc_inst, sms->id, 1, - gsm411_mn_recv, gsm411_mm_send); - gsm411_smr_init(&trans->sms.smr_inst, sms->id, 1, - gsm411_rl_recv, gsm411_mn_send); - trans->sms.sms = sms; - - trans->conn = conn; - - /* Hardcode SMSC Originating Address for now */ - data = (uint8_t *)msgb_put(msg, 8); - data[0] = 0x07; /* originator length == 7 */ - data[1] = 0x91; /* type of number: international, ISDN */ - data[2] = 0x44; /* 447785016005 */ - data[3] = 0x77; - data[4] = 0x58; - data[5] = 0x10; - data[6] = 0x06; - data[7] = 0x50; - - /* Hardcoded Destination Address */ - data = (uint8_t *)msgb_put(msg, 1); - data[0] = 0; /* destination length == 0 */ - - /* obtain a pointer for the rp_ud_len, so we can fill it later */ - rp_ud_len = (uint8_t *)msgb_put(msg, 1); - - /* generate the 03.40 SMS-DELIVER TPDU */ - rc = gsm340_gen_sms_deliver_tpdu(msg, sms); - if (rc < 0) { - send_signal(S_SMS_UNKNOWN_ERROR, trans, sms, 0); - sms_free(sms); - trans->sms.sms = NULL; - trans_free(trans); - msgb_free(msg); - return rc; - } - - *rp_ud_len = rc; - - DEBUGP(DLSMS, "TX: SMS DELIVER\n"); - - rate_ctr_inc(&conn->network->msc_ctrs->ctr[MSC_CTR_SMS_DELIVERED]); - db_sms_inc_deliver_attempts(trans->sms.sms); - - return gsm411_rp_sendmsg(&trans->sms.smr_inst, msg, - GSM411_MT_RP_DATA_MT, msg_ref, GSM411_SM_RL_DATA_REQ); -} - -/* paging callback. Here we get called if paging a subscriber has - * succeeded or failed. */ -static int paging_cb_send_sms(unsigned int hooknum, unsigned int event, - struct msgb *msg, void *_conn, void *_sms) -{ - struct gsm_subscriber_connection *conn = _conn; - struct gsm_sms *sms = _sms; - int rc = 0; - - DEBUGP(DLSMS, "paging_cb_send_sms(hooknum=%u, event=%u, msg=%p," - "conn=%p, sms=%p/id: %llu)\n", hooknum, event, msg, conn, sms, sms->id); - - if (hooknum != GSM_HOOK_RR_PAGING) - return -EINVAL; - - switch (event) { - case GSM_PAGING_SUCCEEDED: - gsm411_send_sms(conn, sms); - break; - case GSM_PAGING_EXPIRED: - case GSM_PAGING_OOM: - case GSM_PAGING_BUSY: - send_signal(S_SMS_UNKNOWN_ERROR, NULL, sms, event); - sms_free(sms); - rc = -ETIMEDOUT; - break; - default: - LOGP(DLSMS, LOGL_ERROR, "Unhandled paging event: %d\n", event); - } - - return rc; -} - -/* high-level function to send a SMS to a given subscriber. The function - * will take care of paging the subscriber, establishing the RLL SAPI3 - * connection, etc. */ -int gsm411_send_sms_subscr(struct gsm_subscriber *subscr, - struct gsm_sms *sms) -{ - struct gsm_subscriber_connection *conn; - void *res; - - /* check if we already have an open lchan to the subscriber. - * if yes, send the SMS this way */ - conn = connection_for_subscr(subscr); - if (conn) { - LOGP(DLSMS, LOGL_DEBUG, "Sending SMS via already open connection %p to %s\n", - conn, subscr_name(subscr)); - return gsm411_send_sms(conn, sms); - } - - /* if not, we have to start paging */ - LOGP(DLSMS, LOGL_DEBUG, "Sending SMS: no connection open, start paging %s\n", - subscr_name(subscr)); - res = subscr_request_channel(subscr, RSL_CHANNEED_SDCCH, - paging_cb_send_sms, sms); - if (!res) { - send_signal(S_SMS_UNKNOWN_ERROR, NULL, sms, GSM_PAGING_BUSY); - sms_free(sms); - } - return 0; -} - -void _gsm411_sms_trans_free(struct gsm_trans *trans) -{ - /* cleanup SMS instance */ - gsm411_smr_clear(&trans->sms.smr_inst); - trans->sms.smr_inst.rl_recv = NULL; - trans->sms.smr_inst.mn_send = NULL; - - gsm411_smc_clear(&trans->sms.smc_inst); - trans->sms.smc_inst.mn_recv = NULL; - trans->sms.smc_inst.mm_send = NULL; - - if (trans->sms.sms) { - LOGP(DLSMS, LOGL_ERROR, "Transaction contains SMS.\n"); - send_signal(S_SMS_UNKNOWN_ERROR, trans, trans->sms.sms, 0); - sms_free(trans->sms.sms); - trans->sms.sms = NULL; - } -} - -void gsm411_sapi_n_reject(struct gsm_subscriber_connection *conn) -{ - struct gsm_network *net; - struct gsm_trans *trans, *tmp; - - net = conn->network; - - llist_for_each_entry_safe(trans, tmp, &net->trans_list, entry) { - struct gsm_sms *sms; - - if (trans->conn != conn) - continue; - if (trans->protocol != GSM48_PDISC_SMS) - continue; - - sms = trans->sms.sms; - if (!sms) { - LOGP(DLSMS, LOGL_ERROR, "SAPI Reject but no SMS.\n"); - continue; - } - - send_signal(S_SMS_UNKNOWN_ERROR, trans, sms, 0); - sms_free(sms); - trans->sms.sms = NULL; - trans_free(trans); - } -} - diff --git a/openbsc/src/libmsc/gsm_04_80.c b/openbsc/src/libmsc/gsm_04_80.c deleted file mode 100644 index 479d6fbd..00000000 --- a/openbsc/src/libmsc/gsm_04_80.c +++ /dev/null @@ -1,155 +0,0 @@ -/* GSM Mobile Radio Interface Layer 3 messages on the A-bis interface - * 3GPP TS 04.08 version 7.21.0 Release 1998 / ETSI TS 100 940 V7.21.0 */ - -/* (C) 2008-2009 by Harald Welte - * (C) 2008, 2009, 2010 by Holger Hans Peter Freyther - * (C) 2009 by Mike Haben - * - * All Rights Reserved - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation; either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - * - */ - - -#include -#include -#include -#include - -#include -#include -#include -#include -#include - -#include -#include -#include -#include - -static inline unsigned char *msgb_wrap_with_TL(struct msgb *msgb, uint8_t tag) -{ - uint8_t *data = msgb_push(msgb, 2); - - data[0] = tag; - data[1] = msgb->len - 2; - return data; -} - -static inline unsigned char *msgb_push_TLV1(struct msgb *msgb, uint8_t tag, - uint8_t value) -{ - uint8_t *data = msgb_push(msgb, 3); - - data[0] = tag; - data[1] = 1; - data[2] = value; - return data; -} - - -/* Send response to a mobile-originated ProcessUnstructuredSS-Request */ -int gsm0480_send_ussd_response(struct gsm_subscriber_connection *conn, - const struct msgb *in_msg, const char *response_text, - const struct ss_request *req) -{ - struct msgb *msg = gsm48_msgb_alloc_name("GSM 04.08 USSD RSP"); - struct gsm48_hdr *gh; - uint8_t *ptr8; - int response_len; - - /* First put the payload text into the message */ - ptr8 = msgb_put(msg, 0); - gsm_7bit_encode_n_ussd(ptr8, msgb_tailroom(msg), response_text, &response_len); - msgb_put(msg, response_len); - - /* Then wrap it as an Octet String */ - msgb_wrap_with_TL(msg, ASN1_OCTET_STRING_TAG); - - /* Pre-pend the DCS octet string */ - msgb_push_TLV1(msg, ASN1_OCTET_STRING_TAG, 0x0F); - - /* Then wrap these as a Sequence */ - msgb_wrap_with_TL(msg, GSM_0480_SEQUENCE_TAG); - - /* Pre-pend the operation code */ - msgb_push_TLV1(msg, GSM0480_OPERATION_CODE, - GSM0480_OP_CODE_PROCESS_USS_REQ); - - /* Wrap the operation code and IA5 string as a sequence */ - msgb_wrap_with_TL(msg, GSM_0480_SEQUENCE_TAG); - - /* Pre-pend the invoke ID */ - msgb_push_TLV1(msg, GSM0480_COMPIDTAG_INVOKE_ID, req->invoke_id); - - /* Wrap this up as a Return Result component */ - msgb_wrap_with_TL(msg, GSM0480_CTYPE_RETURN_RESULT); - - /* Wrap the component in a Facility message */ - msgb_wrap_with_TL(msg, GSM0480_IE_FACILITY); - - /* And finally pre-pend the L3 header */ - gh = (struct gsm48_hdr *) msgb_push(msg, sizeof(*gh)); - gh->proto_discr = GSM48_PDISC_NC_SS | req->transaction_id - | (1<<7); /* TI direction = 1 */ - gh->msg_type = GSM0480_MTYPE_RELEASE_COMPLETE; - - return gsm0808_submit_dtap(conn, msg, 0, 0); -} - -int gsm0480_send_ussd_reject(struct gsm_subscriber_connection *conn, - const struct msgb *in_msg, - const struct ss_request *req) -{ - struct msgb *msg = gsm48_msgb_alloc_name("GSM 04.08 USSD REJ"); - struct gsm48_hdr *gh; - - /* First insert the problem code */ - msgb_push_TLV1(msg, GSM_0480_PROBLEM_CODE_TAG_GENERAL, - GSM_0480_GEN_PROB_CODE_UNRECOGNISED); - - /* Before it insert the invoke ID */ - msgb_push_TLV1(msg, GSM0480_COMPIDTAG_INVOKE_ID, req->invoke_id); - - /* Wrap this up as a Reject component */ - msgb_wrap_with_TL(msg, GSM0480_CTYPE_REJECT); - - /* Wrap the component in a Facility message */ - msgb_wrap_with_TL(msg, GSM0480_IE_FACILITY); - - /* And finally pre-pend the L3 header */ - gh = (struct gsm48_hdr *) msgb_push(msg, sizeof(*gh)); - gh->proto_discr = GSM48_PDISC_NC_SS; - gh->proto_discr |= req->transaction_id | (1<<7); /* TI direction = 1 */ - gh->msg_type = GSM0480_MTYPE_RELEASE_COMPLETE; - - return gsm0808_submit_dtap(conn, msg, 0, 0); -} - -int msc_send_ussd_notify(struct gsm_subscriber_connection *conn, int level, const char *text) -{ - struct msgb *msg = gsm0480_create_ussd_notify(level, text); - if (!msg) - return -1; - return gsm0808_submit_dtap(conn, msg, 0, 0); -} - -int msc_send_ussd_release_complete(struct gsm_subscriber_connection *conn) -{ - struct msgb *msg = gsm0480_create_ussd_release_complete(); - if (!msg) - return -1; - return gsm0808_submit_dtap(conn, msg, 0, 0); -} diff --git a/openbsc/src/libmsc/gsm_subscriber.c b/openbsc/src/libmsc/gsm_subscriber.c deleted file mode 100644 index 1a03cf76..00000000 --- a/openbsc/src/libmsc/gsm_subscriber.c +++ /dev/null @@ -1,422 +0,0 @@ -/* The concept of a subscriber for the MSC, roughly HLR/VLR functionality */ - -/* (C) 2008 by Harald Welte - * (C) 2009,2013 by Holger Hans Peter Freyther - * - * All Rights Reserved - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation; either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - * - */ - -#include -#include -#include -#include -#include -#include -#include - -#include - -#include - -#include -#include -#include -#include -#include -#include -#include - -void *tall_sub_req_ctx; - -extern struct llist_head *subscr_bsc_active_subscribers(void); - -int gsm48_secure_channel(struct gsm_subscriber_connection *conn, int key_seq, - gsm_cbfn *cb, void *cb_data); - - -/* - * Struct for pending channel requests. This is managed in the - * llist_head requests of each subscriber. The reference counting - * should work in such a way that a subscriber with a pending request - * remains in memory. - */ -struct subscr_request { - struct llist_head entry; - - /* the callback data */ - gsm_cbfn *cbfn; - void *param; -}; - -static struct gsm_subscriber *get_subscriber(struct gsm_subscriber_group *sgrp, - int type, const char *ident) -{ - struct gsm_subscriber *subscr = db_get_subscriber(type, ident); - if (subscr) - subscr->group = sgrp; - return subscr; -} - -/* - * We got the channel assigned and can now hand this channel - * over to one of our callbacks. - */ -static int subscr_paging_dispatch(unsigned int hooknum, unsigned int event, - struct msgb *msg, void *data, void *param) -{ - struct subscr_request *request, *tmp; - struct gsm_subscriber_connection *conn = data; - struct gsm_subscriber *subscr = param; - struct paging_signal_data sig_data; - struct bsc_subscr *bsub; - struct gsm_network *net; - - OSMO_ASSERT(subscr && subscr->is_paging); - net = subscr->group->net; - - /* - * Stop paging on all other BTS. E.g. if this is - * the first timeout on a BTS then the others will - * timeout soon as well. Let's just stop everything - * and forget we wanted to page. - */ - - /* TODO MSC split -- creating a BSC subscriber directly from MSC data - * structures in RAM. At some point the MSC will send a message to the - * BSC instead. */ - bsub = bsc_subscr_find_or_create_by_imsi(net->bsc_subscribers, - subscr->imsi); - bsub->tmsi = subscr->tmsi; - bsub->lac = subscr->lac; - paging_request_stop(&net->bts_list, NULL, bsub, NULL, NULL); - bsc_subscr_put(bsub); - - /* Inform parts of the system we don't know */ - sig_data.subscr = subscr; - sig_data.bts = conn ? conn->bts : NULL; - sig_data.conn = conn; - sig_data.paging_result = event; - osmo_signal_dispatch( - SS_PAGING, - event == GSM_PAGING_SUCCEEDED ? - S_PAGING_SUCCEEDED : S_PAGING_EXPIRED, - &sig_data - ); - - llist_for_each_entry_safe(request, tmp, &subscr->requests, entry) { - llist_del(&request->entry); - request->cbfn(hooknum, event, msg, data, request->param); - talloc_free(request); - } - - /* balanced with the moment we start paging */ - subscr->is_paging = 0; - subscr_put(subscr); - return 0; -} - -static int subscr_paging_sec_cb(unsigned int hooknum, unsigned int event, - struct msgb *msg, void *data, void *param) -{ - int rc; - - switch (event) { - case GSM_SECURITY_AUTH_FAILED: - /* Dispatch as paging failure */ - rc = subscr_paging_dispatch( - GSM_HOOK_RR_PAGING, GSM_PAGING_EXPIRED, - msg, data, param); - break; - - case GSM_SECURITY_NOAVAIL: - case GSM_SECURITY_SUCCEEDED: - /* Dispatch as paging failure */ - rc = subscr_paging_dispatch( - GSM_HOOK_RR_PAGING, GSM_PAGING_SUCCEEDED, - msg, data, param); - break; - - default: - rc = -EINVAL; - } - - return rc; -} - -static int subscr_paging_cb(unsigned int hooknum, unsigned int event, - struct msgb *msg, void *data, void *param) -{ - struct gsm_subscriber_connection *conn = data; - struct gsm48_hdr *gh; - struct gsm48_pag_resp *pr; - - /* Other cases mean problem, dispatch direclty */ - if (event != GSM_PAGING_SUCCEEDED) - return subscr_paging_dispatch(hooknum, event, msg, data, param); - - /* Get paging response */ - gh = msgb_l3(msg); - pr = (struct gsm48_pag_resp *)gh->data; - - /* We _really_ have a channel, secure it now ! */ - return gsm48_secure_channel(conn, pr->key_seq, subscr_paging_sec_cb, param); -} - -struct subscr_request *subscr_request_channel(struct gsm_subscriber *subscr, - int channel_type, gsm_cbfn *cbfn, void *param) -{ - int rc; - struct subscr_request *request; - struct bsc_subscr *bsub; - struct gsm_network *net = subscr->group->net; - - /* Start paging.. we know it is async so we can do it before */ - if (!subscr->is_paging) { - LOGP(DMM, LOGL_DEBUG, "Subscriber %s not paged yet.\n", - subscr_name(subscr)); - /* TODO MSC split -- creating a BSC subscriber directly from - * MSC data structures in RAM. At some point the MSC will send - * a message to the BSC instead. */ - bsub = bsc_subscr_find_or_create_by_imsi(net->bsc_subscribers, - subscr->imsi); - bsub->tmsi = subscr->tmsi; - bsub->lac = subscr->lac; - rc = paging_request(net, bsub, channel_type, subscr_paging_cb, - subscr); - bsc_subscr_put(bsub); - if (rc <= 0) { - LOGP(DMM, LOGL_ERROR, "Subscriber %s paging failed: %d\n", - subscr_name(subscr), rc); - return NULL; - } - /* reduced on the first paging callback */ - subscr_get(subscr); - subscr->is_paging = 1; - } - - /* TODO: Stop paging in case of memory allocation failure */ - request = talloc_zero(subscr, struct subscr_request); - if (!request) - return NULL; - - request->cbfn = cbfn; - request->param = param; - llist_add_tail(&request->entry, &subscr->requests); - return request; -} - -void subscr_remove_request(struct subscr_request *request) -{ - llist_del(&request->entry); - talloc_free(request); -} - -struct gsm_subscriber *subscr_create_subscriber(struct gsm_subscriber_group *sgrp, - const char *imsi) -{ - struct gsm_subscriber *subscr = db_create_subscriber(imsi, - sgrp->net->ext_min, - sgrp->net->ext_max, - sgrp->net->auto_assign_exten); - if (subscr) - subscr->group = sgrp; - return subscr; -} - -struct gsm_subscriber *subscr_get_by_tmsi(struct gsm_subscriber_group *sgrp, - uint32_t tmsi) -{ - char tmsi_string[14]; - struct gsm_subscriber *subscr; - - /* we might have a record in memory already */ - llist_for_each_entry(subscr, subscr_bsc_active_subscribers(), entry) { - if (tmsi == subscr->tmsi) - return subscr_get(subscr); - } - - sprintf(tmsi_string, "%u", tmsi); - return get_subscriber(sgrp, GSM_SUBSCRIBER_TMSI, tmsi_string); -} - -struct gsm_subscriber *subscr_get_by_imsi(struct gsm_subscriber_group *sgrp, - const char *imsi) -{ - struct gsm_subscriber *subscr; - - llist_for_each_entry(subscr, subscr_bsc_active_subscribers(), entry) { - if (strcmp(subscr->imsi, imsi) == 0) - return subscr_get(subscr); - } - - return get_subscriber(sgrp, GSM_SUBSCRIBER_IMSI, imsi); -} - -struct gsm_subscriber *subscr_get_by_extension(struct gsm_subscriber_group *sgrp, - const char *ext) -{ - struct gsm_subscriber *subscr; - - llist_for_each_entry(subscr, subscr_bsc_active_subscribers(), entry) { - if (strcmp(subscr->extension, ext) == 0) - return subscr_get(subscr); - } - - return get_subscriber(sgrp, GSM_SUBSCRIBER_EXTENSION, ext); -} - -struct gsm_subscriber *subscr_get_by_id(struct gsm_subscriber_group *sgrp, - unsigned long long id) -{ - struct gsm_subscriber *subscr; - char buf[32]; - sprintf(buf, "%llu", id); - - llist_for_each_entry(subscr, subscr_bsc_active_subscribers(), entry) { - if (subscr->id == id) - return subscr_get(subscr); - } - - return get_subscriber(sgrp, GSM_SUBSCRIBER_ID, buf); -} - -int subscr_update_expire_lu(struct gsm_subscriber *s, struct gsm_bts *bts) -{ - int rc; - - if (!s) { - LOGP(DMM, LOGL_ERROR, "LU Expiration but NULL subscriber\n"); - return -1; - } - if (!bts) { - LOGP(DMM, LOGL_ERROR, "%s: LU Expiration but NULL bts\n", - subscr_name(s)); - return -1; - } - - /* Table 10.5.33: The T3212 timeout value field is coded as the - * binary representation of the timeout value for - * periodic updating in decihours. Mark the subscriber as - * inactive if it missed two consecutive location updates. - * Timeout is twice the t3212 value plus one minute */ - - /* Is expiration handling enabled? */ - if (bts->si_common.chan_desc.t3212 == 0) - s->expire_lu = GSM_SUBSCRIBER_NO_EXPIRATION; - else - s->expire_lu = time(NULL) + - (bts->si_common.chan_desc.t3212 * 60 * 6 * 2) + 60; - - rc = db_sync_subscriber(s); - db_subscriber_update(s); - return rc; -} - -int subscr_update(struct gsm_subscriber *s, struct gsm_bts *bts, int reason) -{ - int rc; - - /* FIXME: Migrate pending requests from one BSC to another */ - switch (reason) { - case GSM_SUBSCRIBER_UPDATE_ATTACHED: - s->group = bts->network->subscr_group; - /* Indicate "attached to LAC" */ - s->lac = bts->location_area_code; - - LOGP(DMM, LOGL_INFO, "Subscriber %s ATTACHED LAC=%u\n", - subscr_name(s), s->lac); - - /* - * The below will set a new expire_lu but as a side-effect - * the new lac will be saved in the database. - */ - rc = subscr_update_expire_lu(s, bts); - osmo_signal_dispatch(SS_SUBSCR, S_SUBSCR_ATTACHED, s); - break; - case GSM_SUBSCRIBER_UPDATE_DETACHED: - /* Only detach if we are currently in this area */ - if (bts->location_area_code == s->lac) - s->lac = GSM_LAC_RESERVED_DETACHED; - LOGP(DMM, LOGL_INFO, "Subscriber %s DETACHED\n", subscr_name(s)); - rc = db_sync_subscriber(s); - db_subscriber_update(s); - osmo_signal_dispatch(SS_SUBSCR, S_SUBSCR_DETACHED, s); - break; - default: - fprintf(stderr, "subscr_update with unknown reason: %d\n", - reason); - rc = db_sync_subscriber(s); - db_subscriber_update(s); - break; - }; - - return rc; -} - -void subscr_update_from_db(struct gsm_subscriber *sub) -{ - db_subscriber_update(sub); -} - -static void subscr_expire_callback(void *data, long long unsigned int id) -{ - struct gsm_network *net = data; - struct gsm_subscriber *s = subscr_get_by_id(net->subscr_group, id); - struct gsm_subscriber_connection *conn = connection_for_subscr(s); - - /* - * The subscriber is active and the phone stopped the timer. As - * we don't want to periodically update the database for active - * subscribers we will just do it when the subscriber was selected - * for expiration. This way on the next around another subscriber - * will be selected. - */ - if (conn && conn->expire_timer_stopped) { - LOGP(DMM, LOGL_DEBUG, "Not expiring subscriber %s (ID %llu)\n", - subscr_name(s), id); - subscr_update_expire_lu(s, conn->bts); - subscr_put(s); - return; - } - - - LOGP(DMM, LOGL_NOTICE, "Expiring inactive subscriber %s (ID %llu)\n", - subscr_name(s), id); - s->lac = GSM_LAC_RESERVED_DETACHED; - db_sync_subscriber(s); - - subscr_put(s); -} - -void subscr_expire(struct gsm_subscriber_group *sgrp) -{ - db_subscriber_expire(sgrp->net, subscr_expire_callback); -} - -struct gsm_subscriber_connection *connection_for_subscr(struct gsm_subscriber *subscr) -{ - /* FIXME: replace this with a backpointer in gsm_subscriber? */ - struct gsm_network *net = subscr->group->net; - struct gsm_subscriber_connection *conn; - - llist_for_each_entry(conn, &net->subscr_conns, entry) { - if (conn->subscr == subscr) - return conn; - } - - return NULL; -} diff --git a/openbsc/src/libmsc/meas_feed.c b/openbsc/src/libmsc/meas_feed.c deleted file mode 100644 index 3ddcdc39..00000000 --- a/openbsc/src/libmsc/meas_feed.c +++ /dev/null @@ -1,167 +0,0 @@ -/* UDP-Feed of measurement reports */ - -#include - -#include - -#include -#include -#include -#include -#include - -#include -#include - -#include -#include -#include -#include -#include - -#include "meas_feed.h" - -struct meas_feed_state { - struct osmo_wqueue wqueue; - char scenario[31+1]; - char *dst_host; - uint16_t dst_port; -}; - - -static struct meas_feed_state g_mfs; - -static int process_meas_rep(struct gsm_meas_rep *mr) -{ - struct msgb *msg; - struct meas_feed_meas *mfm; - struct gsm_subscriber *subscr; - - /* ignore measurements as long as we don't know who it is */ - if (!mr->lchan || !mr->lchan->conn || !mr->lchan->conn->subscr) - return 0; - - subscr = mr->lchan->conn->subscr; - - msg = msgb_alloc(sizeof(struct meas_feed_meas), "Meas. Feed"); - if (!msg) - return 0; - - /* fill in the header */ - mfm = (struct meas_feed_meas *) msgb_put(msg, sizeof(*mfm)); - mfm->hdr.msg_type = MEAS_FEED_MEAS; - mfm->hdr.version = MEAS_FEED_VERSION; - - /* fill in MEAS_FEED_MEAS specific header */ - osmo_strlcpy(mfm->imsi, subscr->imsi, sizeof(mfm->imsi)); - osmo_strlcpy(mfm->name, subscr->name, sizeof(mfm->name)); - osmo_strlcpy(mfm->scenario, g_mfs.scenario, sizeof(mfm->scenario)); - - /* copy the entire measurement report */ - memcpy(&mfm->mr, mr, sizeof(mfm->mr)); - - /* copy channel information */ - /* we assume that the measurement report always belong to some timeslot */ - mfm->lchan_type = (uint8_t)mr->lchan->type; - mfm->pchan_type = (uint8_t)mr->lchan->ts->pchan; - mfm->bts_nr = mr->lchan->ts->trx->bts->nr; - mfm->trx_nr = mr->lchan->ts->trx->nr; - mfm->ts_nr = mr->lchan->ts->nr; - mfm->ss_nr = mr->lchan->nr; - - /* and send it to the socket */ - if (osmo_wqueue_enqueue(&g_mfs.wqueue, msg) != 0) - msgb_free(msg); - - return 0; -} - -static int meas_feed_sig_cb(unsigned int subsys, unsigned int signal, - void *handler_data, void *signal_data) -{ - struct lchan_signal_data *sdata = signal_data; - - if (subsys != SS_LCHAN) - return 0; - - if (signal == S_LCHAN_MEAS_REP) - process_meas_rep(sdata->mr); - - return 0; -} - -static int feed_write_cb(struct osmo_fd *ofd, struct msgb *msg) -{ - return write(ofd->fd, msgb_data(msg), msgb_length(msg)); -} - -static int feed_read_cb(struct osmo_fd *ofd) -{ - int rc; - char buf[256]; - - rc = read(ofd->fd, buf, sizeof(buf)); - ofd->fd &= ~BSC_FD_READ; - - return rc; -} - -int meas_feed_cfg_set(const char *dst_host, uint16_t dst_port) -{ - int rc; - int already_initialized = 0; - - if (g_mfs.wqueue.bfd.fd) - already_initialized = 1; - - - if (already_initialized && - !strcmp(dst_host, g_mfs.dst_host) && - dst_port == g_mfs.dst_port) - return 0; - - if (!already_initialized) { - osmo_wqueue_init(&g_mfs.wqueue, 10); - g_mfs.wqueue.write_cb = feed_write_cb; - g_mfs.wqueue.read_cb = feed_read_cb; - osmo_signal_register_handler(SS_LCHAN, meas_feed_sig_cb, NULL); - } - - if (already_initialized) { - osmo_wqueue_clear(&g_mfs.wqueue); - osmo_fd_unregister(&g_mfs.wqueue.bfd); - close(g_mfs.wqueue.bfd.fd); - /* don't set to zero, as that would mean 'not yet initialized' */ - g_mfs.wqueue.bfd.fd = -1; - } - rc = osmo_sock_init_ofd(&g_mfs.wqueue.bfd, AF_UNSPEC, SOCK_DGRAM, - IPPROTO_UDP, dst_host, dst_port, - OSMO_SOCK_F_CONNECT); - if (rc < 0) - return rc; - - g_mfs.wqueue.bfd.when &= ~BSC_FD_READ; - - if (g_mfs.dst_host) - talloc_free(g_mfs.dst_host); - g_mfs.dst_host = talloc_strdup(NULL, dst_host); - g_mfs.dst_port = dst_port; - - return 0; -} - -void meas_feed_cfg_get(char **host, uint16_t *port) -{ - *port = g_mfs.dst_port; - *host = g_mfs.dst_host; -} - -void meas_feed_scenario_set(const char *name) -{ - osmo_strlcpy(g_mfs.scenario, name, sizeof(g_mfs.scenario)); -} - -const char *meas_feed_scenario_get(void) -{ - return g_mfs.scenario; -} diff --git a/openbsc/src/libmsc/meas_feed.h b/openbsc/src/libmsc/meas_feed.h deleted file mode 100644 index 782a9616..00000000 --- a/openbsc/src/libmsc/meas_feed.h +++ /dev/null @@ -1,12 +0,0 @@ -#ifndef _INT_MEAS_FEED_H -#define _INT_MEAS_FEED_H - -#include - -int meas_feed_cfg_set(const char *dst_host, uint16_t dst_port); -void meas_feed_cfg_get(char **host, uint16_t *port); - -void meas_feed_scenario_set(const char *name); -const char *meas_feed_scenario_get(void); - -#endif /* _INT_MEAS_FEED_H */ diff --git a/openbsc/src/libmsc/mncc.c b/openbsc/src/libmsc/mncc.c deleted file mode 100644 index 8110eadc..00000000 --- a/openbsc/src/libmsc/mncc.c +++ /dev/null @@ -1,107 +0,0 @@ -/* mncc.c - utility routines for the MNCC API between the 04.08 - * message parsing and the actual Call Control logic */ - -/* (C) 2008-2009 by Harald Welte - * (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 const struct value_string mncc_names[] = { - { MNCC_SETUP_REQ, "MNCC_SETUP_REQ" }, - { MNCC_SETUP_IND, "MNCC_SETUP_IND" }, - { MNCC_SETUP_RSP, "MNCC_SETUP_RSP" }, - { MNCC_SETUP_CNF, "MNCC_SETUP_CNF" }, - { MNCC_SETUP_COMPL_REQ, "MNCC_SETUP_COMPL_REQ" }, - { MNCC_SETUP_COMPL_IND, "MNCC_SETUP_COMPL_IND" }, - { MNCC_CALL_CONF_IND, "MNCC_CALL_CONF_IND" }, - { MNCC_CALL_PROC_REQ, "MNCC_CALL_PROC_REQ" }, - { MNCC_PROGRESS_REQ, "MNCC_PROGRESS_REQ" }, - { MNCC_ALERT_REQ, "MNCC_ALERT_REQ" }, - { MNCC_ALERT_IND, "MNCC_ALERT_IND" }, - { MNCC_NOTIFY_REQ, "MNCC_NOTIFY_REQ" }, - { MNCC_NOTIFY_IND, "MNCC_NOTIFY_IND" }, - { MNCC_DISC_REQ, "MNCC_DISC_REQ" }, - { MNCC_DISC_IND, "MNCC_DISC_IND" }, - { MNCC_REL_REQ, "MNCC_REL_REQ" }, - { MNCC_REL_IND, "MNCC_REL_IND" }, - { MNCC_REL_CNF, "MNCC_REL_CNF" }, - { MNCC_FACILITY_REQ, "MNCC_FACILITY_REQ" }, - { MNCC_FACILITY_IND, "MNCC_FACILITY_IND" }, - { MNCC_START_DTMF_IND, "MNCC_START_DTMF_IND" }, - { MNCC_START_DTMF_RSP, "MNCC_START_DTMF_RSP" }, - { MNCC_START_DTMF_REJ, "MNCC_START_DTMF_REJ" }, - { MNCC_STOP_DTMF_IND, "MNCC_STOP_DTMF_IND" }, - { MNCC_STOP_DTMF_RSP, "MNCC_STOP_DTMF_RSP" }, - { MNCC_MODIFY_REQ, "MNCC_MODIFY_REQ" }, - { MNCC_MODIFY_IND, "MNCC_MODIFY_IND" }, - { MNCC_MODIFY_RSP, "MNCC_MODIFY_RSP" }, - { MNCC_MODIFY_CNF, "MNCC_MODIFY_CNF" }, - { MNCC_MODIFY_REJ, "MNCC_MODIFY_REJ" }, - { MNCC_HOLD_IND, "MNCC_HOLD_IND" }, - { MNCC_HOLD_CNF, "MNCC_HOLD_CNF" }, - { MNCC_HOLD_REJ, "MNCC_HOLD_REJ" }, - { MNCC_RETRIEVE_IND, "MNCC_RETRIEVE_IND" }, - { MNCC_RETRIEVE_CNF, "MNCC_RETRIEVE_CNF" }, - { MNCC_RETRIEVE_REJ, "MNCC_RETRIEVE_REJ" }, - { MNCC_USERINFO_REQ, "MNCC_USERINFO_REQ" }, - { MNCC_USERINFO_IND, "MNCC_USERINFO_IND" }, - { MNCC_REJ_REQ, "MNCC_REJ_REQ" }, - { MNCC_REJ_IND, "MNCC_REJ_IND" }, - { MNCC_BRIDGE, "MNCC_BRIDGE" }, - { MNCC_FRAME_RECV, "MNCC_FRAME_RECV" }, - { MNCC_FRAME_DROP, "MNCC_FRAME_DROP" }, - { MNCC_LCHAN_MODIFY, "MNCC_LCHAN_MODIFY" }, - { MNCC_RTP_CREATE, "MNCC_RTP_CREATE" }, - { MNCC_RTP_CONNECT, "MNCC_RTP_CONNECT" }, - { MNCC_RTP_FREE, "MNCC_RTP_FREE" }, - { GSM_TCHF_FRAME, "GSM_TCHF_FRAME" }, - { GSM_TCHF_FRAME_EFR, "GSM_TCHF_FRAME_EFR" }, - { GSM_TCHH_FRAME, "GSM_TCHH_FRAME" }, - { GSM_TCH_FRAME_AMR, "GSM_TCH_FRAME_AMR" }, - { GSM_BAD_FRAME, "GSM_BAD_FRAME" }, - { 0, NULL }, -}; - -const char *get_mncc_name(int value) -{ - return get_value_string(mncc_names, value); -} - -void mncc_set_cause(struct gsm_mncc *data, int loc, int val) -{ - data->fields |= MNCC_F_CAUSE; - data->cause.location = loc; - data->cause.value = val; -} - diff --git a/openbsc/src/libmsc/mncc_builtin.c b/openbsc/src/libmsc/mncc_builtin.c deleted file mode 100644 index 067cc92f..00000000 --- a/openbsc/src/libmsc/mncc_builtin.c +++ /dev/null @@ -1,426 +0,0 @@ -/* mncc_builtin.c - default, minimal built-in MNCC Application for - * standalone bsc_hack (network-in-the-box mode) */ - -/* (C) 2008-2010 by Harald Welte - * (C) 2009 by Andreas Eversberg - * All Rights Reserved - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - * - */ - - -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include - -void *tall_call_ctx; - -static LLIST_HEAD(call_list); - -static uint32_t new_callref = 0x00000001; - -struct mncc_int mncc_int = { - .def_codec = { GSM48_CMODE_SPEECH_V1, GSM48_CMODE_SPEECH_V1 }, -}; - -static void free_call(struct gsm_call *call) -{ - llist_del(&call->entry); - DEBUGP(DMNCC, "(call %x) Call removed.\n", call->callref); - talloc_free(call); -} - - -static struct gsm_call *get_call_ref(uint32_t callref) -{ - struct gsm_call *callt; - - llist_for_each_entry(callt, &call_list, entry) { - if (callt->callref == callref) - return callt; - } - return NULL; -} - -uint8_t mncc_codec_for_mode(int lchan_type) -{ - /* FIXME: check codec capabilities of the phone */ - - if (lchan_type != GSM_LCHAN_TCH_H) - return mncc_int.def_codec[0]; - else - return mncc_int.def_codec[1]; -} - -static uint8_t determine_lchan_mode(struct gsm_mncc *setup) -{ - return mncc_codec_for_mode(setup->lchan_type); -} - -/* on incoming call, look up database and send setup to remote subscr. */ -static int mncc_setup_ind(struct gsm_call *call, int msg_type, - struct gsm_mncc *setup) -{ - struct gsm_mncc mncc; - struct gsm_call *remote; - - memset(&mncc, 0, sizeof(struct gsm_mncc)); - mncc.callref = call->callref; - - /* already have remote call */ - if (call->remote_ref) - return 0; - - /* transfer mode 1 would be packet mode, which was never specified */ - if (setup->bearer_cap.mode != 0) { - LOGP(DMNCC, LOGL_NOTICE, "(call %x) We don't support " - "packet mode\n", call->callref); - mncc_set_cause(&mncc, GSM48_CAUSE_LOC_PRN_S_LU, - GSM48_CC_CAUSE_BEARER_CA_UNAVAIL); - goto out_reject; - } - - /* we currently only do speech */ - if (setup->bearer_cap.transfer != GSM_MNCC_BCAP_SPEECH) { - LOGP(DMNCC, LOGL_NOTICE, "(call %x) We only support " - "voice calls\n", call->callref); - mncc_set_cause(&mncc, GSM48_CAUSE_LOC_PRN_S_LU, - GSM48_CC_CAUSE_BEARER_CA_UNAVAIL); - goto out_reject; - } - - /* create remote call */ - if (!(remote = talloc_zero(tall_call_ctx, struct gsm_call))) { - mncc_set_cause(&mncc, GSM48_CAUSE_LOC_PRN_S_LU, - GSM48_CC_CAUSE_RESOURCE_UNAVAIL); - goto out_reject; - } - llist_add_tail(&remote->entry, &call_list); - remote->net = call->net; - remote->callref = new_callref++; - DEBUGP(DMNCC, "(call %x) Creating new remote instance %x.\n", - call->callref, remote->callref); - - /* link remote call */ - call->remote_ref = remote->callref; - remote->remote_ref = call->callref; - - /* send call proceeding */ - memset(&mncc, 0, sizeof(struct gsm_mncc)); - mncc.callref = call->callref; - DEBUGP(DMNCC, "(call %x) Accepting call.\n", call->callref); - mncc_tx_to_cc(call->net, MNCC_CALL_PROC_REQ, &mncc); - - /* modify mode */ - memset(&mncc, 0, sizeof(struct gsm_mncc)); - mncc.callref = call->callref; - mncc.lchan_mode = determine_lchan_mode(setup); - DEBUGP(DMNCC, "(call %x) Modify channel mode: %s\n", call->callref, - get_value_string(gsm48_chan_mode_names, mncc.lchan_mode)); - mncc_tx_to_cc(call->net, MNCC_LCHAN_MODIFY, &mncc); - - /* send setup to remote */ -// setup->fields |= MNCC_F_SIGNAL; -// setup->signal = GSM48_SIGNAL_DIALTONE; - setup->callref = remote->callref; - DEBUGP(DMNCC, "(call %x) Forwarding SETUP to remote.\n", call->callref); - return mncc_tx_to_cc(remote->net, MNCC_SETUP_REQ, setup); - -out_reject: - mncc_tx_to_cc(call->net, MNCC_REJ_REQ, &mncc); - free_call(call); - return 0; -} - -static int mncc_alert_ind(struct gsm_call *call, int msg_type, - struct gsm_mncc *alert) -{ - struct gsm_call *remote; - - /* send alerting to remote */ - if (!(remote = get_call_ref(call->remote_ref))) - return 0; - alert->callref = remote->callref; - DEBUGP(DMNCC, "(call %x) Forwarding ALERT to remote.\n", call->callref); - return mncc_tx_to_cc(remote->net, MNCC_ALERT_REQ, alert); -} - -static int mncc_notify_ind(struct gsm_call *call, int msg_type, - struct gsm_mncc *notify) -{ - struct gsm_call *remote; - - /* send notify to remote */ - if (!(remote = get_call_ref(call->remote_ref))) - return 0; - notify->callref = remote->callref; - DEBUGP(DMNCC, "(call %x) Forwarding NOTIF to remote.\n", call->callref); - return mncc_tx_to_cc(remote->net, MNCC_NOTIFY_REQ, notify); -} - -static int mncc_setup_cnf(struct gsm_call *call, int msg_type, - struct gsm_mncc *connect) -{ - struct gsm_mncc connect_ack, frame_recv; - struct gsm_network *net = call->net; - struct gsm_call *remote; - struct gsm_mncc_bridge bridge = { .msg_type = MNCC_BRIDGE }; - - /* acknowledge connect */ - memset(&connect_ack, 0, sizeof(struct gsm_mncc)); - connect_ack.callref = call->callref; - DEBUGP(DMNCC, "(call %x) Acknowledge SETUP.\n", call->callref); - mncc_tx_to_cc(call->net, MNCC_SETUP_COMPL_REQ, &connect_ack); - - /* send connect message to remote */ - if (!(remote = get_call_ref(call->remote_ref))) - return 0; - connect->callref = remote->callref; - DEBUGP(DMNCC, "(call %x) Sending CONNECT to remote.\n", call->callref); - mncc_tx_to_cc(remote->net, MNCC_SETUP_RSP, connect); - - /* bridge tch */ - bridge.callref[0] = call->callref; - bridge.callref[1] = call->remote_ref; - DEBUGP(DMNCC, "(call %x) Bridging with remote.\n", call->callref); - - /* in direct mode, we always have to bridge the channels */ - if (ipacc_rtp_direct) - return mncc_tx_to_cc(call->net, MNCC_BRIDGE, &bridge); - - /* proxy mode */ - if (!net->handover.active) { - /* in the no-handover case, we can bridge, i.e. use - * the old RTP proxy code */ - return mncc_tx_to_cc(call->net, MNCC_BRIDGE, &bridge); - } else { - /* in case of handover, we need to re-write the RTP - * SSRC, sequence and timestamp values and thus - * need to enable RTP receive for both directions */ - memset(&frame_recv, 0, sizeof(struct gsm_mncc)); - frame_recv.callref = call->callref; - mncc_tx_to_cc(call->net, MNCC_FRAME_RECV, &frame_recv); - frame_recv.callref = call->remote_ref; - return mncc_tx_to_cc(call->net, MNCC_FRAME_RECV, &frame_recv); - } -} - -static int mncc_disc_ind(struct gsm_call *call, int msg_type, - struct gsm_mncc *disc) -{ - struct gsm_call *remote; - - /* send release */ - DEBUGP(DMNCC, "(call %x) Releasing call with cause %d\n", - call->callref, disc->cause.value); - mncc_tx_to_cc(call->net, MNCC_REL_REQ, disc); - - /* send disc to remote */ - if (!(remote = get_call_ref(call->remote_ref))) { - return 0; - } - disc->callref = remote->callref; - DEBUGP(DMNCC, "(call %x) Disconnecting remote with cause %d\n", - remote->callref, disc->cause.value); - return mncc_tx_to_cc(remote->net, MNCC_DISC_REQ, disc); -} - -static int mncc_rel_ind(struct gsm_call *call, int msg_type, struct gsm_mncc *rel) -{ - struct gsm_call *remote; - - /* send release to remote */ - if (!(remote = get_call_ref(call->remote_ref))) { - free_call(call); - return 0; - } - - rel->callref = remote->callref; - DEBUGP(DMNCC, "(call %x) Releasing remote with cause %d\n", - call->callref, rel->cause.value); - - /* - * Release this side of the call right now. Otherwise we end up - * in this method for the other call and will also try to release - * it and then we will end up with a double free and a crash - */ - free_call(call); - mncc_tx_to_cc(remote->net, MNCC_REL_REQ, rel); - - return 0; -} - -static int mncc_rel_cnf(struct gsm_call *call, int msg_type, struct gsm_mncc *rel) -{ - free_call(call); - return 0; -} - -/* receiving a (speech) traffic frame from the BSC code */ -static int mncc_rcv_data(struct gsm_call *call, int msg_type, - struct gsm_data_frame *dfr) -{ - struct gsm_trans *remote_trans; - - remote_trans = trans_find_by_callref(call->net, call->remote_ref); - - /* this shouldn't really happen */ - if (!remote_trans || !remote_trans->conn) { - LOGP(DMNCC, LOGL_ERROR, "No transaction or transaction without lchan?!?\n"); - return -EIO; - } - - /* RTP socket of remote end has meanwhile died */ - if (!remote_trans->conn->lchan->abis_ip.rtp_socket) - return -EIO; - - return rtp_send_frame(remote_trans->conn->lchan->abis_ip.rtp_socket, dfr); -} - - -/* Internal MNCC handler input function (from CC -> MNCC -> here) */ -int int_mncc_recv(struct gsm_network *net, struct msgb *msg) -{ - void *arg = msgb_data(msg); - struct gsm_mncc *data = arg; - int msg_type = data->msg_type; - int callref; - struct gsm_call *call = NULL, *callt; - int rc = 0; - - /* Special messages */ - switch(msg_type) { - } - - /* find callref */ - callref = data->callref; - llist_for_each_entry(callt, &call_list, entry) { - if (callt->callref == callref) { - call = callt; - break; - } - } - - /* create callref, if setup is received */ - if (!call) { - if (msg_type != MNCC_SETUP_IND) - goto out_free; /* drop */ - /* create call */ - if (!(call = talloc_zero(tall_call_ctx, struct gsm_call))) { - struct gsm_mncc rel; - - memset(&rel, 0, sizeof(struct gsm_mncc)); - rel.callref = callref; - mncc_set_cause(&rel, GSM48_CAUSE_LOC_PRN_S_LU, - GSM48_CC_CAUSE_RESOURCE_UNAVAIL); - mncc_tx_to_cc(net, MNCC_REL_REQ, &rel); - goto out_free; - } - llist_add_tail(&call->entry, &call_list); - call->net = net; - call->callref = callref; - DEBUGP(DMNCC, "(call %x) Call created.\n", call->callref); - } - - if (mncc_is_data_frame(msg_type)) { - rc = mncc_rcv_data(call, msg_type, arg); - goto out_free; - } - - DEBUGP(DMNCC, "(call %x) Received message %s\n", call->callref, - get_mncc_name(msg_type)); - - switch(msg_type) { - case MNCC_SETUP_IND: - rc = mncc_setup_ind(call, msg_type, arg); - break; - case MNCC_SETUP_CNF: - rc = mncc_setup_cnf(call, msg_type, arg); - break; - case MNCC_SETUP_COMPL_IND: - break; - case MNCC_CALL_CONF_IND: - /* we now need to MODIFY the channel */ - data->lchan_mode = determine_lchan_mode(data); - mncc_tx_to_cc(call->net, MNCC_LCHAN_MODIFY, data); - break; - case MNCC_ALERT_IND: - rc = mncc_alert_ind(call, msg_type, arg); - break; - case MNCC_NOTIFY_IND: - rc = mncc_notify_ind(call, msg_type, arg); - break; - case MNCC_DISC_IND: - rc = mncc_disc_ind(call, msg_type, arg); - break; - case MNCC_REL_IND: - case MNCC_REJ_IND: - rc = mncc_rel_ind(call, msg_type, arg); - break; - case MNCC_REL_CNF: - rc = mncc_rel_cnf(call, msg_type, arg); - break; - case MNCC_FACILITY_IND: - break; - case MNCC_START_DTMF_IND: - rc = mncc_tx_to_cc(net, MNCC_START_DTMF_REJ, data); - break; - case MNCC_STOP_DTMF_IND: - rc = mncc_tx_to_cc(net, MNCC_STOP_DTMF_RSP, data); - break; - case MNCC_MODIFY_IND: - mncc_set_cause(data, GSM48_CAUSE_LOC_PRN_S_LU, - GSM48_CC_CAUSE_SERV_OPT_UNIMPL); - DEBUGP(DMNCC, "(call %x) Rejecting MODIFY with cause %d\n", - call->callref, data->cause.value); - rc = mncc_tx_to_cc(net, MNCC_MODIFY_REJ, data); - break; - case MNCC_MODIFY_CNF: - break; - case MNCC_HOLD_IND: - mncc_set_cause(data, GSM48_CAUSE_LOC_PRN_S_LU, - GSM48_CC_CAUSE_SERV_OPT_UNIMPL); - DEBUGP(DMNCC, "(call %x) Rejecting HOLD with cause %d\n", - call->callref, data->cause.value); - rc = mncc_tx_to_cc(net, MNCC_HOLD_REJ, data); - break; - case MNCC_RETRIEVE_IND: - mncc_set_cause(data, GSM48_CAUSE_LOC_PRN_S_LU, - GSM48_CC_CAUSE_SERV_OPT_UNIMPL); - DEBUGP(DMNCC, "(call %x) Rejecting RETRIEVE with cause %d\n", - call->callref, data->cause.value); - rc = mncc_tx_to_cc(net, MNCC_RETRIEVE_REJ, data); - break; - default: - LOGP(DMNCC, LOGL_NOTICE, "(call %x) Message unhandled\n", callref); - break; - } - -out_free: - msgb_free(msg); - - return rc; -} diff --git a/openbsc/src/libmsc/mncc_sock.c b/openbsc/src/libmsc/mncc_sock.c deleted file mode 100644 index 0efe3a15..00000000 --- a/openbsc/src/libmsc/mncc_sock.c +++ /dev/null @@ -1,318 +0,0 @@ -/* mncc_sock.c: Tie the MNCC interface to a unix domain socket */ - -/* (C) 2008-2010 by Harald Welte - * (C) 2009 by Andreas Eversberg - * (C) 2012 by Holger Hans Peter Freyther - * All Rights Reserved - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - * - */ - -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include - -#include -#include -#include - -struct mncc_sock_state { - struct gsm_network *net; - struct osmo_fd listen_bfd; /* fd for listen socket */ - struct osmo_fd conn_bfd; /* fd for connection to lcr */ -}; - -/* input from CC code into mncc_sock */ -int mncc_sock_from_cc(struct gsm_network *net, struct msgb *msg) -{ - struct gsm_mncc *mncc_in = (struct gsm_mncc *) msgb_data(msg); - int msg_type = mncc_in->msg_type; - - /* Check if we currently have a MNCC handler connected */ - if (net->mncc_state->conn_bfd.fd < 0) { - LOGP(DMNCC, LOGL_ERROR, "mncc_sock receives %s for external CC app " - "but socket is gone\n", get_mncc_name(msg_type)); - if (!mncc_is_data_frame(msg_type)) { - /* release the request */ - struct gsm_mncc mncc_out; - memset(&mncc_out, 0, sizeof(mncc_out)); - mncc_out.callref = mncc_in->callref; - mncc_set_cause(&mncc_out, GSM48_CAUSE_LOC_PRN_S_LU, - GSM48_CC_CAUSE_TEMP_FAILURE); - mncc_tx_to_cc(net, MNCC_REL_REQ, &mncc_out); - } - /* free the original message */ - msgb_free(msg); - return -1; - } - - /* FIXME: check for some maximum queue depth? */ - - /* Actually enqueue the message and mark socket write need */ - msgb_enqueue(&net->upqueue, msg); - net->mncc_state->conn_bfd.when |= BSC_FD_WRITE; - return 0; -} - -static void mncc_sock_close(struct mncc_sock_state *state) -{ - struct osmo_fd *bfd = &state->conn_bfd; - - LOGP(DMNCC, LOGL_NOTICE, "MNCC Socket has LOST connection\n"); - - close(bfd->fd); - bfd->fd = -1; - osmo_fd_unregister(bfd); - - /* re-enable the generation of ACCEPT for new connections */ - state->listen_bfd.when |= BSC_FD_READ; - - /* release all exisitng calls */ - gsm0408_clear_all_trans(state->net, GSM48_PDISC_CC); - - /* flush the queue */ - while (!llist_empty(&state->net->upqueue)) { - struct msgb *msg = msgb_dequeue(&state->net->upqueue); - msgb_free(msg); - } -} - -static int mncc_sock_read(struct osmo_fd *bfd) -{ - struct mncc_sock_state *state = (struct mncc_sock_state *)bfd->data; - struct gsm_mncc *mncc_prim; - struct msgb *msg; - int rc; - - msg = msgb_alloc(sizeof(*mncc_prim)+256, "mncc_sock_rx"); - if (!msg) - return -ENOMEM; - - mncc_prim = (struct gsm_mncc *) msg->tail; - - rc = recv(bfd->fd, msg->tail, msgb_tailroom(msg), 0); - if (rc == 0) - goto close; - - if (rc < 0) { - if (errno == EAGAIN) - return 0; - goto close; - } - - rc = mncc_tx_to_cc(state->net, mncc_prim->msg_type, mncc_prim); - - /* as we always synchronously process the message in mncc_send() and - * its callbacks, we can free the message here. */ - msgb_free(msg); - - return rc; - -close: - msgb_free(msg); - mncc_sock_close(state); - return -1; -} - -static int mncc_sock_write(struct osmo_fd *bfd) -{ - struct mncc_sock_state *state = bfd->data; - struct gsm_network *net = state->net; - int rc; - - while (!llist_empty(&net->upqueue)) { - struct msgb *msg, *msg2; - struct gsm_mncc *mncc_prim; - - /* peek at the beginning of the queue */ - msg = llist_entry(net->upqueue.next, struct msgb, list); - mncc_prim = (struct gsm_mncc *)msg->data; - - bfd->when &= ~BSC_FD_WRITE; - - /* bug hunter 8-): maybe someone forgot msgb_put(...) ? */ - if (!msgb_length(msg)) { - LOGP(DMNCC, LOGL_ERROR, "message type (%d) with ZERO " - "bytes!\n", mncc_prim->msg_type); - goto dontsend; - } - - /* try to send it over the socket */ - rc = write(bfd->fd, msgb_data(msg), msgb_length(msg)); - if (rc == 0) - goto close; - if (rc < 0) { - if (errno == EAGAIN) { - bfd->when |= BSC_FD_WRITE; - break; - } - goto close; - } - -dontsend: - /* _after_ we send it, we can deueue */ - msg2 = msgb_dequeue(&net->upqueue); - assert(msg == msg2); - msgb_free(msg); - } - return 0; - -close: - mncc_sock_close(state); - - return -1; -} - -static int mncc_sock_cb(struct osmo_fd *bfd, unsigned int flags) -{ - int rc = 0; - - if (flags & BSC_FD_READ) - rc = mncc_sock_read(bfd); - if (rc < 0) - return rc; - - if (flags & BSC_FD_WRITE) - rc = mncc_sock_write(bfd); - - return rc; -} - -/** - * Send a version indication to the remote. - */ -static void queue_hello(struct mncc_sock_state *mncc) -{ - struct gsm_mncc_hello *hello; - struct msgb *msg; - - msg = msgb_alloc(512, "mncc hello"); - if (!msg) { - LOGP(DMNCC, LOGL_ERROR, "Failed to allocate hello.\n"); - mncc_sock_close(mncc); - return; - } - - hello = (struct gsm_mncc_hello *) msgb_put(msg, sizeof(*hello)); - hello->msg_type = MNCC_SOCKET_HELLO; - hello->version = MNCC_SOCK_VERSION; - hello->mncc_size = sizeof(struct gsm_mncc); - hello->data_frame_size = sizeof(struct gsm_data_frame); - hello->called_offset = offsetof(struct gsm_mncc, called); - hello->signal_offset = offsetof(struct gsm_mncc, signal); - hello->emergency_offset = offsetof(struct gsm_mncc, emergency); - hello->lchan_type_offset = offsetof(struct gsm_mncc, lchan_type); - - msgb_enqueue(&mncc->net->upqueue, msg); - mncc->conn_bfd.when |= BSC_FD_WRITE; -} - -/* accept a new connection */ -static int mncc_sock_accept(struct osmo_fd *bfd, unsigned int flags) -{ - struct mncc_sock_state *state = (struct mncc_sock_state *)bfd->data; - struct osmo_fd *conn_bfd = &state->conn_bfd; - struct sockaddr_un un_addr; - socklen_t len; - int rc; - - len = sizeof(un_addr); - rc = accept(bfd->fd, (struct sockaddr *) &un_addr, &len); - if (rc < 0) { - LOGP(DMNCC, LOGL_ERROR, "Failed to accept a new connection\n"); - return -1; - } - - if (conn_bfd->fd >= 0) { - LOGP(DMNCC, LOGL_NOTICE, "MNCC app connects but we already have " - "another active connection ?!?\n"); - /* We already have one MNCC app connected, this is all we support */ - state->listen_bfd.when &= ~BSC_FD_READ; - close(rc); - return 0; - } - - conn_bfd->fd = rc; - conn_bfd->when = BSC_FD_READ; - conn_bfd->cb = mncc_sock_cb; - conn_bfd->data = state; - - if (osmo_fd_register(conn_bfd) != 0) { - LOGP(DMNCC, LOGL_ERROR, "Failed to register new connection fd\n"); - close(conn_bfd->fd); - conn_bfd->fd = -1; - return -1; - } - - LOGP(DMNCC, LOGL_NOTICE, "MNCC Socket has connection with external " - "call control application\n"); - - queue_hello(state); - return 0; -} - - -int mncc_sock_init(struct gsm_network *net, const char *sock_path) -{ - struct mncc_sock_state *state; - struct osmo_fd *bfd; - int rc; - - state = talloc_zero(tall_bsc_ctx, struct mncc_sock_state); - if (!state) - return -ENOMEM; - - state->net = net; - state->conn_bfd.fd = -1; - - bfd = &state->listen_bfd; - - bfd->fd = osmo_sock_unix_init(SOCK_SEQPACKET, 0, sock_path, - OSMO_SOCK_F_BIND); - if (bfd->fd < 0) { - LOGP(DMNCC, LOGL_ERROR, "Could not create unix socket: %s: %s\n", - sock_path, strerror(errno)); - talloc_free(state); - return -1; - } - - bfd->when = BSC_FD_READ; - bfd->cb = mncc_sock_accept; - bfd->data = state; - - rc = osmo_fd_register(bfd); - if (rc < 0) { - LOGP(DMNCC, LOGL_ERROR, "Could not register listen fd: %d\n", rc); - close(bfd->fd); - talloc_free(state); - return rc; - } - - net->mncc_state = state; - - LOGP(DMNCC, LOGL_NOTICE, "MNCC socket at %s\n", sock_path); - return 0; -} diff --git a/openbsc/src/libmsc/osmo_msc.c b/openbsc/src/libmsc/osmo_msc.c deleted file mode 100644 index 2389980d..00000000 --- a/openbsc/src/libmsc/osmo_msc.c +++ /dev/null @@ -1,177 +0,0 @@ -/* main MSC management code... */ - -/* - * (C) 2010,2013 by Holger Hans Peter Freyther - * (C) 2010 by On-Waves - * - * All Rights Reserved - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation; either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - * - */ - -#include -#include -#include -#include - -#include - -static void msc_sapi_n_reject(struct gsm_subscriber_connection *conn, int dlci) -{ - int sapi = dlci & 0x7; - - if (sapi == UM_SAPI_SMS) - gsm411_sapi_n_reject(conn); -} - -static int msc_clear_request(struct gsm_subscriber_connection *conn, uint32_t cause) -{ - gsm0408_clear_request(conn, cause); - return 1; -} - -static int msc_compl_l3(struct gsm_subscriber_connection *conn, struct msgb *msg, - uint16_t chosen_channel) -{ - gsm0408_new_conn(conn); - gsm0408_dispatch(conn, msg); - - /* - * If this is a silent call we want the channel to remain open as long as - * possible and this is why we accept this connection regardless of any - * pending transaction or ongoing operation. - */ - if (conn->silent_call) - return BSC_API_CONN_POL_ACCEPT; - if (conn->loc_operation || conn->sec_operation || conn->anch_operation) - return BSC_API_CONN_POL_ACCEPT; - if (trans_has_conn(conn)) - return BSC_API_CONN_POL_ACCEPT; - - LOGP(DRR, LOGL_INFO, "MSC Complete L3: Rejecting connection.\n"); - return BSC_API_CONN_POL_REJECT; -} - -static void msc_dtap(struct gsm_subscriber_connection *conn, uint8_t link_id, struct msgb *msg) -{ - gsm0408_dispatch(conn, msg); -} - -static void msc_assign_compl(struct gsm_subscriber_connection *conn, - uint8_t rr_cause, uint8_t chosen_channel, - uint8_t encr_alg_id, uint8_t speec) -{ - LOGP(DRR, LOGL_DEBUG, "MSC assign complete (do nothing).\n"); -} - -static void msc_assign_fail(struct gsm_subscriber_connection *conn, - uint8_t cause, uint8_t *rr_cause) -{ - LOGP(DRR, LOGL_DEBUG, "MSC assign failure (do nothing).\n"); -} - -static void msc_classmark_chg(struct gsm_subscriber_connection *conn, - const uint8_t *cm2, uint8_t cm2_len, - const uint8_t *cm3, uint8_t cm3_len) -{ - struct gsm_subscriber *subscr = conn->subscr; - - if (subscr) { - subscr->equipment.classmark2_len = cm2_len; - memcpy(subscr->equipment.classmark2, cm2, cm2_len); - if (cm3) { - subscr->equipment.classmark3_len = cm3_len; - memcpy(subscr->equipment.classmark3, cm3, cm3_len); - } - db_sync_equipment(&subscr->equipment); - } -} - -static void msc_ciph_m_compl(struct gsm_subscriber_connection *conn, - struct msgb *msg, uint8_t alg_id) -{ - gsm_cbfn *cb; - - DEBUGP(DRR, "CIPHERING MODE COMPLETE\n"); - - /* Safety check */ - if (!conn->sec_operation) { - DEBUGP(DRR, "No authentication/cipher operation in progress !!!\n"); - return; - } - - /* FIXME: check for MI (if any) */ - - /* Call back whatever was in progress (if anything) ... */ - cb = conn->sec_operation->cb; - if (cb) { - cb(GSM_HOOK_RR_SECURITY, GSM_SECURITY_SUCCEEDED, - NULL, conn, conn->sec_operation->cb_data); - - } - - /* Complete the operation */ - release_security_operation(conn); -} - - - -static struct bsc_api msc_handler = { - .sapi_n_reject = msc_sapi_n_reject, - .compl_l3 = msc_compl_l3, - .dtap = msc_dtap, - .clear_request = msc_clear_request, - .assign_compl = msc_assign_compl, - .assign_fail = msc_assign_fail, - .classmark_chg = msc_classmark_chg, - .cipher_mode_compl = msc_ciph_m_compl, -}; - -struct bsc_api *msc_bsc_api() { - return &msc_handler; -} - -/* lchan release handling */ -void msc_release_connection(struct gsm_subscriber_connection *conn) -{ - /* skip when we are in release, e.g. due an error */ - if (conn->in_release) - return; - - /* skip releasing of silent calls as they have no transaction */ - if (conn->silent_call) - return; - - /* check if there is a pending operation */ - if (conn->loc_operation || conn->sec_operation || conn->anch_operation) - return; - - if (trans_has_conn(conn)) - return; - - /* no more connections, asking to release the channel */ - - /* - * We had stopped the LU expire timer T3212. Now we are about - * to send the MS back to the idle state and this should lead - * to restarting the timer. Set the new expiration time. - */ - if (conn->expire_timer_stopped) - subscr_update_expire_lu(conn->subscr, conn->bts); - - conn->in_release = 1; - gsm0808_clear(conn); - msc_subscr_con_free(conn); -} diff --git a/openbsc/src/libmsc/rrlp.c b/openbsc/src/libmsc/rrlp.c deleted file mode 100644 index e695daac..00000000 --- a/openbsc/src/libmsc/rrlp.c +++ /dev/null @@ -1,104 +0,0 @@ -/* Radio Resource LCS (Location) Protocol, GMS TS 04.31 */ - -/* (C) 2009 by Harald Welte - * - * All Rights Reserved - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation; either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - * - */ - - - -#include -#include -#include -#include - -/* RRLP msPositionReq, nsBased, - * Accuracy=60, Method=gps, ResponseTime=2, oneSet */ -static const uint8_t ms_based_pos_req[] = { 0x40, 0x01, 0x78, 0xa8 }; - -/* RRLP msPositionReq, msBasedPref, - Accuracy=60, Method=gpsOrEOTD, ResponseTime=5, multipleSets */ -static const uint8_t ms_pref_pos_req[] = { 0x40, 0x02, 0x79, 0x50 }; - -/* RRLP msPositionReq, msAssistedPref, - Accuracy=60, Method=gpsOrEOTD, ResponseTime=5, multipleSets */ -static const uint8_t ass_pref_pos_req[] = { 0x40, 0x03, 0x79, 0x50 }; - -static int send_rrlp_req(struct gsm_subscriber_connection *conn) -{ - struct gsm_network *net = conn->network; - const uint8_t *req; - - switch (net->rrlp.mode) { - case RRLP_MODE_MS_BASED: - req = ms_based_pos_req; - break; - case RRLP_MODE_MS_PREF: - req = ms_pref_pos_req; - break; - case RRLP_MODE_ASS_PREF: - req = ass_pref_pos_req; - break; - case RRLP_MODE_NONE: - default: - return 0; - } - - return gsm48_send_rr_app_info(conn, 0x00, - sizeof(ms_based_pos_req), req); -} - -static int subscr_sig_cb(unsigned int subsys, unsigned int signal, - void *handler_data, void *signal_data) -{ - struct gsm_subscriber *subscr; - struct gsm_subscriber_connection *conn; - - switch (signal) { - case S_SUBSCR_ATTACHED: - /* A subscriber has attached. */ - subscr = signal_data; - conn = connection_for_subscr(subscr); - if (!conn) - break; - send_rrlp_req(conn); - break; - } - return 0; -} - -static int paging_sig_cb(unsigned int subsys, unsigned int signal, - void *handler_data, void *signal_data) -{ - struct paging_signal_data *psig_data = signal_data; - - switch (signal) { - case S_PAGING_SUCCEEDED: - /* A subscriber has attached. */ - send_rrlp_req(psig_data->conn); - break; - case S_PAGING_EXPIRED: - break; - } - return 0; -} - -void on_dso_load_rrlp(void) -{ - osmo_signal_register_handler(SS_SUBSCR, subscr_sig_cb, NULL); - osmo_signal_register_handler(SS_PAGING, paging_sig_cb, NULL); -} diff --git a/openbsc/src/libmsc/silent_call.c b/openbsc/src/libmsc/silent_call.c deleted file mode 100644 index 590d01bb..00000000 --- a/openbsc/src/libmsc/silent_call.c +++ /dev/null @@ -1,152 +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(DLSMS, "paging_cb_silent: "); - - sigdata.conn = conn; - sigdata.data = _data; - - switch (event) { - case GSM_PAGING_SUCCEEDED: - DEBUGPC(DLSMS, "success, using Timeslot %u on ARFCN %u\n", - conn->lchan->ts->nr, conn->lchan->ts->trx->arfcn); - conn->silent_call = 1; - /* increment lchan reference count */ - osmo_signal_dispatch(SS_SCALL, S_SCALL_SUCCESS, &sigdata); - break; - case GSM_PAGING_EXPIRED: - case GSM_PAGING_BUSY: - case GSM_PAGING_OOM: - DEBUGP(DLSMS, "expired\n"); - osmo_signal_dispatch(SS_SCALL, S_SCALL_EXPIRED, &sigdata); - break; - default: - rc = -EINVAL; - break; - } - - return rc; -} - -#if 0 -/* receive a layer 3 message from a silent call */ -int silent_call_rx(struct gsm_subscriber_connection *conn, struct msgb *msg) -{ - /* FIXME: do something like sending it through a UDP port */ - LOGP(DLSMS, LOGL_NOTICE, "Discarding L3 message from a silent call.\n"); - return 0; -} -#endif - -struct msg_match { - uint8_t pdisc; - uint8_t msg_type; -}; - -/* list of messages that are handled inside OpenBSC, even in a silent call */ -static const struct msg_match silent_call_accept[] = { - { GSM48_PDISC_MM, GSM48_MT_MM_LOC_UPD_REQUEST }, - { GSM48_PDISC_MM, GSM48_MT_MM_CM_SERV_REQ }, -}; - -#if 0 -/* decide if we need to reroute a message as part of a silent call */ -int silent_call_reroute(struct gsm_subscriber_connection *conn, struct msgb *msg) -{ - struct gsm48_hdr *gh = msgb_l3(msg); - uint8_t pdisc = gsm48_hdr_pdisc(gh); - uint8_t msg_type = gsm48_hdr_msg_type(gh); - int i; - - /* if we're not part of a silent call, never reroute */ - if (!conn->silent_call) - return 0; - - /* check if we are a special message that is handled in openbsc */ - for (i = 0; i < ARRAY_SIZE(silent_call_accept); i++) { - if (silent_call_accept[i].pdisc == pdisc && - silent_call_accept[i].msg_type == msg_type) - return 0; - } - - /* otherwise, reroute */ - LOGP(DLSMS, LOGL_INFO, "Rerouting L3 message from a silent call.\n"); - return 1; -} -#endif - - -/* initiate a silent call with a given subscriber */ -int gsm_silent_call_start(struct gsm_subscriber *subscr, void *data, int type) -{ - struct subscr_request *req; - - req = subscr_request_channel(subscr, type, paging_cb_silent, data); - return req != NULL; -} - -/* end a silent call with a given subscriber */ -int gsm_silent_call_stop(struct gsm_subscriber *subscr) -{ - struct gsm_subscriber_connection *conn; - - conn = connection_for_subscr(subscr); - if (!conn) - return -EINVAL; - - /* did we actually establish a silent call for this guy? */ - if (!conn->silent_call) - return -EINVAL; - - DEBUGPC(DLSMS, "Stopping silent call using Timeslot %u on ARFCN %u\n", - conn->lchan->ts->nr, conn->lchan->ts->trx->arfcn); - - conn->silent_call = 0; - msc_release_connection(conn); - - return 0; -} diff --git a/openbsc/src/libmsc/smpp_openbsc.c b/openbsc/src/libmsc/smpp_openbsc.c deleted file mode 100644 index f94968a3..00000000 --- a/openbsc/src/libmsc/smpp_openbsc.c +++ /dev/null @@ -1,758 +0,0 @@ -/* OpenBSC SMPP 3.4 interface, SMSC-side implementation */ - -/* (C) 2012-2013 by Harald Welte - * - * All Rights Reserved - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation; either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public 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 "smpp_smsc.h" - -/*! \brief find gsm_subscriber for a given SMPP NPI/TON/Address */ -static struct gsm_subscriber *subscr_by_dst(struct gsm_network *net, - uint8_t npi, uint8_t ton, const char *addr) -{ - struct gsm_subscriber *subscr = NULL; - - switch (npi) { - case NPI_Land_Mobile_E212: - subscr = subscr_get_by_imsi(net->subscr_group, addr); - break; - case NPI_ISDN_E163_E164: - case NPI_Private: - subscr = subscr_get_by_extension(net->subscr_group, addr); - break; - default: - LOGP(DSMPP, LOGL_NOTICE, "Unsupported NPI: %u\n", npi); - break; - } - - /* tag the context in case we know it */ - log_set_context(LOG_CTX_VLR_SUBSCR, subscr); - return subscr; -} - -/*! \brief find a TLV with given tag in list of libsmpp34 TLVs */ -static struct tlv_t *find_tlv(struct tlv_t *head, uint16_t tag) -{ - struct tlv_t *t; - - for (t = head; t != NULL; t = t->next) { - if (t->tag == tag) - return t; - } - return NULL; -} - -/*! \brief convert from submit_sm_t to gsm_sms */ -static int submit_to_sms(struct gsm_sms **psms, struct gsm_network *net, - const struct submit_sm_t *submit) -{ - struct gsm_subscriber *dest; - struct gsm_sms *sms; - struct tlv_t *t; - const uint8_t *sms_msg; - unsigned int sms_msg_len; - int mode; - - dest = subscr_by_dst(net, submit->dest_addr_npi, - submit->dest_addr_ton, - (const char *)submit->destination_addr); - if (!dest) { - LOGP(DLSMS, LOGL_NOTICE, "SMPP SUBMIT-SM for unknown subscriber: " - "%s (NPI=%u)\n", submit->destination_addr, - submit->dest_addr_npi); - return ESME_RINVDSTADR; - } - - t = find_tlv(submit->tlv, TLVID_message_payload); - if (t) { - if (submit->sm_length) { - /* ERROR: we cannot have both! */ - LOGP(DLSMS, LOGL_ERROR, "SMPP Cannot have payload in " - "TLV _and_ in the header\n"); - subscr_put(dest); - return ESME_ROPTPARNOTALLWD; - } - sms_msg = t->value.octet; - sms_msg_len = t->length; - } else if (submit->sm_length > 0 && submit->sm_length < 255) { - sms_msg = submit->short_message; - sms_msg_len = submit->sm_length; - } else { - LOGP(DLSMS, LOGL_ERROR, - "SMPP neither message payload nor valid sm_length.\n"); - subscr_put(dest); - return ESME_RINVPARLEN; - } - - sms = sms_alloc(); - sms->source = SMS_SOURCE_SMPP; - sms->smpp.sequence_nr = submit->sequence_number; - - /* fill in the destination address */ - sms->receiver = dest; - sms->dst.ton = submit->dest_addr_ton; - sms->dst.npi = submit->dest_addr_npi; - osmo_strlcpy(sms->dst.addr, dest->extension, sizeof(sms->dst.addr)); - - /* fill in the source address */ - sms->src.ton = submit->source_addr_ton; - sms->src.npi = submit->source_addr_npi; - osmo_strlcpy(sms->src.addr, (char *)submit->source_addr, - sizeof(sms->src.addr)); - - if (submit->esm_class & 0x40) - sms->ud_hdr_ind = 1; - - if (submit->esm_class & 0x80) { - sms->reply_path_req = 1; -#warning Implement reply path - } - - if (submit->data_coding == 0x00 || /* SMSC default */ - submit->data_coding == 0x01) { /* GSM default alphabet */ - sms->data_coding_scheme = GSM338_DCS_1111_7BIT; - mode = MODE_7BIT; - } else if ((submit->data_coding & 0xFC) == 0xF0) { /* 03.38 DCS default */ - /* pass DCS 1:1 through from SMPP to GSM */ - sms->data_coding_scheme = submit->data_coding; - mode = MODE_7BIT; - } else if (submit->data_coding == 0x02 || - submit->data_coding == 0x04) { - /* 8-bit binary */ - sms->data_coding_scheme = GSM338_DCS_1111_8BIT_DATA; - mode = MODE_8BIT; - } else if ((submit->data_coding & 0xFC) == 0xF4) { /* 03.38 DCS 8bit */ - /* pass DCS 1:1 through from SMPP to GSM */ - sms->data_coding_scheme = submit->data_coding; - mode = MODE_8BIT; - } else if (submit->data_coding == 0x08) { - /* UCS-2 */ - sms->data_coding_scheme = (2 << 2); - mode = MODE_8BIT; - } else { - sms_free(sms); - LOGP(DLSMS, LOGL_ERROR, "SMPP Unknown Data Coding 0x%02x\n", - submit->data_coding); - return ESME_RUNKNOWNERR; - } - - if (mode == MODE_7BIT) { - uint8_t ud_len = 0, padbits = 0; - sms->data_coding_scheme = GSM338_DCS_1111_7BIT; - if (sms->ud_hdr_ind) { - ud_len = *sms_msg + 1; - printf("copying %u bytes user data...\n", ud_len); - memcpy(sms->user_data, sms_msg, - OSMO_MIN(ud_len, sizeof(sms->user_data))); - sms_msg += ud_len; - sms_msg_len -= ud_len; - padbits = 7 - (ud_len % 7); - } - gsm_septets2octets(sms->user_data+ud_len, sms_msg, - sms_msg_len, padbits); - sms->user_data_len = (ud_len*8 + padbits)/7 + sms_msg_len;/* SEPTETS */ - /* FIXME: sms->text */ - } else { - memcpy(sms->user_data, sms_msg, sms_msg_len); - sms->user_data_len = sms_msg_len; - } - - *psms = sms; - return ESME_ROK; -} - -/*! \brief handle incoming libsmpp34 ssubmit_sm_t from remote ESME */ -int handle_smpp_submit(struct osmo_esme *esme, struct submit_sm_t *submit, - struct submit_sm_resp_t *submit_r) -{ - struct gsm_sms *sms; - struct gsm_network *net = esme->smsc->priv; - struct sms_signal_data sig; - int rc = -1; - - rc = submit_to_sms(&sms, net, submit); - if (rc != ESME_ROK) { - submit_r->command_status = rc; - return 0; - } - smpp_esme_get(esme); - sms->smpp.esme = esme; - sms->protocol_id = submit->protocol_id; - - switch (submit->esm_class & 3) { - case 0: /* default */ - case 1: /* datagram */ - case 3: /* store-and-forward */ - rc = db_sms_store(sms); - sms_free(sms); - sms = NULL; - if (rc < 0) { - LOGP(DLSMS, LOGL_ERROR, "SMPP SUBMIT-SM: Unable to " - "store SMS in database\n"); - submit_r->command_status = ESME_RSYSERR; - return 0; - } - strcpy((char *)submit_r->message_id, "msg_id_not_implemented"); - LOGP(DLSMS, LOGL_INFO, "SMPP SUBMIT-SM: Stored in DB\n"); - - memset(&sig, 0, sizeof(sig)); - osmo_signal_dispatch(SS_SMS, S_SMS_SUBMITTED, &sig); - rc = 0; - break; - case 2: /* forward (i.e. transaction) mode */ - LOGP(DLSMS, LOGL_DEBUG, "SMPP SUBMIT-SM: Forwarding in " - "real time (Transaction/Forward mode)\n"); - sms->smpp.transaction_mode = 1; - gsm411_send_sms_subscr(sms->receiver, sms); - rc = 1; /* don't send any response yet */ - break; - } - return rc; -} - -static void alert_all_esme(struct smsc *smsc, struct gsm_subscriber *subscr, - uint8_t smpp_avail_status) -{ - struct osmo_esme *esme; - - llist_for_each_entry(esme, &smsc->esme_list, list) { - /* we currently send an alert notification to each ESME that is - * connected, and do not require a (non-existant) delivery - * pending flag to be set before, FIXME: make this VTY - * configurable */ - if (esme->acl && esme->acl->deliver_src_imsi) { - smpp_tx_alert(esme, TON_Subscriber_Number, - NPI_Land_Mobile_E212, - subscr->imsi, smpp_avail_status); - } else { - smpp_tx_alert(esme, TON_Network_Specific, - NPI_ISDN_E163_E164, - subscr->extension, smpp_avail_status); - } - } -} - - -/*! \brief signal handler for status of attempted SMS deliveries */ -static int smpp_sms_cb(unsigned int subsys, unsigned int signal, - void *handler_data, void *signal_data) -{ - struct sms_signal_data *sig_sms = signal_data; - struct gsm_sms *sms = sig_sms->sms; - struct smsc *smsc = handler_data; - int rc = 0; - - if (!sms) - return 0; - - if (sms->source != SMS_SOURCE_SMPP) - return 0; - - switch (signal) { - case S_SMS_MEM_EXCEEDED: - /* fall-through: There is no ESME_Rxxx result code to - * indicate a MEMORY EXCEEDED in transaction mode back - * to the ESME */ - case S_SMS_UNKNOWN_ERROR: - if (sms->smpp.transaction_mode) { - /* Send back the SUBMIT-SM response with apropriate error */ - LOGP(DLSMS, LOGL_INFO, "SMPP SUBMIT-SM: Error\n"); - rc = smpp_tx_submit_r(sms->smpp.esme, - sms->smpp.sequence_nr, - ESME_RDELIVERYFAILURE, - sms->smpp.msg_id); - } - break; - case S_SMS_DELIVERED: - /* SMS layer tells us the delivery has been completed */ - if (sms->smpp.transaction_mode) { - /* Send back the SUBMIT-SM response */ - LOGP(DLSMS, LOGL_INFO, "SMPP SUBMIT-SM: Success\n"); - rc = smpp_tx_submit_r(sms->smpp.esme, - sms->smpp.sequence_nr, - ESME_ROK, sms->smpp.msg_id); - } - break; - case S_SMS_SMMA: - if (!sig_sms->trans || !sig_sms->trans->subscr) { - /* SMMA without a subscriber? strange... */ - LOGP(DLSMS, LOGL_NOTICE, "SMMA without subscriber?\n"); - break; - } - - /* There's no real 1:1 match for SMMA in SMPP. However, - * an ALERT NOTIFICATION seems to be the most logical - * choice */ - alert_all_esme(smsc, sig_sms->trans->subscr, 0); - break; - } - - return rc; -} - -/*! \brief signal handler for subscriber related signals */ -static int smpp_subscr_cb(unsigned int subsys, unsigned int signal, - void *handler_data, void *signal_data) -{ - struct gsm_subscriber *subscr = signal_data; - struct smsc *smsc = handler_data; - uint8_t smpp_avail_status; - - /* determine the smpp_avail_status depending on attach/detach */ - switch (signal) { - case S_SUBSCR_ATTACHED: - smpp_avail_status = 0; - break; - case S_SUBSCR_DETACHED: - smpp_avail_status = 2; - break; - default: - return 0; - } - - alert_all_esme(smsc, subscr, smpp_avail_status); - - return 0; -} - -/* GSM 03.38 6.2.1 Character expanding (no decode!) */ -static int gsm_7bit_expand(char *text, const uint8_t *user_data, uint8_t septet_l, uint8_t ud_hdr_ind) -{ - int i = 0; - int shift = 0; - uint8_t c; - - /* skip the user data header */ - if (ud_hdr_ind) { - /* get user data header length + 1 (for the 'user data header length'-field) */ - shift = ((user_data[0] + 1) * 8) / 7; - if ((((user_data[0] + 1) * 8) % 7) != 0) - shift++; - septet_l = septet_l - shift; - } - - for (i = 0; i < septet_l; i++) { - c = - ((user_data[((i + shift) * 7 + 7) >> 3] << - (7 - (((i + shift) * 7 + 7) & 7))) | - (user_data[((i + shift) * 7) >> 3] >> - (((i + shift) * 7) & 7))) & 0x7f; - - *(text++) = c; - } - - *text = '\0'; - - return i; -} - - -/* FIXME: libsmpp34 helpers, they should be part of libsmpp34! */ -void append_tlv(tlv_t **req_tlv, uint16_t tag, - const uint8_t *data, uint16_t len) -{ - tlv_t tlv; - - memset(&tlv, 0, sizeof(tlv)); - tlv.tag = tag; - tlv.length = len; - memcpy(tlv.value.octet, data, tlv.length); - build_tlv(req_tlv, &tlv); -} -void append_tlv_u8(tlv_t **req_tlv, uint16_t tag, uint8_t val) -{ - tlv_t tlv; - - memset(&tlv, 0, sizeof(tlv)); - tlv.tag = tag; - tlv.length = 1; - tlv.value.val08 = val; - build_tlv(req_tlv, &tlv); -} -void append_tlv_u16(tlv_t **req_tlv, uint16_t tag, uint16_t val) -{ - tlv_t tlv; - - memset(&tlv, 0, sizeof(tlv)); - tlv.tag = tag; - tlv.length = 2; - tlv.value.val16 = htons(val); - build_tlv(req_tlv, &tlv); -} - -/* Append the Osmocom vendor-specific additional TLVs to a SMPP msg */ -static void append_osmo_tlvs(tlv_t **req_tlv, const struct gsm_lchan *lchan) -{ - int idx = calc_initial_idx(ARRAY_SIZE(lchan->meas_rep), - lchan->meas_rep_idx, 1); - const struct gsm_meas_rep *mr = &lchan->meas_rep[idx]; - const struct gsm_meas_rep_unidir *ul_meas = &mr->ul; - const struct gsm_meas_rep_unidir *dl_meas = &mr->dl; - - /* Osmocom vendor-specific SMPP34 extensions */ - append_tlv_u16(req_tlv, TLVID_osmo_arfcn, lchan->ts->trx->arfcn); - if (mr->flags & MEAS_REP_F_MS_L1) { - uint8_t ms_dbm; - append_tlv_u8(req_tlv, TLVID_osmo_ta, mr->ms_l1.ta); - ms_dbm = ms_pwr_dbm(lchan->ts->trx->bts->band, mr->ms_l1.pwr); - append_tlv_u8(req_tlv, TLVID_osmo_ms_l1_txpwr, ms_dbm); - } else if (mr->flags & MEAS_REP_F_MS_TO) /* Save Timing Offset field = MS Timing Offset + 63 */ - append_tlv_u8(req_tlv, TLVID_osmo_ta, mr->ms_timing_offset + 63); - - append_tlv_u16(req_tlv, TLVID_osmo_rxlev_ul, - rxlev2dbm(ul_meas->full.rx_lev)); - append_tlv_u8(req_tlv, TLVID_osmo_rxqual_ul, ul_meas->full.rx_qual); - - if (mr->flags & MEAS_REP_F_DL_VALID) { - append_tlv_u16(req_tlv, TLVID_osmo_rxlev_dl, - rxlev2dbm(dl_meas->full.rx_lev)); - append_tlv_u8(req_tlv, TLVID_osmo_rxqual_dl, - dl_meas->full.rx_qual); - } - - if (lchan->conn && lchan->conn->subscr) { - struct gsm_subscriber *subscr = lchan->conn->subscr; - size_t imei_len = strlen(subscr->equipment.imei); - if (imei_len) - append_tlv(req_tlv, TLVID_osmo_imei, - (uint8_t *)subscr->equipment.imei, imei_len+1); - } -} - -struct { - uint32_t smpp_status_code; - uint8_t gsm411_cause; -} smpp_to_gsm411_err_array[] = { - - /* Seems like most phones don't care about the failure cause, - * although some will display a different notification for - * GSM411_RP_CAUSE_MO_NUM_UNASSIGNED - * Some provoke a display of "Try again later" - * while others a more definitive "Message sending failed" - */ - - { ESME_RSYSERR, GSM411_RP_CAUSE_MO_DEST_OUT_OF_ORDER }, - { ESME_RINVDSTADR, GSM411_RP_CAUSE_MO_NUM_UNASSIGNED }, - { ESME_RMSGQFUL, GSM411_RP_CAUSE_MO_CONGESTION }, - { ESME_RINVSRCADR, GSM411_RP_CAUSE_MO_SMS_REJECTED }, - { ESME_RINVMSGID, GSM411_RP_CAUSE_INV_TRANS_REF } -}; - -static int smpp_to_gsm411_err(uint32_t smpp_status_code, int *gsm411_cause) -{ - int i; - for (i = 0; i < ARRAY_SIZE(smpp_to_gsm411_err_array); i++) { - if (smpp_to_gsm411_err_array[i].smpp_status_code != smpp_status_code) - continue; - *gsm411_cause = smpp_to_gsm411_err_array[i].gsm411_cause; - return 0; - } - return -1; -} - -static void smpp_cmd_free(struct osmo_smpp_cmd *cmd) -{ - osmo_timer_del(&cmd->response_timer); - llist_del(&cmd->list); - subscr_put(cmd->subscr); - sms_free(cmd->sms); - talloc_free(cmd); -} - -void smpp_cmd_flush_pending(struct osmo_esme *esme) -{ - struct osmo_smpp_cmd *cmd, *next; - - llist_for_each_entry_safe(cmd, next, &esme->smpp_cmd_list, list) - smpp_cmd_free(cmd); -} - -void smpp_cmd_ack(struct osmo_smpp_cmd *cmd) -{ - struct gsm_subscriber_connection *conn; - struct gsm_trans *trans; - - conn = connection_for_subscr(cmd->subscr); - if (!conn) { - LOGP(DSMPP, LOGL_ERROR, "No connection to subscriber anymore\n"); - return; - } - - trans = trans_find_by_id(conn, GSM48_PDISC_SMS, - cmd->sms->gsm411.transaction_id); - if (!trans) { - LOGP(DSMPP, LOGL_ERROR, "GSM transaction %u is gone\n", - cmd->sms->gsm411.transaction_id); - return; - } - - gsm411_send_rp_ack(trans, cmd->sms->gsm411.msg_ref); - smpp_cmd_free(cmd); -} - -void smpp_cmd_err(struct osmo_smpp_cmd *cmd, uint32_t status) -{ - struct gsm_subscriber_connection *conn; - struct gsm_trans *trans; - int gsm411_cause; - - conn = connection_for_subscr(cmd->subscr); - if (!conn) { - LOGP(DSMPP, LOGL_ERROR, "No connection to subscriber anymore\n"); - return; - } - - trans = trans_find_by_id(conn, GSM48_PDISC_SMS, - cmd->sms->gsm411.transaction_id); - if (!trans) { - LOGP(DSMPP, LOGL_ERROR, "GSM transaction %u is gone\n", - cmd->sms->gsm411.transaction_id); - return; - } - - if (smpp_to_gsm411_err(status, &gsm411_cause) < 0) - gsm411_cause = GSM411_RP_CAUSE_MO_NET_OUT_OF_ORDER; - - gsm411_send_rp_error(trans, cmd->sms->gsm411.msg_ref, gsm411_cause); - - smpp_cmd_free(cmd); -} - -static void smpp_deliver_sm_cb(void *data) -{ - smpp_cmd_err(data, ESME_RSYSERR); -} - -static int smpp_cmd_enqueue(struct osmo_esme *esme, - struct gsm_subscriber *subscr, struct gsm_sms *sms, - uint32_t sequence_number, bool *deferred) -{ - struct osmo_smpp_cmd *cmd; - - cmd = talloc_zero(esme, struct osmo_smpp_cmd); - if (!cmd) - return -1; - - cmd->sequence_nr = sequence_number; - cmd->sms = sms; - cmd->subscr = subscr_get(subscr); - - /* FIXME: No predefined value for this response_timer as specified by - * SMPP 3.4 specs, section 7.2. Make this configurable? Don't forget - * lchan keeps busy until we get a reply to this SMPP command. Too high - * value may exhaust resources. - */ - osmo_timer_setup(&cmd->response_timer, smpp_deliver_sm_cb, cmd); - osmo_timer_schedule(&cmd->response_timer, 5, 0); - llist_add_tail(&cmd->list, &esme->smpp_cmd_list); - *deferred = true; - - return 0; -} - -struct osmo_smpp_cmd *smpp_cmd_find_by_seqnum(struct osmo_esme *esme, - uint32_t sequence_nr) -{ - struct osmo_smpp_cmd *cmd; - - llist_for_each_entry(cmd, &esme->smpp_cmd_list, list) { - if (cmd->sequence_nr == sequence_nr) - return cmd; - } - return NULL; -} - -static int deliver_to_esme(struct osmo_esme *esme, struct gsm_sms *sms, - struct gsm_subscriber_connection *conn, - bool *deferred) -{ - struct deliver_sm_t deliver; - int mode, ret; - uint8_t dcs; - - memset(&deliver, 0, sizeof(deliver)); - deliver.command_length = 0; - deliver.command_id = DELIVER_SM; - deliver.command_status = ESME_ROK; - - strcpy((char *)deliver.service_type, "CMT"); - if (esme->acl && esme->acl->deliver_src_imsi) { - deliver.source_addr_ton = TON_Subscriber_Number; - deliver.source_addr_npi = NPI_Land_Mobile_E212; - snprintf((char *)deliver.source_addr, - sizeof(deliver.source_addr), "%s", - conn->subscr->imsi); - } else { - deliver.source_addr_ton = TON_Network_Specific; - deliver.source_addr_npi = NPI_ISDN_E163_E164; - snprintf((char *)deliver.source_addr, - sizeof(deliver.source_addr), "%s", - conn->subscr->extension); - } - - deliver.dest_addr_ton = sms->dst.ton; - deliver.dest_addr_npi = sms->dst.npi; - memcpy(deliver.destination_addr, sms->dst.addr, - sizeof(deliver.destination_addr)); - - deliver.esm_class = 1; /* datagram mode */ - if (sms->ud_hdr_ind) - deliver.esm_class |= 0x40; - if (sms->reply_path_req) - deliver.esm_class |= 0x80; - - deliver.protocol_id = sms->protocol_id; - deliver.priority_flag = 0; - deliver.registered_delivery = 0; - - /* Figure out SMPP DCS from TP-DCS */ - dcs = sms->data_coding_scheme; - if (smpp_determine_scheme(dcs, &deliver.data_coding, &mode) == -1) - return -1; - - /* Transparently pass on DCS via SMPP if requested */ - if (esme->acl && esme->acl->dcs_transparent) - deliver.data_coding = dcs; - - if (mode == MODE_7BIT) { - uint8_t *dst = deliver.short_message; - - /* SMPP has this strange notion of putting 7bit SMS in - * an octet-aligned mode */ - if (sms->ud_hdr_ind) { - /* length (bytes) of UDH inside UD */ - uint8_t udh_len = sms->user_data[0] + 1; - - /* copy over the UDH */ - memcpy(dst, sms->user_data, udh_len); - dst += udh_len; - deliver.sm_length = udh_len; - } - /* add decoded text */ - deliver.sm_length += gsm_7bit_expand((char *)dst, sms->user_data, sms->user_data_len, sms->ud_hdr_ind); - } else { - deliver.sm_length = sms->user_data_len; - memcpy(deliver.short_message, sms->user_data, deliver.sm_length); - deliver.sm_length = sms->user_data_len; - memcpy(deliver.short_message, sms->user_data, deliver.sm_length); - } - - if (esme->acl && esme->acl->osmocom_ext && conn->lchan) - append_osmo_tlvs(&deliver.tlv, conn->lchan); - - ret = smpp_tx_deliver(esme, &deliver); - if (ret < 0) - return ret; - - return smpp_cmd_enqueue(esme, conn->subscr, sms, - deliver.sequence_number, deferred); -} - -static struct smsc *g_smsc; - -int smpp_route_smpp_first(struct gsm_sms *sms, struct gsm_subscriber_connection *conn) -{ - return g_smsc->smpp_first; -} - -int smpp_try_deliver(struct gsm_sms *sms, - struct gsm_subscriber_connection *conn, bool *deferred) -{ - struct osmo_esme *esme; - struct osmo_smpp_addr dst; - - memset(&dst, 0, sizeof(dst)); - dst.ton = sms->dst.ton; - dst.npi = sms->dst.npi; - memcpy(dst.addr, sms->dst.addr, sizeof(dst.addr)); - - esme = smpp_route(g_smsc, &dst); - if (!esme) - return GSM411_RP_CAUSE_MO_NUM_UNASSIGNED; - - return deliver_to_esme(esme, sms, conn, deferred); -} - -struct smsc *smsc_from_vty(struct vty *v) -{ - /* FIXME: this is ugly */ - return g_smsc; -} - -/*! \brief Allocate the OpenBSC SMPP interface struct and init VTY. */ -int smpp_openbsc_alloc_init(void *ctx) -{ - g_smsc = smpp_smsc_alloc_init(ctx); - if (!g_smsc) { - LOGP(DSMPP, LOGL_FATAL, "Cannot allocate smsc struct\n"); - return -1; - } - return smpp_vty_init(); -} - -/*! \brief Launch the OpenBSC SMPP interface with the parameters set from VTY. - */ -int smpp_openbsc_start(struct gsm_network *net) -{ - int rc; - g_smsc->priv = net; - - /* If a VTY configuration has taken place, the values have been stored - * in the smsc struct. Otherwise, use the defaults (NULL -> any, 0 -> - * default SMPP port, see smpp_smsc_bind()). */ - rc = smpp_smsc_start(g_smsc, g_smsc->bind_addr, g_smsc->listen_port); - if (rc < 0) - return rc; - - rc = osmo_signal_register_handler(SS_SMS, smpp_sms_cb, g_smsc); - if (rc < 0) - return rc; - rc = osmo_signal_register_handler(SS_SUBSCR, smpp_subscr_cb, g_smsc); - if (rc < 0) - return rc; - - return 0; -} - diff --git a/openbsc/src/libmsc/smpp_smsc.c b/openbsc/src/libmsc/smpp_smsc.c deleted file mode 100644 index 48a11926..00000000 --- a/openbsc/src/libmsc/smpp_smsc.c +++ /dev/null @@ -1,1030 +0,0 @@ -/* SMPP 3.4 interface, SMSC-side implementation */ -/* (C) 2012 by Harald Welte - * - * All Rights Reserved - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation; either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - */ - - -#include -#include -#include -#include -#include -#include - -#include -#include - -#include -#include -#include - -#include -#include -#include -#include -#include -#include - -#include "smpp_smsc.h" - -#include -#include - -/*! \brief Ugly wrapper. libsmpp34 should do this itself! */ -#define SMPP34_UNPACK(rc, type, str, data, len) \ - memset(str, 0, sizeof(*str)); \ - rc = smpp34_unpack(type, str, data, len) - -enum emse_bind { - ESME_BIND_RX = 0x01, - ESME_BIND_TX = 0x02, -}; - -const struct value_string smpp_status_strs[] = { - { ESME_ROK, "No Error" }, - { ESME_RINVMSGLEN, "Message Length is invalid" }, - { ESME_RINVCMDLEN, "Command Length is invalid" }, - { ESME_RINVCMDID, "Invalid Command ID" }, - { ESME_RINVBNDSTS, "Incorrect BIND Status for given command" }, - { ESME_RALYBND, "ESME Already in Bound State" }, - { ESME_RINVPRTFLG, "Invalid Priority Flag" }, - { ESME_RINVREGDLVFLG, "Invalid Registered Delivery Flag" }, - { ESME_RSYSERR, "System Error" }, - { ESME_RINVSRCADR, "Invalid Source Address" }, - { ESME_RINVDSTADR, "Invalid Destination Address" }, - { ESME_RINVMSGID, "Message ID is invalid" }, - { ESME_RBINDFAIL, "Bind failed" }, - { ESME_RINVPASWD, "Invalid Password" }, - { ESME_RINVSYSID, "Invalid System ID" }, - { ESME_RCANCELFAIL, "Cancel SM Failed" }, - { ESME_RREPLACEFAIL, "Replace SM Failed" }, - { ESME_RMSGQFUL, "Message Queue Full" }, - { ESME_RINVSERTYP, "Invalid Service Type" }, - { ESME_RINVNUMDESTS, "Invalid number of destinations" }, - { ESME_RINVDLNAME, "Invalid Distribution List name" }, - { ESME_RINVDESTFLAG, "Destination flag is invalid" }, - { ESME_RINVSUBREP, "Invalid submit with replace request" }, - { ESME_RINVESMCLASS, "Invalid esm_class field data" }, - { ESME_RCNTSUBDL, "Cannot Submit to Distribution List" }, - { ESME_RSUBMITFAIL, "submit_sm or submit_multi failed" }, - { ESME_RINVSRCTON, "Invalid Source address TON" }, - { ESME_RINVSRCNPI, "Invalid Sourec address NPI" }, - { ESME_RINVDSTTON, "Invalid Destination address TON" }, - { ESME_RINVDSTNPI, "Invalid Desetination address NPI" }, - { ESME_RINVSYSTYP, "Invalid system_type field" }, - { ESME_RINVREPFLAG, "Invalid replace_if_present field" }, - { ESME_RINVNUMMSGS, "Invalid number of messages" }, - { ESME_RTHROTTLED, "Throttling error (ESME has exceeded message limits)" }, - { ESME_RINVSCHED, "Invalid Scheduled Delivery Time" }, - { ESME_RINVEXPIRY, "Invalid message validity period (Expiry time)" }, - { ESME_RINVDFTMSGID, "Predefined Message Invalid or Not Found" }, - { ESME_RX_T_APPN, "ESME Receiver Temporary App Error Code" }, - { ESME_RX_P_APPN, "ESME Receiver Permanent App Error Code" }, - { ESME_RX_R_APPN, "ESME Receiver Reject Message Error Code" }, - { ESME_RQUERYFAIL, "query_sm request failed" }, - { ESME_RINVOPTPARSTREAM,"Error in the optional part of the PDU Body" }, - { ESME_ROPTPARNOTALLWD, "Optional Parameter not allowed" }, - { ESME_RINVPARLEN, "Invalid Parameter Length" }, - { ESME_RMISSINGOPTPARAM,"Expected Optional Parameter missing" }, - { ESME_RINVOPTPARAMVAL, "Invalid Optional Parameter Value" }, - { ESME_RDELIVERYFAILURE,"Delivery Failure (used for data_sm_resp)" }, - { ESME_RUNKNOWNERR, "Unknown Error" }, - { 0, NULL } -}; - -/*! \brief compare if two SMPP addresses are equal */ -int smpp_addr_eq(const struct osmo_smpp_addr *a, - const struct osmo_smpp_addr *b) -{ - if (a->ton == b->ton && - a->npi == b->npi && - !strcmp(a->addr, b->addr)) - return 1; - - return 0; -} - - -struct osmo_smpp_acl *smpp_acl_by_system_id(struct smsc *smsc, - const char *sys_id) -{ - struct osmo_smpp_acl *acl; - - llist_for_each_entry(acl, &smsc->acl_list, list) { - if (!strcmp(acl->system_id, sys_id)) - return acl; - } - - return NULL; -} - -struct osmo_smpp_acl *smpp_acl_alloc(struct smsc *smsc, const char *sys_id) -{ - struct osmo_smpp_acl *acl; - - if (strlen(sys_id) > SMPP_SYS_ID_LEN) - return NULL; - - if (smpp_acl_by_system_id(smsc, sys_id)) - return NULL; - - acl = talloc_zero(smsc, struct osmo_smpp_acl); - if (!acl) - return NULL; - - acl->smsc = smsc; - strcpy(acl->system_id, sys_id); - INIT_LLIST_HEAD(&acl->route_list); - - llist_add_tail(&acl->list, &smsc->acl_list); - - return acl; -} - -void smpp_acl_delete(struct osmo_smpp_acl *acl) -{ - struct osmo_smpp_route *r, *r2; - - llist_del(&acl->list); - - /* kill any active ESMEs */ - if (acl->esme) { - struct osmo_esme *esme = acl->esme; - osmo_fd_unregister(&esme->wqueue.bfd); - close(esme->wqueue.bfd.fd); - esme->wqueue.bfd.fd = -1; - esme->acl = NULL; - smpp_esme_put(esme); - } - - /* delete all routes for this ACL */ - llist_for_each_entry_safe(r, r2, &acl->route_list, list) { - llist_del(&r->list); - llist_del(&r->global_list); - talloc_free(r); - } - - talloc_free(acl); -} - -static struct osmo_smpp_route *route_alloc(struct osmo_smpp_acl *acl) -{ - struct osmo_smpp_route *r; - - r = talloc_zero(acl, struct osmo_smpp_route); - if (!r) - return NULL; - - llist_add_tail(&r->list, &acl->route_list); - llist_add_tail(&r->global_list, &acl->smsc->route_list); - - return r; -} - -int smpp_route_pfx_add(struct osmo_smpp_acl *acl, - const struct osmo_smpp_addr *pfx) -{ - struct osmo_smpp_route *r; - - llist_for_each_entry(r, &acl->route_list, list) { - if (r->type == SMPP_ROUTE_PREFIX && - smpp_addr_eq(&r->u.prefix, pfx)) - return -EEXIST; - } - - r = route_alloc(acl); - if (!r) - return -ENOMEM; - r->type = SMPP_ROUTE_PREFIX; - r->acl = acl; - memcpy(&r->u.prefix, pfx, sizeof(r->u.prefix)); - - return 0; -} - -int smpp_route_pfx_del(struct osmo_smpp_acl *acl, - const struct osmo_smpp_addr *pfx) -{ - struct osmo_smpp_route *r, *r2; - - llist_for_each_entry_safe(r, r2, &acl->route_list, list) { - if (r->type == SMPP_ROUTE_PREFIX && - smpp_addr_eq(&r->u.prefix, pfx)) { - llist_del(&r->list); - talloc_free(r); - return 0; - } - } - - return -ENODEV; -} - - -/*! \brief increaes the use/reference count */ -void smpp_esme_get(struct osmo_esme *esme) -{ - esme->use++; -} - -static void esme_destroy(struct osmo_esme *esme) -{ - osmo_wqueue_clear(&esme->wqueue); - if (esme->wqueue.bfd.fd >= 0) { - osmo_fd_unregister(&esme->wqueue.bfd); - close(esme->wqueue.bfd.fd); - } - smpp_cmd_flush_pending(esme); - llist_del(&esme->list); - talloc_free(esme); -} - -static uint32_t esme_inc_seq_nr(struct osmo_esme *esme) -{ - esme->own_seq_nr++; - if (esme->own_seq_nr > 0x7fffffff) - esme->own_seq_nr = 1; - - return esme->own_seq_nr; -} - -/*! \brief decrease the use/reference count, free if it is 0 */ -void smpp_esme_put(struct osmo_esme *esme) -{ - esme->use--; - if (esme->use <= 0) - esme_destroy(esme); -} - -/*! \brief try to find a SMPP route (ESME) for given destination */ -struct osmo_esme * -smpp_route(const struct smsc *smsc, const struct osmo_smpp_addr *dest) -{ - struct osmo_smpp_route *r; - struct osmo_smpp_acl *acl = NULL; - - DEBUGP(DSMPP, "Looking up route for (%u/%u/%s)\n", - dest->ton, dest->npi, dest->addr); - - /* search for a specific route */ - llist_for_each_entry(r, &smsc->route_list, global_list) { - switch (r->type) { - case SMPP_ROUTE_PREFIX: - DEBUGP(DSMPP, "Checking prefix route (%u/%u/%s)->%s\n", - r->u.prefix.ton, r->u.prefix.npi, r->u.prefix.addr, - r->acl->system_id); - if (r->u.prefix.ton == dest->ton && - r->u.prefix.npi == dest->npi && - !strncmp(r->u.prefix.addr, dest->addr, - strlen(r->u.prefix.addr))) { - DEBUGP(DSMPP, "Found prefix route ACL\n"); - acl = r->acl; - } - break; - default: - break; - } - - if (acl) - break; - } - - if (!acl) { - /* check for default route */ - if (smsc->def_route) { - DEBUGP(DSMPP, "Using existing default route\n"); - acl = smsc->def_route; - } - } - - if (acl && acl->esme) { - struct osmo_esme *esme; - DEBUGP(DSMPP, "ACL even has ESME, we can route to it!\n"); - esme = acl->esme; - if (esme->bind_flags & ESME_BIND_RX) - return esme; - else - LOGP(DSMPP, LOGL_NOTICE, "[%s] is matching route, " - "but not bound for Rx, discarding MO SMS\n", - esme->system_id); - } - - return NULL; -} - - -/*! \brief initialize the libsmpp34 data structure for a response */ -#define INIT_RESP(type, resp, req) { \ - memset((resp), 0, sizeof(*(resp))); \ - (resp)->command_length = 0; \ - (resp)->command_id = type; \ - (resp)->command_status = ESME_ROK; \ - (resp)->sequence_number = (req)->sequence_number; \ -} - -/*! \brief pack a libsmpp34 data strcutrure and send it to the ESME */ -#define PACK_AND_SEND(esme, ptr) pack_and_send(esme, (ptr)->command_id, ptr) -static int pack_and_send(struct osmo_esme *esme, uint32_t type, void *ptr) -{ - struct msgb *msg = msgb_alloc(4096, "SMPP_Tx"); - int rc, rlen; - if (!msg) - return -ENOMEM; - - rc = smpp34_pack(type, msg->tail, msgb_tailroom(msg), &rlen, ptr); - if (rc != 0) { - LOGP(DSMPP, LOGL_ERROR, "[%s] Error during smpp34_pack(): %s\n", - esme->system_id, smpp34_strerror); - msgb_free(msg); - return -EINVAL; - } - msgb_put(msg, rlen); - - if (osmo_wqueue_enqueue(&esme->wqueue, msg) != 0) { - LOGP(DSMPP, LOGL_ERROR, "[%s] Write queue full. Dropping message\n", - esme->system_id); - msgb_free(msg); - return -EAGAIN; - } - return 0; -} - -/*! \brief transmit a generic NACK to a remote ESME */ -static int smpp_tx_gen_nack(struct osmo_esme *esme, uint32_t seq, uint32_t status) -{ - struct generic_nack_t nack; - char buf[SMALL_BUFF]; - - nack.command_length = 0; - nack.command_id = GENERIC_NACK; - nack.sequence_number = seq; - nack.command_status = status; - - LOGP(DSMPP, LOGL_ERROR, "[%s] Tx GENERIC NACK: %s\n", - esme->system_id, str_command_status(status, buf)); - - return PACK_AND_SEND(esme, &nack); -} - -/*! \brief retrieve SMPP command ID from a msgb */ -static inline uint32_t smpp_msgb_cmdid(struct msgb *msg) -{ - uint8_t *tmp = msgb_data(msg) + 4; - return ntohl(*(uint32_t *)tmp); -} - -/*! \brief retrieve SMPP sequence number from a msgb */ -static inline uint32_t smpp_msgb_seq(struct msgb *msg) -{ - uint8_t *tmp = msgb_data(msg); - return ntohl(*(uint32_t *)tmp); -} - -/*! \brief handle an incoming SMPP generic NACK */ -static int smpp_handle_gen_nack(struct osmo_esme *esme, struct msgb *msg) -{ - struct generic_nack_t nack; - char buf[SMALL_BUFF]; - int rc; - - SMPP34_UNPACK(rc, GENERIC_NACK, &nack, msgb_data(msg), - msgb_length(msg)); - if (rc < 0) { - LOGP(DSMPP, LOGL_ERROR, "[%s] Error in smpp34_unpack():%s\n", - esme->system_id, smpp34_strerror); - return rc; - } - - LOGP(DSMPP, LOGL_ERROR, "[%s] Rx GENERIC NACK: %s\n", - esme->system_id, str_command_status(nack.command_status, buf)); - - return 0; -} - -static int _process_bind(struct osmo_esme *esme, uint8_t if_version, - uint32_t bind_flags, const char *sys_id, - const char *passwd) -{ - struct osmo_smpp_acl *acl; - - if (if_version != SMPP_VERSION) - return ESME_RSYSERR; - - if (esme->bind_flags) - return ESME_RALYBND; - - esme->smpp_version = if_version; - snprintf(esme->system_id, sizeof(esme->system_id), "%s", sys_id); - - acl = smpp_acl_by_system_id(esme->smsc, esme->system_id); - if (!esme->smsc->accept_all) { - if (!acl) { - /* This system is unknown */ - return ESME_RINVSYSID; - } else { - if (strlen(acl->passwd) && - strcmp(acl->passwd, passwd)) { - return ESME_RINVPASWD; - } - } - } - if (acl) { - esme->acl = acl; - acl->esme = esme; - } - - esme->bind_flags = bind_flags; - - return ESME_ROK; -} - - -/*! \brief handle an incoming SMPP BIND RECEIVER */ -static int smpp_handle_bind_rx(struct osmo_esme *esme, struct msgb *msg) -{ - struct bind_receiver_t bind; - struct bind_receiver_resp_t bind_r; - int rc; - - SMPP34_UNPACK(rc, BIND_RECEIVER, &bind, msgb_data(msg), - msgb_length(msg)); - if (rc < 0) { - LOGP(DSMPP, LOGL_ERROR, "[%s] Error in smpp34_unpack():%s\n", - esme->system_id, smpp34_strerror); - return rc; - } - - INIT_RESP(BIND_TRANSMITTER_RESP, &bind_r, &bind); - - LOGP(DSMPP, LOGL_INFO, "[%s] Rx BIND Rx from (Version %02x)\n", - bind.system_id, bind.interface_version); - - rc = _process_bind(esme, bind.interface_version, ESME_BIND_RX, - (const char *)bind.system_id, (const char *)bind.password); - bind_r.command_status = rc; - - return PACK_AND_SEND(esme, &bind_r); -} - -/*! \brief handle an incoming SMPP BIND TRANSMITTER */ -static int smpp_handle_bind_tx(struct osmo_esme *esme, struct msgb *msg) -{ - struct bind_transmitter_t bind; - struct bind_transmitter_resp_t bind_r; - struct tlv_t tlv; - int rc; - - SMPP34_UNPACK(rc, BIND_TRANSMITTER, &bind, msgb_data(msg), - msgb_length(msg)); - if (rc < 0) { - LOGP(DSMPP, LOGL_ERROR, "[%s] Error in smpp34_unpack():%s\n", - esme->system_id, smpp34_strerror); - return rc; - } - - INIT_RESP(BIND_TRANSMITTER_RESP, &bind_r, &bind); - - LOGP(DSMPP, LOGL_INFO, "[%s] Rx BIND Tx (Version %02x)\n", - bind.system_id, bind.interface_version); - - rc = _process_bind(esme, bind.interface_version, ESME_BIND_TX, - (const char *)bind.system_id, (const char *)bind.password); - bind_r.command_status = rc; - - /* build response */ - snprintf((char *)bind_r.system_id, sizeof(bind_r.system_id), "%s", - esme->smsc->system_id); - - /* add interface version TLV */ - tlv.tag = TLVID_sc_interface_version; - tlv.length = sizeof(uint8_t); - tlv.value.val16 = esme->smpp_version; - build_tlv(&bind_r.tlv, &tlv); - - return PACK_AND_SEND(esme, &bind_r); -} - -/*! \brief handle an incoming SMPP BIND TRANSCEIVER */ -static int smpp_handle_bind_trx(struct osmo_esme *esme, struct msgb *msg) -{ - struct bind_transceiver_t bind; - struct bind_transceiver_resp_t bind_r; - int rc; - - SMPP34_UNPACK(rc, BIND_TRANSCEIVER, &bind, msgb_data(msg), - msgb_length(msg)); - if (rc < 0) { - LOGP(DSMPP, LOGL_ERROR, "[%s] Error in smpp34_unpack():%s\n", - esme->system_id, smpp34_strerror); - return rc; - } - - INIT_RESP(BIND_TRANSCEIVER_RESP, &bind_r, &bind); - - LOGP(DSMPP, LOGL_INFO, "[%s] Rx BIND Trx (Version %02x)\n", - bind.system_id, bind.interface_version); - - rc = _process_bind(esme, bind.interface_version, ESME_BIND_RX|ESME_BIND_TX, - (const char *)bind.system_id, (const char *)bind.password); - bind_r.command_status = rc; - - return PACK_AND_SEND(esme, &bind_r); -} - -/*! \brief handle an incoming SMPP UNBIND */ -static int smpp_handle_unbind(struct osmo_esme *esme, struct msgb *msg) -{ - struct unbind_t unbind; - struct unbind_resp_t unbind_r; - int rc; - - SMPP34_UNPACK(rc, UNBIND, &unbind, msgb_data(msg), - msgb_length(msg)); - if (rc < 0) { - LOGP(DSMPP, LOGL_ERROR, "[%s] Error in smpp34_unpack():%s\n", - esme->system_id, smpp34_strerror); - return rc; - } - - INIT_RESP(UNBIND_RESP, &unbind_r, &unbind); - - LOGP(DSMPP, LOGL_INFO, "[%s] Rx UNBIND\n", esme->system_id); - - if (esme->bind_flags == 0) { - unbind_r.command_status = ESME_RINVBNDSTS; - goto err; - } - - esme->bind_flags = 0; -err: - return PACK_AND_SEND(esme, &unbind_r); -} - -/*! \brief handle an incoming SMPP ENQUIRE LINK */ -static int smpp_handle_enq_link(struct osmo_esme *esme, struct msgb *msg) -{ - struct enquire_link_t enq; - struct enquire_link_resp_t enq_r; - int rc; - - SMPP34_UNPACK(rc, ENQUIRE_LINK, &enq, msgb_data(msg), - msgb_length(msg)); - if (rc < 0) { - LOGP(DSMPP, LOGL_ERROR, "[%s] Error in smpp34_unpack():%s\n", - esme->system_id, smpp34_strerror); - return rc; - } - - LOGP(DSMPP, LOGL_DEBUG, "[%s] Rx Enquire Link\n", esme->system_id); - - INIT_RESP(ENQUIRE_LINK_RESP, &enq_r, &enq); - - LOGP(DSMPP, LOGL_DEBUG, "[%s] Tx Enquire Link Response\n", esme->system_id); - - return PACK_AND_SEND(esme, &enq_r); -} - -/*! \brief send a SUBMIT-SM RESPONSE to a remote ESME */ -int smpp_tx_submit_r(struct osmo_esme *esme, uint32_t sequence_nr, - uint32_t command_status, char *msg_id) -{ - struct submit_sm_resp_t submit_r; - - memset(&submit_r, 0, sizeof(submit_r)); - submit_r.command_length = 0; - submit_r.command_id = SUBMIT_SM_RESP; - submit_r.command_status = command_status; - submit_r.sequence_number= sequence_nr; - snprintf((char *) submit_r.message_id, sizeof(submit_r.message_id), "%s", msg_id); - - return PACK_AND_SEND(esme, &submit_r); -} - -static const struct value_string smpp_avail_strs[] = { - { 0, "Available" }, - { 1, "Denied" }, - { 2, "Unavailable" }, - { 0, NULL } -}; - -/*! \brief send an ALERT_NOTIFICATION to a remote ESME */ -int smpp_tx_alert(struct osmo_esme *esme, uint8_t ton, uint8_t npi, - const char *addr, uint8_t avail_status) -{ - struct alert_notification_t alert; - struct tlv_t tlv; - - memset(&alert, 0, sizeof(alert)); - alert.command_length = 0; - alert.command_id = ALERT_NOTIFICATION; - alert.command_status = ESME_ROK; - alert.sequence_number = esme_inc_seq_nr(esme); - alert.source_addr_ton = ton; - alert.source_addr_npi = npi; - snprintf((char *)alert.source_addr, sizeof(alert.source_addr), "%s", addr); - - tlv.tag = TLVID_ms_availability_status; - tlv.length = sizeof(uint8_t); - tlv.value.val08 = avail_status; - build_tlv(&alert.tlv, &tlv); - - LOGP(DSMPP, LOGL_DEBUG, "[%s] Tx ALERT_NOTIFICATION (%s/%u/%u): %s\n", - esme->system_id, alert.source_addr, alert.source_addr_ton, - alert.source_addr_npi, - get_value_string(smpp_avail_strs, avail_status)); - - return PACK_AND_SEND(esme, &alert); -} - -/* \brief send a DELIVER-SM message to given ESME */ -int smpp_tx_deliver(struct osmo_esme *esme, struct deliver_sm_t *deliver) -{ - deliver->sequence_number = esme_inc_seq_nr(esme); - - return PACK_AND_SEND(esme, deliver); -} - -/*! \brief handle an incoming SMPP DELIVER-SM RESPONSE */ -static int smpp_handle_deliver_resp(struct osmo_esme *esme, struct msgb *msg) -{ - struct deliver_sm_resp_t deliver_r; - struct osmo_smpp_cmd *cmd; - int rc; - - memset(&deliver_r, 0, sizeof(deliver_r)); - SMPP34_UNPACK(rc, DELIVER_SM_RESP, &deliver_r, msgb_data(msg), - msgb_length(msg)); - if (rc < 0) { - LOGP(DSMPP, LOGL_ERROR, "[%s] Error in smpp34_unpack():%s\n", - esme->system_id, smpp34_strerror); - return rc; - } - - cmd = smpp_cmd_find_by_seqnum(esme, deliver_r.sequence_number); - if (!cmd) { - LOGP(DSMPP, LOGL_ERROR, "[%s] Rx DELIVER-SM RESP !? (%s)\n", - esme->system_id, get_value_string(smpp_status_strs, - deliver_r.command_status)); - return -1; - } - - if (deliver_r.command_status == ESME_ROK) - smpp_cmd_ack(cmd); - else - smpp_cmd_err(cmd, deliver_r.command_status); - - LOGP(DSMPP, LOGL_INFO, "[%s] Rx DELIVER-SM RESP (%s)\n", - esme->system_id, get_value_string(smpp_status_strs, - deliver_r.command_status)); - - return 0; -} - -/*! \brief handle an incoming SMPP SUBMIT-SM */ -static int smpp_handle_submit(struct osmo_esme *esme, struct msgb *msg) -{ - struct submit_sm_t submit; - struct submit_sm_resp_t submit_r; - int rc; - - memset(&submit, 0, sizeof(submit)); - SMPP34_UNPACK(rc, SUBMIT_SM, &submit, msgb_data(msg), - msgb_length(msg)); - if (rc < 0) { - LOGP(DSMPP, LOGL_ERROR, "[%s] Error in smpp34_unpack():%s\n", - esme->system_id, smpp34_strerror); - return rc; - } - - INIT_RESP(SUBMIT_SM_RESP, &submit_r, &submit); - - if (!(esme->bind_flags & ESME_BIND_TX)) { - submit_r.command_status = ESME_RINVBNDSTS; - return PACK_AND_SEND(esme, &submit_r); - } - - LOGP(DSMPP, LOGL_INFO, "[%s] Rx SUBMIT-SM (%s/%u/%u)\n", - esme->system_id, submit.destination_addr, - submit.dest_addr_ton, submit.dest_addr_npi); - - INIT_RESP(SUBMIT_SM_RESP, &submit_r, &submit); - - rc = handle_smpp_submit(esme, &submit, &submit_r); - if (rc == 0) - return PACK_AND_SEND(esme, &submit_r); - - return rc; -} - -/*! \brief one complete SMPP PDU from the ESME has been received */ -static int smpp_pdu_rx(struct osmo_esme *esme, struct msgb *msg __uses) -{ - uint32_t cmd_id = smpp_msgb_cmdid(msg); - int rc = 0; - - LOGP(DSMPP, LOGL_DEBUG, "[%s] smpp_pdu_rx(%s)\n", esme->system_id, - osmo_hexdump(msgb_data(msg), msgb_length(msg))); - - switch (cmd_id) { - case GENERIC_NACK: - rc = smpp_handle_gen_nack(esme, msg); - break; - case BIND_RECEIVER: - rc = smpp_handle_bind_rx(esme, msg); - break; - case BIND_TRANSMITTER: - rc = smpp_handle_bind_tx(esme, msg); - break; - case BIND_TRANSCEIVER: - rc = smpp_handle_bind_trx(esme, msg); - break; - case UNBIND: - rc = smpp_handle_unbind(esme, msg); - break; - case ENQUIRE_LINK: - rc = smpp_handle_enq_link(esme, msg); - break; - case SUBMIT_SM: - rc = smpp_handle_submit(esme, msg); - break; - case DELIVER_SM_RESP: - rc = smpp_handle_deliver_resp(esme, msg); - break; - case DELIVER_SM: - break; - case DATA_SM: - break; - case CANCEL_SM: - case QUERY_SM: - case REPLACE_SM: - case SUBMIT_MULTI: - LOGP(DSMPP, LOGL_NOTICE, "[%s] Unimplemented PDU Command " - "0x%08x\n", esme->system_id, cmd_id); - break; - default: - LOGP(DSMPP, LOGL_ERROR, "[%s] Unknown PDU Command 0x%08x\n", - esme->system_id, cmd_id); - rc = smpp_tx_gen_nack(esme, smpp_msgb_seq(msg), ESME_RINVCMDID); - break; - } - - return rc; -} - -/* This macro should be called after a call to read() in the read_cb of an - * osmo_fd to properly check for errors. - * rc is the return value of read, err_label is the label to jump to in case of - * an error. The code there should handle closing the connection. - * FIXME: This code should go in libosmocore utils.h so it can be used by other - * projects as well. - * */ -#define OSMO_FD_CHECK_READ(rc, err_label) \ - if (rc < 0) { \ - /* EINTR is a non-fatal error, just try again */ \ - if (errno == EINTR) \ - return 0; \ - goto err_label; \ - } else if (rc == 0) { \ - goto err_label; \ - } - -/* !\brief call-back when per-ESME TCP socket has some data to be read */ -static int esme_link_read_cb(struct osmo_fd *ofd) -{ - struct osmo_esme *esme = ofd->data; - uint32_t len; - uint8_t *lenptr = (uint8_t *) &len; - uint8_t *cur; - struct msgb *msg; - ssize_t rdlen, rc; - - switch (esme->read_state) { - case READ_ST_IN_LEN: - rdlen = sizeof(uint32_t) - esme->read_idx; - rc = read(ofd->fd, lenptr + esme->read_idx, rdlen); - if (rc < 0) - LOGP(DSMPP, LOGL_ERROR, "[%s] read returned %zd (%s)\n", - esme->system_id, rc, strerror(errno)); - OSMO_FD_CHECK_READ(rc, dead_socket); - - esme->read_idx += rc; - - if (esme->read_idx >= sizeof(uint32_t)) { - esme->read_len = ntohl(len); - if (esme->read_len < 8 || esme->read_len > UINT16_MAX) { - LOGP(DSMPP, LOGL_ERROR, "[%s] length invalid %u\n", - esme->system_id, esme->read_len); - goto dead_socket; - } - - msg = msgb_alloc(esme->read_len, "SMPP Rx"); - if (!msg) - return -ENOMEM; - esme->read_msg = msg; - cur = msgb_put(msg, sizeof(uint32_t)); - memcpy(cur, lenptr, sizeof(uint32_t)); - esme->read_state = READ_ST_IN_MSG; - esme->read_idx = sizeof(uint32_t); - } - break; - case READ_ST_IN_MSG: - msg = esme->read_msg; - rdlen = esme->read_len - esme->read_idx; - rc = read(ofd->fd, msg->tail, OSMO_MIN(rdlen, msgb_tailroom(msg))); - if (rc < 0) - LOGP(DSMPP, LOGL_ERROR, "[%s] read returned %zd (%s)\n", - esme->system_id, rc, strerror(errno)); - OSMO_FD_CHECK_READ(rc, dead_socket); - - esme->read_idx += rc; - msgb_put(msg, rc); - - if (esme->read_idx >= esme->read_len) { - rc = smpp_pdu_rx(esme, esme->read_msg); - msgb_free(esme->read_msg); - esme->read_msg = NULL; - esme->read_idx = 0; - esme->read_len = 0; - esme->read_state = READ_ST_IN_LEN; - } - break; - } - - return 0; -dead_socket: - msgb_free(esme->read_msg); - osmo_fd_unregister(&esme->wqueue.bfd); - close(esme->wqueue.bfd.fd); - esme->wqueue.bfd.fd = -1; - smpp_esme_put(esme); - - return 0; -} - -/* call-back of write queue once it wishes to write a message to the socket */ -static int esme_link_write_cb(struct osmo_fd *ofd, struct msgb *msg) -{ - struct osmo_esme *esme = ofd->data; - int rc; - - rc = write(ofd->fd, msgb_data(msg), msgb_length(msg)); - if (rc == 0) { - osmo_fd_unregister(&esme->wqueue.bfd); - close(esme->wqueue.bfd.fd); - esme->wqueue.bfd.fd = -1; - smpp_esme_put(esme); - } else if (rc < msgb_length(msg)) { - LOGP(DSMPP, LOGL_ERROR, "[%s] Short write\n", esme->system_id); - return -1; - } - - return 0; -} - -/* callback for already-accepted new TCP socket */ -static int link_accept_cb(struct smsc *smsc, int fd, - struct sockaddr_storage *s, socklen_t s_len) -{ - struct osmo_esme *esme = talloc_zero(smsc, struct osmo_esme); - if (!esme) { - close(fd); - return -ENOMEM; - } - - INIT_LLIST_HEAD(&esme->smpp_cmd_list); - smpp_esme_get(esme); - esme->own_seq_nr = rand(); - esme_inc_seq_nr(esme); - esme->smsc = smsc; - osmo_wqueue_init(&esme->wqueue, 10); - esme->wqueue.bfd.fd = fd; - esme->wqueue.bfd.data = esme; - esme->wqueue.bfd.when = BSC_FD_READ; - - if (osmo_fd_register(&esme->wqueue.bfd) != 0) { - close(fd); - talloc_free(esme); - return -EIO; - } - - esme->wqueue.read_cb = esme_link_read_cb; - esme->wqueue.write_cb = esme_link_write_cb; - - esme->sa_len = OSMO_MIN(sizeof(esme->sa), s_len); - memcpy(&esme->sa, s, esme->sa_len); - - llist_add_tail(&esme->list, &smsc->esme_list); - - return 0; -} - -/* callback of listening TCP socket */ -static int smsc_fd_cb(struct osmo_fd *ofd, unsigned int what) -{ - int rc; - struct sockaddr_storage sa; - socklen_t sa_len = sizeof(sa); - - rc = accept(ofd->fd, (struct sockaddr *)&sa, &sa_len); - if (rc < 0) { - LOGP(DSMPP, LOGL_ERROR, "Accept returns %d (%s)\n", - rc, strerror(errno)); - return rc; - } - return link_accept_cb(ofd->data, rc, &sa, sa_len); -} - -/*! \brief allocate and initialize an smsc struct from talloc context ctx. */ -struct smsc *smpp_smsc_alloc_init(void *ctx) -{ - struct smsc *smsc = talloc_zero(ctx, struct smsc); - - INIT_LLIST_HEAD(&smsc->esme_list); - INIT_LLIST_HEAD(&smsc->acl_list); - INIT_LLIST_HEAD(&smsc->route_list); - - smsc->listen_ofd.data = smsc; - smsc->listen_ofd.cb = smsc_fd_cb; - - return smsc; -} - -/*! \brief Set the SMPP address and port without binding. */ -int smpp_smsc_conf(struct smsc *smsc, const char *bind_addr, uint16_t port) -{ - talloc_free((void*)smsc->bind_addr); - smsc->bind_addr = NULL; - if (bind_addr) { - smsc->bind_addr = talloc_strdup(smsc, bind_addr); - if (!smsc->bind_addr) - return -ENOMEM; - } - smsc->listen_port = port; - return 0; -} - -/*! \brief Bind to given address and port and accept connections. - * \param[in] bind_addr Local IP address, may be NULL for any. - * \param[in] port TCP port number, may be 0 for default SMPP (2775). - */ -int smpp_smsc_start(struct smsc *smsc, const char *bind_addr, uint16_t port) -{ - int rc; - - /* default port for SMPP */ - if (!port) - port = 2775; - - smpp_smsc_stop(smsc); - - LOGP(DSMPP, LOGL_NOTICE, "SMPP at %s %d\n", - bind_addr? bind_addr : "0.0.0.0", port); - - rc = osmo_sock_init_ofd(&smsc->listen_ofd, AF_UNSPEC, SOCK_STREAM, - IPPROTO_TCP, bind_addr, port, - OSMO_SOCK_F_BIND); - if (rc < 0) - return rc; - - /* store new address and port */ - rc = smpp_smsc_conf(smsc, bind_addr, port); - if (rc) - smpp_smsc_stop(smsc); - return rc; -} - -/*! \brief Change a running connection to a different address/port, and upon - * error switch back to the running configuration. */ -int smpp_smsc_restart(struct smsc *smsc, const char *bind_addr, uint16_t port) -{ - int rc; - - rc = smpp_smsc_start(smsc, bind_addr, port); - if (rc) - /* if there is an error, try to re-bind to the old port */ - return smpp_smsc_start(smsc, smsc->bind_addr, smsc->listen_port); - return 0; -} - -/*! /brief Close SMPP connection. */ -void smpp_smsc_stop(struct smsc *smsc) -{ - if (smsc->listen_ofd.fd > 0) { - close(smsc->listen_ofd.fd); - smsc->listen_ofd.fd = 0; - osmo_fd_unregister(&smsc->listen_ofd); - } -} diff --git a/openbsc/src/libmsc/smpp_smsc.h b/openbsc/src/libmsc/smpp_smsc.h deleted file mode 100644 index d8e82e42..00000000 --- a/openbsc/src/libmsc/smpp_smsc.h +++ /dev/null @@ -1,166 +0,0 @@ -#ifndef _SMPP_SMSC_H -#define _SMPP_SMSC_H - -#include -#include - -#include -#include -#include -#include - -#include -#include -#include - -#define SMPP_SYS_ID_LEN 16 -#define SMPP_PASSWD_LEN 16 - -#define MODE_7BIT 7 -#define MODE_8BIT 8 - -enum esme_read_state { - READ_ST_IN_LEN = 0, - READ_ST_IN_MSG = 1, -}; - -struct osmo_smpp_acl; - -struct osmo_smpp_addr { - uint8_t ton; - uint8_t npi; - char addr[21+1]; -}; - -struct osmo_esme { - struct llist_head list; - struct smsc *smsc; - struct osmo_smpp_acl *acl; - int use; - - struct llist_head smpp_cmd_list; - - uint32_t own_seq_nr; - - struct osmo_wqueue wqueue; - struct sockaddr_storage sa; - socklen_t sa_len; - - enum esme_read_state read_state; - uint32_t read_len; - uint32_t read_idx; - struct msgb *read_msg; - - uint8_t smpp_version; - char system_id[SMPP_SYS_ID_LEN+1]; - - uint8_t bind_flags; -}; - -struct osmo_smpp_acl { - struct llist_head list; - struct smsc *smsc; - struct osmo_esme *esme; - char *description; - char system_id[SMPP_SYS_ID_LEN+1]; - char passwd[SMPP_PASSWD_LEN+1]; - int default_route; - int deliver_src_imsi; - int osmocom_ext; - int dcs_transparent; - struct llist_head route_list; -}; - -enum osmo_smpp_rtype { - SMPP_ROUTE_NONE, - SMPP_ROUTE_PREFIX, -}; - -struct osmo_smpp_route { - struct llist_head list; /*!< in acl.route_list */ - struct llist_head global_list; /*!< in smsc->route_list */ - struct osmo_smpp_acl *acl; - enum osmo_smpp_rtype type; - union { - struct osmo_smpp_addr prefix; - } u; -}; - -struct osmo_smpp_cmd { - struct llist_head list; - struct gsm_subscriber *subscr; - struct gsm_sms *sms; - uint32_t sequence_nr; - struct osmo_timer_list response_timer; -}; - -struct osmo_smpp_cmd *smpp_cmd_find_by_seqnum(struct osmo_esme *esme, - uint32_t sequence_number); -void smpp_cmd_ack(struct osmo_smpp_cmd *cmd); -void smpp_cmd_err(struct osmo_smpp_cmd *cmd, uint32_t status); -void smpp_cmd_flush_pending(struct osmo_esme *esme); - -struct smsc { - struct osmo_fd listen_ofd; - struct llist_head esme_list; - struct llist_head acl_list; - struct llist_head route_list; - const char *bind_addr; - uint16_t listen_port; - char system_id[SMPP_SYS_ID_LEN+1]; - int accept_all; - int smpp_first; - struct osmo_smpp_acl *def_route; - void *priv; -}; - -int smpp_addr_eq(const struct osmo_smpp_addr *a, - const struct osmo_smpp_addr *b); - -struct smsc *smpp_smsc_alloc_init(void *ctx); -int smpp_smsc_conf(struct smsc *smsc, const char *bind_addr, uint16_t port); -int smpp_smsc_start(struct smsc *smsc, const char *bind_addr, uint16_t port); -int smpp_smsc_restart(struct smsc *smsc, const char *bind_addr, uint16_t port); -void smpp_smsc_stop(struct smsc *smsc); - -void smpp_esme_get(struct osmo_esme *esme); -void smpp_esme_put(struct osmo_esme *esme); - -struct osmo_esme * -smpp_route(const struct smsc *smsc, const struct osmo_smpp_addr *dest); - -struct osmo_smpp_acl *smpp_acl_alloc(struct smsc *smsc, const char *sys_id); -struct osmo_smpp_acl *smpp_acl_by_system_id(struct smsc *smsc, - const char *sys_id); -void smpp_acl_delete(struct osmo_smpp_acl *acl); - -int smpp_tx_submit_r(struct osmo_esme *esme, uint32_t sequence_nr, - uint32_t command_status, char *msg_id); - -int smpp_tx_alert(struct osmo_esme *esme, uint8_t ton, uint8_t npi, - const char *addr, uint8_t avail_status); - -int smpp_tx_deliver(struct osmo_esme *esme, struct deliver_sm_t *deliver); - -int handle_smpp_submit(struct osmo_esme *esme, struct submit_sm_t *submit, - struct submit_sm_resp_t *submit_r); - -int smpp_route_pfx_add(struct osmo_smpp_acl *acl, - const struct osmo_smpp_addr *pfx); -int smpp_route_pfx_del(struct osmo_smpp_acl *acl, - const struct osmo_smpp_addr *pfx); - -int smpp_vty_init(void); - -int smpp_determine_scheme(uint8_t dcs, uint8_t *data_coding, int *mode); - - - -struct gsm_sms; -struct gsm_subscriber_connection; - -int smpp_route_smpp_first(struct gsm_sms *sms, - struct gsm_subscriber_connection *conn); -int smpp_try_deliver(struct gsm_sms *sms, - struct gsm_subscriber_connection *conn, bool *deferred); -#endif diff --git a/openbsc/src/libmsc/smpp_utils.c b/openbsc/src/libmsc/smpp_utils.c deleted file mode 100644 index d0850d8c..00000000 --- a/openbsc/src/libmsc/smpp_utils.c +++ /dev/null @@ -1,62 +0,0 @@ - -/* (C) 2012-2013 by Harald Welte - * - * All Rights Reserved - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation; either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - */ - - -#include "smpp_smsc.h" -#include - - -int smpp_determine_scheme(uint8_t dcs, uint8_t *data_coding, int *mode) -{ - if ((dcs & 0xF0) == 0xF0) { - if (dcs & 0x04) { - /* bit 2 == 1: 8bit data */ - *data_coding = 0x02; - *mode = MODE_8BIT; - } else { - /* bit 2 == 0: default alphabet */ - *data_coding = 0x01; - *mode = MODE_7BIT; - } - } else if ((dcs & 0xE0) == 0) { - switch (dcs & 0xC) { - case 0: - *data_coding = 0x01; - *mode = MODE_7BIT; - break; - case 4: - *data_coding = 0x02; - *mode = MODE_8BIT; - break; - case 8: - *data_coding = 0x08; /* UCS-2 */ - *mode = MODE_8BIT; - break; - default: - goto unknown_mo; - } - } else { -unknown_mo: - LOGP(DLSMS, LOGL_ERROR, "SMPP MO Unknown Data Coding 0x%02x\n", dcs); - return -1; - } - - return 0; - -} diff --git a/openbsc/src/libmsc/smpp_vty.c b/openbsc/src/libmsc/smpp_vty.c deleted file mode 100644 index 13467f18..00000000 --- a/openbsc/src/libmsc/smpp_vty.c +++ /dev/null @@ -1,612 +0,0 @@ -/* SMPP vty interface */ - -/* (C) 2012 by Harald Welte - * All Rights Reserved - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation; either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - * - */ - -#include -#include -#include -#include -#include - -#include -#include -#include - -#include -#include -#include - -#include - -#include "smpp_smsc.h" - -struct smsc *smsc_from_vty(struct vty *v); - -static struct cmd_node smpp_node = { - SMPP_NODE, - "%s(config-smpp)# ", - 1, -}; - -static struct cmd_node esme_node = { - SMPP_ESME_NODE, - "%s(config-smpp-esme)# ", - 1, -}; - -DEFUN(cfg_smpp, cfg_smpp_cmd, - "smpp", "Configure SMPP SMS Interface") -{ - vty->node = SMPP_NODE; - - return CMD_SUCCESS; -} - -DEFUN(cfg_smpp_first, cfg_smpp_first_cmd, - "smpp-first", - "Try SMPP routes before the subscriber DB\n") -{ - struct smsc *smsc = smsc_from_vty(vty); - smsc->smpp_first = 1; - return CMD_SUCCESS; -} - -DEFUN(cfg_no_smpp_first, cfg_no_smpp_first_cmd, - "no smpp-first", - NO_STR "Try SMPP before routes before the subscriber DB\n") -{ - struct smsc *smsc = smsc_from_vty(vty); - smsc->smpp_first = 0; - return CMD_SUCCESS; -} - -static int smpp_local_tcp(struct vty *vty, - const char *bind_addr, uint16_t port) -{ - struct smsc *smsc = smsc_from_vty(vty); - int is_running = smsc->listen_ofd.fd > 0; - int same_bind_addr; - int rc; - - /* If it is not up yet, don't rebind, just set values. */ - if (!is_running) { - rc = smpp_smsc_conf(smsc, bind_addr, port); - if (rc < 0) { - vty_out(vty, "%% Cannot configure new address:port%s", - VTY_NEWLINE); - return CMD_WARNING; - } - return CMD_SUCCESS; - } - - rc = smpp_smsc_restart(smsc, bind_addr, port); - if (rc < 0) { - vty_out(vty, "%% Cannot bind to new port %s:%u nor to" - " old port %s:%u%s", - bind_addr? bind_addr : "0.0.0.0", - port, - smsc->bind_addr? smsc->bind_addr : "0.0.0.0", - smsc->listen_port, - VTY_NEWLINE); - return CMD_WARNING; - } - - same_bind_addr = (bind_addr == smsc->bind_addr) - || (bind_addr && smsc->bind_addr - && (strcmp(bind_addr, smsc->bind_addr) == 0)); - - if (!same_bind_addr || port != smsc->listen_port) { - vty_out(vty, "%% Cannot bind to new port %s:%u, staying on" - " old port %s:%u%s", - bind_addr? bind_addr : "0.0.0.0", - port, - smsc->bind_addr? smsc->bind_addr : "0.0.0.0", - smsc->listen_port, - VTY_NEWLINE); - return CMD_WARNING; - } - - return CMD_SUCCESS; -} - -DEFUN(cfg_smpp_port, cfg_smpp_port_cmd, - "local-tcp-port <1-65535>", - "Set the local TCP port on which we listen for SMPP\n" - "TCP port number") -{ - struct smsc *smsc = smsc_from_vty(vty); - uint16_t port = atoi(argv[0]); - return smpp_local_tcp(vty, smsc->bind_addr, port); -} - -DEFUN(cfg_smpp_addr_port, cfg_smpp_addr_port_cmd, - "local-tcp-ip A.B.C.D <1-65535>", - "Set the local IP address and TCP port on which we listen for SMPP\n" - "Local IP address\n" - "TCP port number") -{ - const char *bind_addr = argv[0]; - uint16_t port = atoi(argv[1]); - return smpp_local_tcp(vty, bind_addr, port); -} - -DEFUN(cfg_smpp_sys_id, cfg_smpp_sys_id_cmd, - "system-id ID", - "Set the System ID of this SMSC\n" - "Alphanumeric SMSC System ID\n") -{ - struct smsc *smsc = smsc_from_vty(vty); - - if (strlen(argv[0])+1 > sizeof(smsc->system_id)) - return CMD_WARNING; - - strcpy(smsc->system_id, argv[0]); - - return CMD_SUCCESS; -} - -DEFUN(cfg_smpp_policy, cfg_smpp_policy_cmd, - "policy (accept-all|closed)", - "Set the authentication policy of this SMSC\n" - "Accept all SMPP connections independeint of system ID / passwd\n" - "Accept only SMPP connections from ESMEs explicitly configured") -{ - struct smsc *smsc = smsc_from_vty(vty); - - if (!strcmp(argv[0], "accept-all")) - smsc->accept_all = 1; - else - smsc->accept_all = 0; - - return CMD_SUCCESS; -} - - -static int config_write_smpp(struct vty *vty) -{ - struct smsc *smsc = smsc_from_vty(vty); - - vty_out(vty, "smpp%s", VTY_NEWLINE); - if (smsc->bind_addr) - vty_out(vty, " local-tcp-ip %s %u%s", smsc->bind_addr, - smsc->listen_port, VTY_NEWLINE); - else - vty_out(vty, " local-tcp-port %u%s", smsc->listen_port, - VTY_NEWLINE); - if (strlen(smsc->system_id) > 0) - vty_out(vty, " system-id %s%s", smsc->system_id, VTY_NEWLINE); - vty_out(vty, " policy %s%s", - smsc->accept_all ? "accept-all" : "closed", VTY_NEWLINE); - vty_out(vty, " %ssmpp-first%s", - smsc->smpp_first ? "" : "no ", VTY_NEWLINE); - - return CMD_SUCCESS; -} - -DEFUN(cfg_esme, cfg_esme_cmd, - "esme NAME", - "Configure a particular ESME\n" - "Alphanumeric System ID of the ESME to be configured\n") -{ - struct smsc *smsc = smsc_from_vty(vty); - struct osmo_smpp_acl *acl; - const char *id = argv[0]; - - if (strlen(id) > 16) { - vty_out(vty, "%% System ID cannot be more than 16 " - "characters long%s", VTY_NEWLINE); - return CMD_WARNING; - } - acl = smpp_acl_by_system_id(smsc, id); - if (!acl) { - acl = smpp_acl_alloc(smsc, id); - if (!acl) - return CMD_WARNING; - } - - vty->index = acl; - vty->index_sub = &acl->description; - vty->node = SMPP_ESME_NODE; - - return CMD_SUCCESS; -} - -DEFUN(cfg_no_esme, cfg_no_esme_cmd, - "no esme NAME", - NO_STR "Remove ESME configuration\n" - "Alphanumeric System ID of the ESME to be removed\n") -{ - struct smsc *smsc = smsc_from_vty(vty); - struct osmo_smpp_acl *acl; - const char *id = argv[0]; - - acl = smpp_acl_by_system_id(smsc, id); - if (!acl) { - vty_out(vty, "%% ESME with system id '%s' unknown%s", - id, VTY_NEWLINE); - return CMD_WARNING; - } - - /* FIXME: close the connection, free data structure, etc. */ - - smpp_acl_delete(acl); - - return CMD_SUCCESS; -} - - -DEFUN(cfg_esme_passwd, cfg_esme_passwd_cmd, - "password PASSWORD", - "Set the password for this ESME\n" - "Alphanumeric password string\n") -{ - struct osmo_smpp_acl *acl = vty->index; - - if (strlen(argv[0])+1 > sizeof(acl->passwd)) - return CMD_WARNING; - - strcpy(acl->passwd, argv[0]); - - return CMD_SUCCESS; -} - -DEFUN(cfg_esme_no_passwd, cfg_esme_no_passwd_cmd, - "no password", - NO_STR "Remove the password for this ESME\n") -{ - struct osmo_smpp_acl *acl = vty->index; - - memset(acl->passwd, 0, sizeof(acl->passwd)); - - return CMD_SUCCESS; -} - -static int osmo_is_digits(const char *str) -{ - int i; - for (i = 0; i < strlen(str); i++) { - if (!isdigit(str[i])) - return 0; - } - return 1; -} - -static const struct value_string route_errstr[] = { - { -EEXIST, "Route already exists" }, - { -ENODEV, "Route does not exist" }, - { -ENOMEM, "No memory" }, - { -EINVAL, "Invalid" }, - { 0, NULL } -}; - -static const struct value_string smpp_ton_str_short[] = { - { TON_Unknown, "unknown" }, - { TON_International, "international" }, - { TON_National, "national" }, - { TON_Network_Specific, "network" }, - { TON_Subscriber_Number,"subscriber" }, - { TON_Alphanumeric, "alpha" }, - { TON_Abbreviated, "abbrev" }, - { 0, NULL } -}; - -static const struct value_string smpp_npi_str_short[] = { - { NPI_Unknown, "unknown" }, - { NPI_ISDN_E163_E164, "isdn" }, - { NPI_Data_X121, "x121" }, - { NPI_Telex_F69, "f69" }, - { NPI_Land_Mobile_E212, "e212" }, - { NPI_National, "national" }, - { NPI_Private, "private" }, - { NPI_ERMES, "ermes" }, - { NPI_Internet_IP, "ip" }, - { NPI_WAP_Client_Id, "wap" }, - { 0, NULL } -}; - - -#define SMPP_ROUTE_STR "Configure a route for MO-SMS to be sent to this ESME\n" -#define SMPP_ROUTE_P_STR SMPP_ROUTE_STR "Prefix-match route\n" -#define SMPP_PREFIX_STR "Destination number prefix\n" - -#define TON_CMD "(unknown|international|national|network|subscriber|alpha|abbrev)" -#define NPI_CMD "(unknown|isdn|x121|f69|e212|national|private|ermes|ip|wap)" -#define TON_STR "Unknown type-of-number\n" \ - "International type-of-number\n" \ - "National type-of-number\n" \ - "Network specific type-of-number\n" \ - "Subscriber type-of-number\n" \ - "Alphanumeric type-of-number\n" \ - "Abbreviated type-of-number\n" -#define NPI_STR "Unknown numbering plan\n" \ - "ISDN (E.164) numbering plan\n" \ - "X.121 numbering plan\n" \ - "F.69 numbering plan\n" \ - "E.212 numbering plan\n" \ - "National numbering plan\n" \ - "Private numbering plan\n" \ - "ERMES numbering plan\n" \ - "IP numbering plan\n" \ - "WAP numbeing plan\n" - -DEFUN(cfg_esme_route_pfx, cfg_esme_route_pfx_cmd, - "route prefix " TON_CMD " " NPI_CMD " PREFIX", - SMPP_ROUTE_P_STR TON_STR NPI_STR SMPP_PREFIX_STR) -{ - struct osmo_smpp_acl *acl = vty->index; - struct osmo_smpp_addr pfx; - int rc; - - /* check if DESTINATION is all-digits */ - if (!osmo_is_digits(argv[2])) { - vty_out(vty, "%% PREFIX has to be numeric%s", VTY_NEWLINE); - return CMD_WARNING; - } - - pfx.ton = get_string_value(smpp_ton_str_short, argv[0]); - pfx.npi = get_string_value(smpp_npi_str_short, argv[1]); - snprintf(pfx.addr, sizeof(pfx.addr), "%s", argv[2]); - - rc = smpp_route_pfx_add(acl, &pfx); - if (rc < 0) { - vty_out(vty, "%% error adding prefix route: %s%s", - get_value_string(route_errstr, rc), VTY_NEWLINE); - return CMD_WARNING; - } - - return CMD_SUCCESS; -} - -DEFUN(cfg_esme_no_route_pfx, cfg_esme_no_route_pfx_cmd, - "no route prefix " TON_CMD " " NPI_CMD " PREFIX", - NO_STR SMPP_ROUTE_P_STR TON_STR NPI_STR SMPP_PREFIX_STR) -{ - struct osmo_smpp_acl *acl = vty->index; - struct osmo_smpp_addr pfx; - int rc; - - /* check if DESTINATION is all-digits */ - if (!osmo_is_digits(argv[2])) { - vty_out(vty, "%% PREFIX has to be numeric%s", VTY_NEWLINE); - return CMD_WARNING; - } - - pfx.ton = get_string_value(smpp_ton_str_short, argv[0]); - pfx.npi = get_string_value(smpp_npi_str_short, argv[1]); - snprintf(pfx.addr, sizeof(pfx.addr), "%s", argv[2]); - - rc = smpp_route_pfx_del(acl, &pfx); - if (rc < 0) { - vty_out(vty, "%% error removing prefix route: %s%s", - get_value_string(route_errstr, rc), VTY_NEWLINE); - return CMD_WARNING; - } - - return CMD_SUCCESS; - -} - - -DEFUN(cfg_esme_defaultroute, cfg_esme_defaultroute_cmd, - "default-route", - "Set this ESME as default-route for all SMS to unknown destinations") -{ - struct osmo_smpp_acl *acl = vty->index; - - acl->default_route = 1; - - if (!acl->smsc->def_route) - acl->smsc->def_route = acl; - - return CMD_SUCCESS; -} - -DEFUN(cfg_no_esme_defaultroute, cfg_esme_no_defaultroute_cmd, - "no default-route", NO_STR - "Remove this ESME as default-route for all SMS to unknown destinations") -{ - struct osmo_smpp_acl *acl = vty->index; - - acl->default_route = 0; - - /* remove currently active default route, if it was created by - * this ACL */ - if (acl->smsc->def_route && acl->smsc->def_route == acl) - acl->smsc->def_route = NULL; - - return CMD_SUCCESS; -} - -DEFUN(cfg_esme_del_src_imsi, cfg_esme_del_src_imsi_cmd, - "deliver-src-imsi", - "Enable the use of IMSI as source address in DELIVER") -{ - struct osmo_smpp_acl *acl = vty->index; - - acl->deliver_src_imsi = 1; - - return CMD_SUCCESS; -} - -DEFUN(cfg_esme_no_del_src_imsi, cfg_esme_no_del_src_imsi_cmd, - "no deliver-src-imsi", NO_STR - "Disable the use of IMSI as source address in DELIVER") -{ - struct osmo_smpp_acl *acl = vty->index; - - acl->deliver_src_imsi = 0; - - return CMD_SUCCESS; -} - -DEFUN(cfg_esme_osmo_ext, cfg_esme_osmo_ext_cmd, - "osmocom-extensions", - "Enable the use of Osmocom SMPP Extensions for this ESME") -{ - struct osmo_smpp_acl *acl = vty->index; - - acl->osmocom_ext = 1; - - return CMD_SUCCESS; -} - -DEFUN(cfg_esme_no_osmo_ext, cfg_esme_no_osmo_ext_cmd, - "no osmocom-extensions", NO_STR - "Disable the use of Osmocom SMPP Extensions for this ESME") -{ - struct osmo_smpp_acl *acl = vty->index; - - acl->osmocom_ext = 0; - - return CMD_SUCCESS; -} - -DEFUN(cfg_esme_dcs_transp, cfg_esme_dcs_transp_cmd, - "dcs-transparent", - "Enable the transparent pass-through of TP-DCS to SMPP DataCoding") -{ - struct osmo_smpp_acl *acl = vty->index; - - acl->dcs_transparent = 1; - - return CMD_SUCCESS; -} - -DEFUN(cfg_esme_no_dcs_transp, cfg_esme_no_dcs_transp_cmd, - "no dcs-transparent", NO_STR - "Disable the transparent pass-through of TP-DCS to SMPP DataCoding") -{ - struct osmo_smpp_acl *acl = vty->index; - - acl->dcs_transparent = 0; - - return CMD_SUCCESS; -} - - -static void dump_one_esme(struct vty *vty, struct osmo_esme *esme) -{ - char host[128], serv[128]; - - host[0] = 0; - serv[0] = 0; - getnameinfo((const struct sockaddr *) &esme->sa, esme->sa_len, - host, sizeof(host), serv, sizeof(serv), NI_NUMERICSERV); - - vty_out(vty, "ESME System ID: %s, Password: %s, SMPP Version %02x%s", - esme->system_id, esme->acl ? esme->acl->passwd : "", - esme->smpp_version, VTY_NEWLINE); - vty_out(vty, " Connected from: %s:%s%s", host, serv, VTY_NEWLINE); - if (esme->smsc->def_route == esme->acl) - vty_out(vty, " Is current default route%s", VTY_NEWLINE); -} - -DEFUN(show_esme, show_esme_cmd, - "show smpp esme", - SHOW_STR "SMPP Interface\n" "SMPP Extrenal SMS Entity\n") -{ - struct smsc *smsc = smsc_from_vty(vty); - struct osmo_esme *esme; - - llist_for_each_entry(esme, &smsc->esme_list, list) - dump_one_esme(vty, esme); - - return CMD_SUCCESS; -} - -static void write_esme_route_single(struct vty *vty, struct osmo_smpp_route *r) -{ - switch (r->type) { - case SMPP_ROUTE_PREFIX: - vty_out(vty, " route prefix %s ", - get_value_string(smpp_ton_str_short, r->u.prefix.ton)); - vty_out(vty, "%s %s%s", - get_value_string(smpp_npi_str_short, r->u.prefix.npi), - r->u.prefix.addr, VTY_NEWLINE); - break; - case SMPP_ROUTE_NONE: - break; - } -} - -static void config_write_esme_single(struct vty *vty, struct osmo_smpp_acl *acl) -{ - struct osmo_smpp_route *r; - - vty_out(vty, " esme %s%s", acl->system_id, VTY_NEWLINE); - if (strlen(acl->passwd)) - vty_out(vty, " password %s%s", acl->passwd, VTY_NEWLINE); - if (acl->default_route) - vty_out(vty, " default-route%s", VTY_NEWLINE); - if (acl->deliver_src_imsi) - vty_out(vty, " deliver-src-imsi%s", VTY_NEWLINE); - if (acl->osmocom_ext) - vty_out(vty, " osmocom-extensions%s", VTY_NEWLINE); - if (acl->dcs_transparent) - vty_out(vty, " dcs-transparent%s", VTY_NEWLINE); - - llist_for_each_entry(r, &acl->route_list, list) - write_esme_route_single(vty, r); -} - -static int config_write_esme(struct vty *v) -{ - struct smsc *smsc = smsc_from_vty(v); - struct osmo_smpp_acl *acl; - - llist_for_each_entry(acl, &smsc->acl_list, list) - config_write_esme_single(v, acl); - - return CMD_SUCCESS; -} - -int smpp_vty_init(void) -{ - install_node(&smpp_node, config_write_smpp); - vty_install_default(SMPP_NODE); - install_element(CONFIG_NODE, &cfg_smpp_cmd); - - install_element(SMPP_NODE, &cfg_smpp_first_cmd); - install_element(SMPP_NODE, &cfg_no_smpp_first_cmd); - install_element(SMPP_NODE, &cfg_smpp_port_cmd); - install_element(SMPP_NODE, &cfg_smpp_addr_port_cmd); - install_element(SMPP_NODE, &cfg_smpp_sys_id_cmd); - install_element(SMPP_NODE, &cfg_smpp_policy_cmd); - install_element(SMPP_NODE, &cfg_esme_cmd); - install_element(SMPP_NODE, &cfg_no_esme_cmd); - - install_node(&esme_node, config_write_esme); - vty_install_default(SMPP_ESME_NODE); - install_element(SMPP_ESME_NODE, &cfg_esme_passwd_cmd); - install_element(SMPP_ESME_NODE, &cfg_esme_no_passwd_cmd); - install_element(SMPP_ESME_NODE, &cfg_esme_route_pfx_cmd); - install_element(SMPP_ESME_NODE, &cfg_esme_no_route_pfx_cmd); - install_element(SMPP_ESME_NODE, &cfg_esme_defaultroute_cmd); - install_element(SMPP_ESME_NODE, &cfg_esme_no_defaultroute_cmd); - install_element(SMPP_ESME_NODE, &cfg_esme_del_src_imsi_cmd); - install_element(SMPP_ESME_NODE, &cfg_esme_no_del_src_imsi_cmd); - install_element(SMPP_ESME_NODE, &cfg_esme_osmo_ext_cmd); - install_element(SMPP_ESME_NODE, &cfg_esme_no_osmo_ext_cmd); - install_element(SMPP_ESME_NODE, &cfg_esme_dcs_transp_cmd); - install_element(SMPP_ESME_NODE, &cfg_esme_no_dcs_transp_cmd); - - install_element_ve(&show_esme_cmd); - - return 0; -} diff --git a/openbsc/src/libmsc/sms_queue.c b/openbsc/src/libmsc/sms_queue.c deleted file mode 100644 index dc7f6e8c..00000000 --- a/openbsc/src/libmsc/sms_queue.c +++ /dev/null @@ -1,544 +0,0 @@ -/* SMS queue to continously attempt to deliver SMS */ -/* - * (C) 2010 by Holger Hans Peter Freyther - * All Rights Reserved - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation; either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - * - */ - -/** - * The difficulty of such a queue is to send a lot of SMS without - * overloading the paging subsystem and the database and other users - * of the MSC. To make the best use we would need to know the number - * of pending paging requests, then throttle the number of SMS we - * want to send and such. - * We will start with a very simple SMS Queue and then try to speed - * things up by collecting data from other parts of the system. - */ - -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -#include - -/* - * One pending SMS that we wait for. - */ -struct gsm_sms_pending { - struct llist_head entry; - - struct gsm_subscriber *subscr; - unsigned long long sms_id; - int failed_attempts; - int resend; -}; - -struct gsm_sms_queue { - struct osmo_timer_list resend_pending; - struct osmo_timer_list push_queue; - struct gsm_network *network; - int max_fail; - int max_pending; - int pending; - - struct llist_head pending_sms; - unsigned long long last_subscr_id; -}; - -static int sms_subscr_cb(unsigned int, unsigned int, void *, void *); -static int sms_sms_cb(unsigned int, unsigned int, void *, void *); - -static struct gsm_sms_pending *sms_find_pending(struct gsm_sms_queue *smsq, - struct gsm_sms *sms) -{ - struct gsm_sms_pending *pending; - - llist_for_each_entry(pending, &smsq->pending_sms, entry) { - if (pending->sms_id == sms->id) - return pending; - } - - return NULL; -} - -static int sms_is_in_pending(struct gsm_sms_queue *smsq, struct gsm_sms *sms) -{ - return sms_find_pending(smsq, sms) != NULL; -} - -static struct gsm_sms_pending *sms_subscriber_find_pending( - struct gsm_sms_queue *smsq, - struct gsm_subscriber *subscr) -{ - struct gsm_sms_pending *pending; - - llist_for_each_entry(pending, &smsq->pending_sms, entry) { - if (pending->subscr == subscr) - return pending; - } - - return NULL; -} - -static int sms_subscriber_is_pending(struct gsm_sms_queue *smsq, - struct gsm_subscriber *subscr) -{ - return sms_subscriber_find_pending(smsq, subscr) != NULL; -} - -static struct gsm_sms_pending *sms_pending_from(struct gsm_sms_queue *smsq, - struct gsm_sms *sms) -{ - struct gsm_sms_pending *pending; - - pending = talloc_zero(smsq, struct gsm_sms_pending); - if (!pending) - return NULL; - - pending->subscr = subscr_get(sms->receiver); - pending->sms_id = sms->id; - return pending; -} - -static void sms_pending_free(struct gsm_sms_pending *pending) -{ - subscr_put(pending->subscr); - llist_del(&pending->entry); - talloc_free(pending); -} - -static void sms_pending_resend(struct gsm_sms_pending *pending) -{ - struct gsm_sms_queue *smsq; - LOGP(DLSMS, LOGL_DEBUG, - "Scheduling resend of SMS %llu.\n", pending->sms_id); - - pending->resend = 1; - - smsq = pending->subscr->group->net->sms_queue; - if (osmo_timer_pending(&smsq->resend_pending)) - return; - - osmo_timer_schedule(&smsq->resend_pending, 1, 0); -} - -static void sms_pending_failed(struct gsm_sms_pending *pending, int paging_error) -{ - struct gsm_sms_queue *smsq; - - LOGP(DLSMS, LOGL_NOTICE, "Sending SMS %llu failed %d times.\n", - pending->sms_id, pending->failed_attempts); - - smsq = pending->subscr->group->net->sms_queue; - if (++pending->failed_attempts < smsq->max_fail) - return sms_pending_resend(pending); - - sms_pending_free(pending); - smsq->pending -= 1; - sms_queue_trigger(smsq); -} - -/* - * Resend all SMS that are scheduled for a resend. This is done to - * avoid an immediate failure. - */ -static void sms_resend_pending(void *_data) -{ - struct gsm_sms_pending *pending, *tmp; - struct gsm_sms_queue *smsq = _data; - - llist_for_each_entry_safe(pending, tmp, &smsq->pending_sms, entry) { - struct gsm_sms *sms; - if (!pending->resend) - continue; - - sms = db_sms_get(smsq->network, pending->sms_id); - - /* the sms is gone? Move to the next */ - if (!sms) { - sms_pending_free(pending); - smsq->pending -= 1; - sms_queue_trigger(smsq); - } else { - pending->resend = 0; - gsm411_send_sms_subscr(sms->receiver, sms); - } - } -} - -static struct gsm_sms *take_next_sms(struct gsm_sms_queue *smsq) -{ - struct gsm_sms *sms; - - sms = db_sms_get_unsent_by_subscr(smsq->network, smsq->last_subscr_id, 10); - if (sms) { - smsq->last_subscr_id = sms->receiver->id + 1; - return sms; - } - - /* need to wrap around */ - smsq->last_subscr_id = 0; - sms = db_sms_get_unsent_by_subscr(smsq->network, - smsq->last_subscr_id, 10); - if (sms) - smsq->last_subscr_id = sms->receiver->id + 1; - return sms; -} - -/** - * I will submit up to max_pending - pending SMS to the - * subsystem. - */ -static void sms_submit_pending(void *_data) -{ - struct gsm_sms_queue *smsq = _data; - int attempts = smsq->max_pending - smsq->pending; - int initialized = 0; - unsigned long long first_sub = 0; - int attempted = 0, rounds = 0; - - LOGP(DLSMS, LOGL_DEBUG, "Attempting to send %d SMS\n", attempts); - - do { - struct gsm_sms_pending *pending; - struct gsm_sms *sms; - - - sms = take_next_sms(smsq); - if (!sms) { - LOGP(DLSMS, LOGL_DEBUG, "Sending SMS done (%d attempted)\n", - attempted); - break; - } - - rounds += 1; - LOGP(DLSMS, LOGL_DEBUG, "Sending SMS round %d\n", rounds); - - /* - * This code needs to detect a loop. It assumes that no SMS - * will vanish during the time this is executed. We will remember - * the id of the first GSM subscriber we see and then will - * compare this. The Database code should make sure that we will - * see all other subscribers first before seeing this one again. - * - * It is always scary to have an infinite loop like this. - */ - if (!initialized) { - first_sub = sms->receiver->id; - initialized = 1; - } else if (first_sub == sms->receiver->id) { - LOGP(DLSMS, LOGL_DEBUG, "Sending SMS done (loop) (%d attempted)\n", - attempted); - sms_free(sms); - break; - } - - /* no need to send a pending sms */ - if (sms_is_in_pending(smsq, sms)) { - LOGP(DLSMS, LOGL_DEBUG, - "SMSqueue with pending sms: %llu. Skipping\n", sms->id); - sms_free(sms); - continue; - } - - /* no need to send a SMS with the same receiver */ - if (sms_subscriber_is_pending(smsq, sms->receiver)) { - LOGP(DLSMS, LOGL_DEBUG, - "SMSqueue with pending sub: %llu. Skipping\n", sms->receiver->id); - sms_free(sms); - continue; - } - - pending = sms_pending_from(smsq, sms); - if (!pending) { - LOGP(DLSMS, LOGL_ERROR, - "Failed to create pending SMS entry.\n"); - sms_free(sms); - continue; - } - - attempted += 1; - smsq->pending += 1; - llist_add_tail(&pending->entry, &smsq->pending_sms); - gsm411_send_sms_subscr(sms->receiver, sms); - } while (attempted < attempts && rounds < 1000); - - LOGP(DLSMS, LOGL_DEBUG, "SMSqueue added %d messages in %d rounds\n", attempted, rounds); -} - -/** - * Send the next SMS or trigger the queue - */ -static void sms_send_next(struct gsm_subscriber *subscr) -{ - struct gsm_sms_queue *smsq = subscr->group->net->sms_queue; - struct gsm_sms_pending *pending; - struct gsm_sms *sms; - - /* the subscriber should not be in the queue */ - OSMO_ASSERT(!sms_subscriber_is_pending(smsq, subscr)); - - /* check for more messages for this subscriber */ - sms = db_sms_get_unsent_for_subscr(subscr); - if (!sms) - goto no_pending_sms; - - /* No sms should be scheduled right now */ - OSMO_ASSERT(!sms_is_in_pending(smsq, sms)); - - /* Remember that we deliver this SMS and send it */ - pending = sms_pending_from(smsq, sms); - if (!pending) { - LOGP(DLSMS, LOGL_ERROR, - "Failed to create pending SMS entry.\n"); - sms_free(sms); - goto no_pending_sms; - } - - smsq->pending += 1; - llist_add_tail(&pending->entry, &smsq->pending_sms); - gsm411_send_sms_subscr(sms->receiver, sms); - return; - -no_pending_sms: - /* Try to send the SMS to avoid the queue being stuck */ - sms_submit_pending(subscr->group->net->sms_queue); -} - -/* - * Kick off the queue again. - */ -int sms_queue_trigger(struct gsm_sms_queue *smsq) -{ - LOGP(DLSMS, LOGL_DEBUG, "Triggering SMS queue\n"); - if (osmo_timer_pending(&smsq->push_queue)) - return 0; - - osmo_timer_schedule(&smsq->push_queue, 1, 0); - return 0; -} - -int sms_queue_start(struct gsm_network *network, int max_pending) -{ - struct gsm_sms_queue *sms = talloc_zero(network, struct gsm_sms_queue); - if (!sms) { - LOGP(DMSC, LOGL_ERROR, "Failed to create the SMS queue.\n"); - return -1; - } - - osmo_signal_register_handler(SS_SUBSCR, sms_subscr_cb, network); - osmo_signal_register_handler(SS_SMS, sms_sms_cb, network); - - network->sms_queue = sms; - INIT_LLIST_HEAD(&sms->pending_sms); - sms->max_fail = 1; - sms->network = network; - sms->max_pending = max_pending; - osmo_timer_setup(&sms->push_queue, sms_submit_pending, sms); - osmo_timer_setup(&sms->resend_pending, sms_resend_pending, sms); - - sms_submit_pending(sms); - - return 0; -} - -static int sub_ready_for_sm(struct gsm_network *net, struct gsm_subscriber *subscr) -{ - struct gsm_sms *sms; - struct gsm_sms_pending *pending; - struct gsm_subscriber_connection *conn; - - /* - * The code used to be very clever and tried to submit - * a SMS during the Location Updating Request. This has - * two issues: - * 1.) The Phone might not be ready yet, e.g. the C155 - * will not respond to the Submit when it is booting. - * 2.) The queue is already trying to submit SMS to the - * user and by not responding to the paging request - * we will set the LAC back to 0. We would have to - * stop the paging and move things over. - * - * We need to be careful in what we try here. - */ - - /* check if we have pending requests */ - pending = sms_subscriber_find_pending(net->sms_queue, subscr); - if (pending) { - LOGP(DMSC, LOGL_NOTICE, - "Pending paging while subscriber %llu attached.\n", - subscr->id); - return 0; - } - - conn = connection_for_subscr(subscr); - if (!conn) - return -1; - - /* Now try to deliver any pending SMS to this sub */ - sms = db_sms_get_unsent_for_subscr(subscr); - if (!sms) - return -1; - gsm411_send_sms(conn, sms); - return 0; -} - -static int sms_subscr_cb(unsigned int subsys, unsigned int signal, - void *handler_data, void *signal_data) -{ - struct gsm_subscriber *subscr = signal_data; - - if (signal != S_SUBSCR_ATTACHED) - return 0; - - /* this is readyForSM */ - return sub_ready_for_sm(handler_data, subscr); -} - -static int sms_sms_cb(unsigned int subsys, unsigned int signal, - void *handler_data, void *signal_data) -{ - struct gsm_network *network = handler_data; - struct sms_signal_data *sig_sms = signal_data; - struct gsm_sms_pending *pending; - struct gsm_subscriber *subscr; - - /* We got a new SMS and maybe should launch the queue again. */ - if (signal == S_SMS_SUBMITTED || signal == S_SMS_SMMA) { - /* TODO: For SMMA we might want to re-use the radio connection. */ - sms_queue_trigger(network->sms_queue); - return 0; - } - - if (!sig_sms->sms) - return -1; - - - /* - * Find the entry of our queue. The SMS subsystem will submit - * sms that are not in our control as we just have a channel - * open anyway. - */ - pending = sms_find_pending(network->sms_queue, sig_sms->sms); - if (!pending) - return 0; - - switch (signal) { - case S_SMS_DELIVERED: - /* Remember the subscriber and clear the pending entry */ - network->sms_queue->pending -= 1; - subscr = subscr_get(pending->subscr); - sms_pending_free(pending); - /* Attempt to send another SMS to this subscriber */ - sms_send_next(subscr); - subscr_put(subscr); - break; - case S_SMS_MEM_EXCEEDED: - network->sms_queue->pending -= 1; - sms_pending_free(pending); - sms_queue_trigger(network->sms_queue); - break; - case S_SMS_UNKNOWN_ERROR: - /* - * There can be many reasons for this failure. E.g. the paging - * timed out, the subscriber was not paged at all, or there was - * a protocol error. The current strategy is to try sending the - * next SMS for busy/oom and to retransmit when we have paged. - * - * When the paging expires three times we will disable the - * subscriber. If we have some kind of other transmit error we - * should flag the SMS as bad. - */ - switch (sig_sms->paging_result) { - case 0: - /* BAD SMS? */ - db_sms_inc_deliver_attempts(sig_sms->sms); - sms_pending_failed(pending, 0); - break; - case GSM_PAGING_EXPIRED: - sms_pending_failed(pending, 1); - break; - - case GSM_PAGING_OOM: - case GSM_PAGING_BUSY: - network->sms_queue->pending -= 1; - sms_pending_free(pending); - sms_queue_trigger(network->sms_queue); - break; - default: - LOGP(DLSMS, LOGL_ERROR, "Unhandled result: %d\n", - sig_sms->paging_result); - } - break; - default: - LOGP(DLSMS, LOGL_ERROR, "Unhandled result: %d\n", - sig_sms->paging_result); - } - - return 0; -} - -/* VTY helper functions */ -int sms_queue_stats(struct gsm_sms_queue *smsq, struct vty *vty) -{ - struct gsm_sms_pending *pending; - - vty_out(vty, "SMSqueue with max_pending: %d pending: %d%s", - smsq->max_pending, smsq->pending, VTY_NEWLINE); - - llist_for_each_entry(pending, &smsq->pending_sms, entry) - vty_out(vty, " SMS Pending for Subscriber: %llu SMS: %llu Failed: %d.%s", - pending->subscr->id, pending->sms_id, - pending->failed_attempts, VTY_NEWLINE); - return 0; -} - -int sms_queue_set_max_pending(struct gsm_sms_queue *smsq, int max_pending) -{ - LOGP(DLSMS, LOGL_NOTICE, "SMSqueue old max: %d new: %d\n", - smsq->max_pending, max_pending); - smsq->max_pending = max_pending; - return 0; -} - -int sms_queue_set_max_failure(struct gsm_sms_queue *smsq, int max_fail) -{ - LOGP(DLSMS, LOGL_NOTICE, "SMSqueue max failure old: %d new: %d\n", - smsq->max_fail, max_fail); - smsq->max_fail = max_fail; - return 0; -} - -int sms_queue_clear(struct gsm_sms_queue *smsq) -{ - struct gsm_sms_pending *pending, *tmp; - - llist_for_each_entry_safe(pending, tmp, &smsq->pending_sms, entry) { - LOGP(DLSMS, LOGL_NOTICE, - "SMSqueue clearing for sub %llu\n", pending->subscr->id); - sms_pending_free(pending); - } - - smsq->pending = 0; - return 0; -} diff --git a/openbsc/src/libmsc/token_auth.c b/openbsc/src/libmsc/token_auth.c deleted file mode 100644 index 5af1e980..00000000 --- a/openbsc/src/libmsc/token_auth.c +++ /dev/null @@ -1,160 +0,0 @@ -/* SMS based token authentication for ad-hoc GSM networks */ - -/* (C) 2009 by Harald Welte - * - * All Rights Reserved - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation; either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - * - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#define TOKEN_SMS_TEXT "HAR 2009 GSM. Register at http://har2009.gnumonks.org/ " \ - "Your IMSI is %s, auth token is %08X, phone no is %s." - -static char *build_sms_string(struct gsm_subscriber *subscr, uint32_t token) -{ - char *sms_str; - unsigned int len; - - len = strlen(subscr->imsi) + 8 + strlen(TOKEN_SMS_TEXT); - sms_str = talloc_size(tall_bsc_ctx, len); - if (!sms_str) - return NULL; - - snprintf(sms_str, len, TOKEN_SMS_TEXT, subscr->imsi, token, - subscr->extension); - sms_str[len-1] = '\0'; - - return sms_str; -} - -static int token_subscr_cb(unsigned int subsys, unsigned int signal, - void *handler_data, void *signal_data) -{ - struct gsm_subscriber *subscr = signal_data; - struct gsm_sms *sms; - int rc = 0; - - if (signal != S_SUBSCR_ATTACHED) - return 0; - - if (subscr->group->net->auth_policy != GSM_AUTH_POLICY_TOKEN) - return 0; - - if (subscr->flags & GSM_SUBSCRIBER_FIRST_CONTACT) { - struct gsm_subscriber *sender; - uint32_t token; - char *sms_str; - - /* we've seen this subscriber for the first time. */ - rc = db_subscriber_alloc_token(subscr, &token); - if (rc != 0) { - rc = -EIO; - goto unauth; - } - - sms_str = build_sms_string(subscr, token); - if (!sms_str) { - rc = -ENOMEM; - goto unauth; - } - - - /* FIXME: don't use ID 1 static */ - sender = subscr_get_by_id(subscr->group, 1); - - sms = sms_from_text(subscr, sender, 0, sms_str); - - subscr_put(sender); - talloc_free(sms_str); - if (!sms) { - rc = -ENOMEM; - goto unauth; - } - - rc = gsm411_send_sms_subscr(subscr, sms); - - /* FIXME: else, delete the subscirber from database */ -unauth: - - /* make sure we don't allow him in again unless he clicks the web UI */ - subscr->authorized = 0; - db_sync_subscriber(subscr); - if (rc) { - struct gsm_subscriber_connection *conn = connection_for_subscr(subscr); - if (conn) { - uint8_t auth_rand[16]; - /* kick the subscriber off the network */ - gsm48_tx_mm_auth_req(conn, auth_rand, NULL, 0); - gsm48_tx_mm_auth_rej(conn); - /* FIXME: close the channel early ?*/ - //gsm48_send_rr_Release(lchan); - } - } - } - - return rc; -} - -static int token_sms_cb(unsigned int subsys, unsigned int signal, - void *handler_data, void *signal_data) -{ - struct sms_signal_data *sig = signal_data; - struct gsm_sms *sms = sig->sms;; - struct gsm_subscriber_connection *conn; - uint8_t auth_rand[16]; - - - if (signal != S_SMS_DELIVERED) - return 0; - - - /* these are not the droids we've been looking for */ - if (!sms->receiver || - !(sms->receiver->flags & GSM_SUBSCRIBER_FIRST_CONTACT)) - return 0; - - - if (sms->receiver->group->net->auth_policy != GSM_AUTH_POLICY_TOKEN) - return 0; - - - conn = connection_for_subscr(sms->receiver); - if (conn) { - /* kick the subscriber off the network */ - gsm48_tx_mm_auth_req(conn, auth_rand, NULL, 0); - gsm48_tx_mm_auth_rej(conn); - /* FIXME: close the channel early ?*/ - //gsm48_send_rr_Release(lchan); - } - - return 0; -} - -//static __attribute__((constructor)) void on_dso_load_token(void) -void on_dso_load_token(void) -{ - osmo_signal_register_handler(SS_SUBSCR, token_subscr_cb, NULL); - osmo_signal_register_handler(SS_SMS, token_sms_cb, NULL); -} diff --git a/openbsc/src/libmsc/transaction.c b/openbsc/src/libmsc/transaction.c deleted file mode 100644 index dba4bed1..00000000 --- a/openbsc/src/libmsc/transaction.c +++ /dev/null @@ -1,163 +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_connection *conn, - uint8_t proto, uint8_t trans_id) -{ - struct gsm_trans *trans; - struct gsm_network *net = conn->network; - struct gsm_subscriber *subscr = conn->subscr; - - llist_for_each_entry(trans, &net->trans_list, entry) { - if (trans->subscr == subscr && - trans->protocol == proto && - trans->transaction_id == trans_id) - return trans; - } - return NULL; -} - -struct gsm_trans *trans_find_by_callref(struct gsm_network *net, - uint32_t callref) -{ - struct gsm_trans *trans; - - llist_for_each_entry(trans, &net->trans_list, entry) { - if (trans->callref == callref) - return trans; - } - return NULL; -} - -struct gsm_trans *trans_alloc(struct gsm_network *net, - struct gsm_subscriber *subscr, - uint8_t protocol, uint8_t trans_id, - uint32_t callref) -{ - struct gsm_trans *trans; - - DEBUGP(DCC, "subscr=%p, net=%p\n", subscr, net); - - trans = talloc_zero(tall_trans_ctx, struct gsm_trans); - if (!trans) - return NULL; - - trans->subscr = subscr; - subscr_get(trans->subscr); - - trans->protocol = protocol; - trans->transaction_id = trans_id; - trans->callref = callref; - - trans->net = net; - llist_add_tail(&trans->entry, &net->trans_list); - - return trans; -} - -void trans_free(struct gsm_trans *trans) -{ - switch (trans->protocol) { - case GSM48_PDISC_CC: - _gsm48_cc_trans_free(trans); - break; - case GSM48_PDISC_SMS: - _gsm411_sms_trans_free(trans); - break; - } - - if (trans->paging_request) { - subscr_remove_request(trans->paging_request); - trans->paging_request = NULL; - } - - if (trans->subscr) { - subscr_put(trans->subscr); - trans->subscr = NULL; - } - - llist_del(&trans->entry); - - if (trans->conn) - msc_release_connection(trans->conn); - - trans->conn = NULL; - talloc_free(trans); -} - -/* allocate an unused transaction ID for the given subscriber - * in the given protocol using the ti_flag specified */ -int trans_assign_trans_id(struct gsm_network *net, struct gsm_subscriber *subscr, - uint8_t protocol, uint8_t ti_flag) -{ - struct gsm_trans *trans; - unsigned int used_tid_bitmask = 0; - int i, j, h; - - if (ti_flag) - ti_flag = 0x8; - - /* generate bitmask of already-used TIDs for this (subscr,proto) */ - llist_for_each_entry(trans, &net->trans_list, entry) { - if (trans->subscr != subscr || - trans->protocol != protocol || - trans->transaction_id == 0xff) - continue; - used_tid_bitmask |= (1 << trans->transaction_id); - } - - /* find a new one, trying to go in a 'circular' pattern */ - for (h = 6; h > 0; h--) - if (used_tid_bitmask & (1 << (h | ti_flag))) - break; - for (i = 0; i < 7; i++) { - j = ((h + i) % 7) | ti_flag; - if ((used_tid_bitmask & (1 << j)) == 0) - return j; - } - - return -1; -} - -int trans_has_conn(const struct gsm_subscriber_connection *conn) -{ - struct gsm_trans *trans; - - llist_for_each_entry(trans, &conn->network->trans_list, entry) - if (trans->conn == conn) - return 1; - - return 0; -} diff --git a/openbsc/src/libmsc/ussd.c b/openbsc/src/libmsc/ussd.c deleted file mode 100644 index f12c1f28..00000000 --- a/openbsc/src/libmsc/ussd.c +++ /dev/null @@ -1,95 +0,0 @@ -/* Network-specific handling of mobile-originated USSDs. */ - -/* (C) 2008-2009 by Harald Welte - * (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 ss_request *req); - - -/* Entrypoint - handler function common to all mobile-originated USSDs */ -int handle_rcv_ussd(struct gsm_subscriber_connection *conn, struct msgb *msg) -{ - int rc; - struct ss_request req; - struct gsm48_hdr *gh; - - memset(&req, 0, sizeof(req)); - gh = msgb_l3(msg); - rc = gsm0480_decode_ss_request(gh, msgb_l3len(msg), &req); - if (!rc) { - DEBUGP(DMM, "Unhandled SS\n"); - rc = gsm0480_send_ussd_reject(conn, msg, &req); - msc_release_connection(conn); - return rc; - } - - /* Interrogation or releaseComplete? */ - if (req.ussd_text[0] == '\0' || req.ussd_text[0] == 0xFF) { - if (req.ss_code > 0) { - /* Assume interrogateSS or modification of it and reject */ - rc = gsm0480_send_ussd_reject(conn, msg, &req); - msc_release_connection(conn); - return rc; - } - /* Still assuming a Release-Complete and returning */ - return 0; - } - - if (!strcmp(USSD_TEXT_OWN_NUMBER, (const char *)req.ussd_text)) { - DEBUGP(DMM, "USSD: Own number requested\n"); - rc = send_own_number(conn, msg, &req); - } else { - DEBUGP(DMM, "Unhandled USSD %s\n", req.ussd_text); - rc = gsm0480_send_ussd_reject(conn, msg, &req); - } - - /* check if we can release it */ - msc_release_connection(conn); - return rc; -} - -/* A network-specific handler function */ -static int send_own_number(struct gsm_subscriber_connection *conn, const struct msgb *msg, const struct ss_request *req) -{ - char *own_number = conn->subscr->extension; - char response_string[GSM_EXTENSION_LENGTH + 20]; - - /* Need trailing CR as EOT character */ - snprintf(response_string, sizeof(response_string), "Your extension is %s\r", own_number); - return gsm0480_send_ussd_response(conn, msg, response_string, req); -} diff --git a/openbsc/src/libmsc/vty_interface_layer3.c b/openbsc/src/libmsc/vty_interface_layer3.c deleted file mode 100644 index e5032910..00000000 --- a/openbsc/src/libmsc/vty_interface_layer3.c +++ /dev/null @@ -1,1210 +0,0 @@ -/* OpenBSC interface to quagga VTY */ -/* (C) 2009 by Harald Welte - * (C) 2009-2011 by Holger Hans Peter Freyther - * All Rights Reserved - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation; either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - * - */ - -#include -#include -#include -#include -#include -#include - -#include -#include -#include - -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -#include "meas_feed.h" - -extern struct gsm_network *gsmnet_from_vty(struct vty *v); - -static void subscr_dump_full_vty(struct vty *vty, struct gsm_subscriber *subscr) -{ - int rc; - int reqs; - struct gsm_auth_info ainfo; - struct gsm_auth_tuple atuple; - struct llist_head *entry; - char expire_time[200]; - - vty_out(vty, " ID: %llu, Authorized: %d%s", subscr->id, - subscr->authorized, VTY_NEWLINE); - if (strlen(subscr->name)) - vty_out(vty, " Name: '%s'%s", subscr->name, VTY_NEWLINE); - if (strlen(subscr->extension)) - vty_out(vty, " Extension: %s%s", subscr->extension, - VTY_NEWLINE); - vty_out(vty, " LAC: %d/0x%x%s", - subscr->lac, subscr->lac, VTY_NEWLINE); - vty_out(vty, " IMSI: %s%s", subscr->imsi, VTY_NEWLINE); - if (subscr->tmsi != GSM_RESERVED_TMSI) - vty_out(vty, " TMSI: %08X%s", subscr->tmsi, - VTY_NEWLINE); - - rc = db_get_authinfo_for_subscr(&ainfo, subscr); - if (!rc) { - vty_out(vty, " A3A8 algorithm id: %d%s", - ainfo.auth_algo, VTY_NEWLINE); - vty_out(vty, " A3A8 Ki: %s%s", - osmo_hexdump(ainfo.a3a8_ki, ainfo.a3a8_ki_len), - VTY_NEWLINE); - } - - rc = db_get_lastauthtuple_for_subscr(&atuple, subscr); - if (!rc) { - vty_out(vty, " A3A8 last tuple (used %d times):%s", - atuple.use_count, VTY_NEWLINE); - vty_out(vty, " seq # : %d%s", - atuple.key_seq, VTY_NEWLINE); - vty_out(vty, " RAND : %s%s", - osmo_hexdump(atuple.vec.rand, sizeof(atuple.vec.rand)), - VTY_NEWLINE); - vty_out(vty, " SRES : %s%s", - osmo_hexdump(atuple.vec.sres, sizeof(atuple.vec.sres)), - VTY_NEWLINE); - vty_out(vty, " Kc : %s%s", - osmo_hexdump(atuple.vec.kc, sizeof(atuple.vec.kc)), - VTY_NEWLINE); - } - - /* print the expiration time of a subscriber */ - strftime(expire_time, sizeof(expire_time), - "%a, %d %b %Y %T %z", localtime(&subscr->expire_lu)); - expire_time[sizeof(expire_time) - 1] = '\0'; - vty_out(vty, " Expiration Time: %s%s", expire_time, VTY_NEWLINE); - - reqs = 0; - llist_for_each(entry, &subscr->requests) - reqs += 1; - vty_out(vty, " Paging: %s paging Requests: %d%s", - subscr->is_paging ? "is" : "not", reqs, VTY_NEWLINE); - vty_out(vty, " Use count: %u%s", subscr->use_count, VTY_NEWLINE); -} - - -/* Subscriber */ -DEFUN(show_subscr_cache, - show_subscr_cache_cmd, - "show subscriber cache", - SHOW_STR "Show information about subscribers\n" - "Display contents of subscriber cache\n") -{ - struct gsm_subscriber *subscr; - - llist_for_each_entry(subscr, &active_subscribers, entry) { - vty_out(vty, " Subscriber:%s", VTY_NEWLINE); - subscr_dump_full_vty(vty, subscr); - } - - return CMD_SUCCESS; -} - -DEFUN(sms_send_pend, - sms_send_pend_cmd, - "sms send pending", - "SMS related commands\n" "SMS Sending related commands\n" - "Send all pending SMS") -{ - struct gsm_network *gsmnet = gsmnet_from_vty(vty); - struct gsm_sms *sms; - int id = 0; - - while (1) { - sms = db_sms_get_unsent_by_subscr(gsmnet, id, UINT_MAX); - if (!sms) - break; - - gsm411_send_sms_subscr(sms->receiver, sms); - - id = sms->receiver->id + 1; - } - - return CMD_SUCCESS; -} - -static int _send_sms_str(struct gsm_subscriber *receiver, - struct gsm_subscriber *sender, - char *str, uint8_t tp_pid) -{ - struct gsm_sms *sms; - - sms = sms_from_text(receiver, sender, 0, str); - sms->protocol_id = tp_pid; - - /* store in database for the queue */ - if (db_sms_store(sms) != 0) { - LOGP(DLSMS, LOGL_ERROR, "Failed to store SMS in Database\n"); - sms_free(sms); - return CMD_WARNING; - } - LOGP(DLSMS, LOGL_DEBUG, "SMS stored in DB\n"); - - sms_free(sms); - sms_queue_trigger(receiver->group->net->sms_queue); - return CMD_SUCCESS; -} - -static struct gsm_subscriber *get_subscr_by_argv(struct gsm_network *gsmnet, - const char *type, - const char *id) -{ - if (!strcmp(type, "extension")) - return subscr_get_by_extension(gsmnet->subscr_group, id); - else if (!strcmp(type, "imsi")) - return subscr_get_by_imsi(gsmnet->subscr_group, id); - else if (!strcmp(type, "tmsi")) - return subscr_get_by_tmsi(gsmnet->subscr_group, atoi(id)); - else if (!strcmp(type, "id")) - return subscr_get_by_id(gsmnet->subscr_group, atoi(id)); - - return NULL; -} -#define SUBSCR_TYPES "(extension|imsi|tmsi|id)" -#define SUBSCR_HELP "Operations on a Subscriber\n" \ - "Identify subscriber by extension (phone number)\n" \ - "Identify subscriber by IMSI\n" \ - "Identify subscriber by TMSI\n" \ - "Identify subscriber by database ID\n" \ - "Identifier for the subscriber\n" - -DEFUN(show_subscr, - show_subscr_cmd, - "show subscriber " SUBSCR_TYPES " ID", - SHOW_STR SUBSCR_HELP) -{ - struct gsm_network *gsmnet = gsmnet_from_vty(vty); - struct gsm_subscriber *subscr = - get_subscr_by_argv(gsmnet, argv[0], argv[1]); - - if (!subscr) { - vty_out(vty, "%% No subscriber found for %s %s%s", - argv[0], argv[1], VTY_NEWLINE); - return CMD_WARNING; - } - - subscr_dump_full_vty(vty, subscr); - - subscr_put(subscr); - - return CMD_SUCCESS; -} - -DEFUN(subscriber_create, - subscriber_create_cmd, - "subscriber create imsi ID", - "Operations on a Subscriber\n" \ - "Create new subscriber\n" \ - "Identify the subscriber by his IMSI\n" \ - "Identifier for the subscriber\n") -{ - struct gsm_network *gsmnet = gsmnet_from_vty(vty); - struct gsm_subscriber *subscr; - - subscr = subscr_get_by_imsi(gsmnet->subscr_group, argv[0]); - if (subscr) - db_sync_subscriber(subscr); - else { - subscr = subscr_create_subscriber(gsmnet->subscr_group, argv[0]); - - if (!subscr) { - vty_out(vty, "%% No subscriber created for IMSI %s%s", - argv[0], VTY_NEWLINE); - return CMD_WARNING; - } - } - - /* Show info about the created subscriber. */ - subscr_dump_full_vty(vty, subscr); - - subscr_put(subscr); - - return CMD_SUCCESS; -} - -DEFUN(subscriber_send_pending_sms, - subscriber_send_pending_sms_cmd, - "subscriber " SUBSCR_TYPES " ID sms pending-send", - SUBSCR_HELP "SMS Operations\n" "Send pending SMS\n") -{ - struct gsm_network *gsmnet = gsmnet_from_vty(vty); - struct gsm_subscriber *subscr; - struct gsm_sms *sms; - - subscr = get_subscr_by_argv(gsmnet, argv[0], argv[1]); - if (!subscr) { - vty_out(vty, "%% No subscriber found for %s %s%s", - argv[0], argv[1], VTY_NEWLINE); - return CMD_WARNING; - } - - sms = db_sms_get_unsent_by_subscr(gsmnet, subscr->id, UINT_MAX); - if (sms) - gsm411_send_sms_subscr(sms->receiver, sms); - - subscr_put(subscr); - - return CMD_SUCCESS; -} - -DEFUN(subscriber_send_sms, - subscriber_send_sms_cmd, - "subscriber " SUBSCR_TYPES " ID sms sender " SUBSCR_TYPES " SENDER_ID send .LINE", - SUBSCR_HELP "SMS Operations\n" SUBSCR_HELP "Send SMS\n" "Actual SMS Text\n") -{ - struct gsm_network *gsmnet = gsmnet_from_vty(vty); - struct gsm_subscriber *subscr = get_subscr_by_argv(gsmnet, argv[0], argv[1]); - struct gsm_subscriber *sender = get_subscr_by_argv(gsmnet, argv[2], argv[3]); - char *str; - int rc; - - if (!subscr) { - vty_out(vty, "%% No subscriber found for %s %s%s", - argv[0], argv[1], VTY_NEWLINE); - rc = CMD_WARNING; - goto err; - } - - if (!sender) { - vty_out(vty, "%% No sender found for %s %s%s", - argv[2], argv[3], VTY_NEWLINE); - rc = CMD_WARNING; - goto err; - } - - str = argv_concat(argv, argc, 4); - rc = _send_sms_str(subscr, sender, str, 0); - talloc_free(str); - -err: - if (sender) - subscr_put(sender); - - if (subscr) - subscr_put(subscr); - - return rc; -} - -DEFUN(subscriber_silent_sms, - subscriber_silent_sms_cmd, - - "subscriber " SUBSCR_TYPES " ID silent-sms sender " SUBSCR_TYPES " SENDER_ID send .LINE", - SUBSCR_HELP "Silent SMS Operations\n" SUBSCR_HELP "Send SMS\n" "Actual SMS Text\n") -{ - struct gsm_network *gsmnet = gsmnet_from_vty(vty); - struct gsm_subscriber *subscr = get_subscr_by_argv(gsmnet, argv[0], argv[1]); - struct gsm_subscriber *sender = get_subscr_by_argv(gsmnet, argv[2], argv[3]); - char *str; - int rc; - - if (!subscr) { - vty_out(vty, "%% No subscriber found for %s %s%s", - argv[0], argv[1], VTY_NEWLINE); - rc = CMD_WARNING; - goto err; - } - - if (!sender) { - vty_out(vty, "%% No sender found for %s %s%s", - argv[2], argv[3], VTY_NEWLINE); - rc = CMD_WARNING; - goto err; - } - - str = argv_concat(argv, argc, 4); - rc = _send_sms_str(subscr, sender, str, 64); - talloc_free(str); - -err: - if (sender) - subscr_put(sender); - - if (subscr) - subscr_put(subscr); - - return rc; -} - -#define CHAN_TYPES "(any|tch/f|tch/any|sdcch)" -#define CHAN_TYPE_HELP \ - "Any channel\n" \ - "TCH/F channel\n" \ - "Any TCH channel\n" \ - "SDCCH channel\n" - -DEFUN(subscriber_silent_call_start, - subscriber_silent_call_start_cmd, - "subscriber " SUBSCR_TYPES " ID silent-call start (any|tch/f|tch/any|sdcch)", - SUBSCR_HELP "Silent call operation\n" "Start silent call\n" - CHAN_TYPE_HELP) -{ - struct gsm_network *gsmnet = gsmnet_from_vty(vty); - struct gsm_subscriber *subscr = get_subscr_by_argv(gsmnet, argv[0], argv[1]); - int rc, type; - - if (!subscr) { - vty_out(vty, "%% No subscriber found for %s %s%s", - argv[0], argv[1], VTY_NEWLINE); - return CMD_WARNING; - } - - if (!strcmp(argv[2], "tch/f")) - type = RSL_CHANNEED_TCH_F; - else if (!strcmp(argv[2], "tch/any")) - type = RSL_CHANNEED_TCH_ForH; - else if (!strcmp(argv[2], "sdcch")) - type = RSL_CHANNEED_SDCCH; - else - type = RSL_CHANNEED_ANY; /* Defaults to ANY */ - - rc = gsm_silent_call_start(subscr, vty, type); - if (rc <= 0) { - vty_out(vty, "%% Subscriber not attached%s", - VTY_NEWLINE); - subscr_put(subscr); - return CMD_WARNING; - } - - subscr_put(subscr); - - return CMD_SUCCESS; -} - -DEFUN(subscriber_silent_call_stop, - subscriber_silent_call_stop_cmd, - "subscriber " SUBSCR_TYPES " ID silent-call stop", - SUBSCR_HELP "Silent call operation\n" "Stop silent call\n" - CHAN_TYPE_HELP) -{ - struct gsm_network *gsmnet = gsmnet_from_vty(vty); - struct gsm_subscriber *subscr = get_subscr_by_argv(gsmnet, argv[0], argv[1]); - int rc; - - if (!subscr) { - vty_out(vty, "%% No subscriber found for %s %s%s", - argv[0], argv[1], VTY_NEWLINE); - return CMD_WARNING; - } - - rc = gsm_silent_call_stop(subscr); - if (rc < 0) { - subscr_put(subscr); - return CMD_WARNING; - } - - subscr_put(subscr); - - return CMD_SUCCESS; -} - -DEFUN(subscriber_ussd_notify, - subscriber_ussd_notify_cmd, - "subscriber " SUBSCR_TYPES " ID ussd-notify (0|1|2) .TEXT", - SUBSCR_HELP "Send a USSD notify to the subscriber\n" - "Alerting Level 0\n" - "Alerting Level 1\n" - "Alerting Level 2\n" - "Text of USSD message to send\n") -{ - char *text; - struct gsm_subscriber_connection *conn; - struct gsm_network *gsmnet = gsmnet_from_vty(vty); - struct gsm_subscriber *subscr = get_subscr_by_argv(gsmnet, argv[0], argv[1]); - int level; - - if (!subscr) { - vty_out(vty, "%% No subscriber found for %s %s%s", - argv[0], argv[1], VTY_NEWLINE); - return CMD_WARNING; - } - - level = atoi(argv[2]); - text = argv_concat(argv, argc, 3); - if (!text) { - subscr_put(subscr); - return CMD_WARNING; - } - - conn = connection_for_subscr(subscr); - if (!conn) { - vty_out(vty, "%% An active connection is required for %s %s%s", - argv[0], argv[1], VTY_NEWLINE); - subscr_put(subscr); - talloc_free(text); - return CMD_WARNING; - } - - msc_send_ussd_notify(conn, level, text); - msc_send_ussd_release_complete(conn); - - subscr_put(subscr); - talloc_free(text); - return CMD_SUCCESS; -} - -DEFUN(ena_subscr_delete, - ena_subscr_delete_cmd, - "subscriber " SUBSCR_TYPES " ID delete", - SUBSCR_HELP "Delete subscriber in HLR\n") -{ - int rc; - struct gsm_network *gsmnet = gsmnet_from_vty(vty); - struct gsm_subscriber *subscr = - get_subscr_by_argv(gsmnet, argv[0], argv[1]); - - if (!subscr) { - vty_out(vty, "%% No subscriber found for %s %s%s", - argv[0], argv[1], VTY_NEWLINE); - return CMD_WARNING; - } - - if (subscr->use_count != 1) { - vty_out(vty, "Removing active subscriber%s", VTY_NEWLINE); - } - - rc = db_subscriber_delete(subscr); - subscr_put(subscr); - - if (rc != 0) { - vty_out(vty, "Failed to remove subscriber%s", VTY_NEWLINE); - return CMD_WARNING; - } - - return CMD_SUCCESS; -} - -DEFUN(ena_subscr_expire, - ena_subscr_expire_cmd, - "subscriber " SUBSCR_TYPES " ID expire", - SUBSCR_HELP "Expire the subscriber Now\n") -{ - struct gsm_network *gsmnet = gsmnet_from_vty(vty); - struct gsm_subscriber *subscr = - get_subscr_by_argv(gsmnet, argv[0], argv[1]); - - if (!subscr) { - vty_out(vty, "%% No subscriber found for %s %s%s", - argv[0], argv[1], VTY_NEWLINE); - return CMD_WARNING; - } - - subscr->expire_lu = time(0); - db_sync_subscriber(subscr); - subscr_put(subscr); - - return CMD_SUCCESS; -} - -DEFUN(ena_subscr_authorized, - ena_subscr_authorized_cmd, - "subscriber " SUBSCR_TYPES " ID authorized (0|1)", - SUBSCR_HELP "(De-)Authorize subscriber in HLR\n" - "Subscriber should NOT be authorized\n" - "Subscriber should be authorized\n") -{ - struct gsm_network *gsmnet = gsmnet_from_vty(vty); - struct gsm_subscriber *subscr = - get_subscr_by_argv(gsmnet, argv[0], argv[1]); - - if (!subscr) { - vty_out(vty, "%% No subscriber found for %s %s%s", - argv[0], argv[1], VTY_NEWLINE); - return CMD_WARNING; - } - - subscr->authorized = atoi(argv[2]); - db_sync_subscriber(subscr); - - subscr_put(subscr); - - return CMD_SUCCESS; -} - -DEFUN(ena_subscr_name, - ena_subscr_name_cmd, - "subscriber " SUBSCR_TYPES " ID name .NAME", - SUBSCR_HELP "Set the name of the subscriber\n" - "Name of the Subscriber\n") -{ - struct gsm_network *gsmnet = gsmnet_from_vty(vty); - struct gsm_subscriber *subscr = - get_subscr_by_argv(gsmnet, argv[0], argv[1]); - char *name; - - if (!subscr) { - vty_out(vty, "%% No subscriber found for %s %s%s", - argv[0], argv[1], VTY_NEWLINE); - return CMD_WARNING; - } - - name = argv_concat(argv, argc, 2); - if (!name) { - subscr_put(subscr); - return CMD_WARNING; - } - - if (strlen(name) > sizeof(subscr->name)-1) { - vty_out(vty, - "%% NAME is too long, max. %zu characters are allowed%s", - sizeof(subscr->name)-1, VTY_NEWLINE); - subscr_put(subscr); - return CMD_WARNING; - } - - osmo_strlcpy(subscr->name, name, sizeof(subscr->name)); - talloc_free(name); - db_sync_subscriber(subscr); - - subscr_put(subscr); - - return CMD_SUCCESS; -} - -DEFUN(ena_subscr_extension, - ena_subscr_extension_cmd, - "subscriber " SUBSCR_TYPES " ID extension EXTENSION", - SUBSCR_HELP "Set the extension (phone number) of the subscriber\n" - "Extension (phone number)\n") -{ - struct gsm_network *gsmnet = gsmnet_from_vty(vty); - struct gsm_subscriber *subscr = - get_subscr_by_argv(gsmnet, argv[0], argv[1]); - const char *ext = argv[2]; - - if (!subscr) { - vty_out(vty, "%% No subscriber found for %s %s%s", - argv[0], argv[1], VTY_NEWLINE); - return CMD_WARNING; - } - - if (strlen(ext) > sizeof(subscr->extension)-1) { - vty_out(vty, - "%% EXTENSION is too long, max. %zu characters are allowed%s", - sizeof(subscr->extension)-1, VTY_NEWLINE); - return CMD_WARNING; - } - - osmo_strlcpy(subscr->extension, ext, sizeof(subscr->extension)); - db_sync_subscriber(subscr); - - subscr_put(subscr); - - return CMD_SUCCESS; -} - -DEFUN(ena_subscr_handover, - ena_subscr_handover_cmd, - "subscriber " SUBSCR_TYPES " ID handover BTS_NR", - SUBSCR_HELP "Handover the active connection\n" - "Number of the BTS to handover to\n") -{ - int ret; - struct gsm_subscriber_connection *conn; - struct gsm_bts *bts; - struct gsm_network *gsmnet = gsmnet_from_vty(vty); - struct gsm_subscriber *subscr = - get_subscr_by_argv(gsmnet, argv[0], argv[1]); - - if (!subscr) { - vty_out(vty, "%% No subscriber found for %s %s.%s", - argv[0], argv[1], VTY_NEWLINE); - return CMD_WARNING; - } - - conn = connection_for_subscr(subscr); - if (!conn) { - vty_out(vty, "%% No active connection for subscriber %s %s.%s", - argv[0], argv[1], VTY_NEWLINE); - subscr_put(subscr); - return CMD_WARNING; - } - - bts = gsm_bts_num(gsmnet, atoi(argv[2])); - if (!bts) { - vty_out(vty, "%% BTS with number(%d) could not be found.%s", - atoi(argv[2]), VTY_NEWLINE); - subscr_put(subscr); - return CMD_WARNING; - } - - /* now start the handover */ - ret = bsc_handover_start(conn->lchan, bts); - if (ret != 0) { - vty_out(vty, "%% Handover failed with errno %d.%s", - ret, VTY_NEWLINE); - } else { - vty_out(vty, "%% Handover started from %s", - gsm_lchan_name(conn->lchan)); - vty_out(vty, " to %s.%s", gsm_lchan_name(conn->ho_lchan), - VTY_NEWLINE); - } - - subscr_put(subscr); - return CMD_SUCCESS; -} - -#define A3A8_ALG_TYPES "(none|xor|comp128v1)" -#define A3A8_ALG_HELP \ - "Use No A3A8 algorithm\n" \ - "Use XOR algorithm\n" \ - "Use COMP128v1 algorithm\n" - -DEFUN(ena_subscr_a3a8, - ena_subscr_a3a8_cmd, - "subscriber " SUBSCR_TYPES " ID a3a8 " A3A8_ALG_TYPES " [KI]", - SUBSCR_HELP "Set a3a8 parameters for the subscriber\n" - A3A8_ALG_HELP "Encryption Key Ki\n") -{ - struct gsm_network *gsmnet = gsmnet_from_vty(vty); - struct gsm_subscriber *subscr = - get_subscr_by_argv(gsmnet, argv[0], argv[1]); - const char *alg_str = argv[2]; - const char *ki_str = argc == 4 ? argv[3] : NULL; - struct gsm_auth_info ainfo; - int rc, minlen, maxlen; - - if (!subscr) { - vty_out(vty, "%% No subscriber found for %s %s%s", - argv[0], argv[1], VTY_NEWLINE); - return CMD_WARNING; - } - - if (!strcasecmp(alg_str, "none")) { - ainfo.auth_algo = AUTH_ALGO_NONE; - minlen = maxlen = 0; - } else if (!strcasecmp(alg_str, "xor")) { - ainfo.auth_algo = AUTH_ALGO_XOR; - minlen = A38_XOR_MIN_KEY_LEN; - maxlen = A38_XOR_MAX_KEY_LEN; - } else if (!strcasecmp(alg_str, "comp128v1")) { - ainfo.auth_algo = AUTH_ALGO_COMP128v1; - minlen = maxlen = A38_COMP128_KEY_LEN; - } else { - /* Unknown method */ - subscr_put(subscr); - vty_out(vty, "%% Unknown auth method %s%s", - alg_str, VTY_NEWLINE); - return CMD_WARNING; - } - - if (ki_str) { - rc = osmo_hexparse(ki_str, ainfo.a3a8_ki, sizeof(ainfo.a3a8_ki)); - if ((rc > maxlen) || (rc < minlen)) { - subscr_put(subscr); - vty_out(vty, "%% Wrong Ki `%s'%s", - ki_str, VTY_NEWLINE); - return CMD_WARNING; - } - ainfo.a3a8_ki_len = rc; - } else { - ainfo.a3a8_ki_len = 0; - if (minlen) { - subscr_put(subscr); - vty_out(vty, "%% Missing Ki argument%s", VTY_NEWLINE); - return CMD_WARNING; - } - } - - rc = db_sync_authinfo_for_subscr( - ainfo.auth_algo == AUTH_ALGO_NONE ? NULL : &ainfo, - subscr); - - /* the last tuple probably invalid with the new auth settings */ - db_sync_lastauthtuple_for_subscr(NULL, subscr); - subscr_put(subscr); - - if (rc) { - vty_out(vty, "%% Operation has failed%s", VTY_NEWLINE); - return CMD_WARNING; - } - return CMD_SUCCESS; -} - -DEFUN(subscriber_purge, - subscriber_purge_cmd, - "subscriber purge-inactive", - "Operations on a Subscriber\n" "Purge subscribers with a zero use count.\n") -{ - struct gsm_network *net = gsmnet_from_vty(vty); - int purged; - - purged = subscr_purge_inactive(net->subscr_group); - vty_out(vty, "%d subscriber(s) were purged.%s", purged, VTY_NEWLINE); - return CMD_SUCCESS; -} - -DEFUN(subscriber_update, - subscriber_update_cmd, - "subscriber " SUBSCR_TYPES " ID update", - SUBSCR_HELP "Update the subscriber data from the dabase.\n") -{ - struct gsm_network *gsmnet = gsmnet_from_vty(vty); - struct gsm_subscriber *subscr = get_subscr_by_argv(gsmnet, argv[0], argv[1]); - - if (!subscr) { - vty_out(vty, "%% No subscriber found for %s %s%s", - argv[0], argv[1], VTY_NEWLINE); - return CMD_WARNING; - } - - subscr_update_from_db(subscr); - subscr_put(subscr); - return CMD_SUCCESS; -} - -static int scall_cbfn(unsigned int subsys, unsigned int signal, - void *handler_data, void *signal_data) -{ - struct scall_signal_data *sigdata = signal_data; - struct vty *vty = sigdata->data; - - switch (signal) { - case S_SCALL_SUCCESS: - vty_out(vty, "%% silent call on ARFCN %u timeslot %u%s", - sigdata->conn->lchan->ts->trx->arfcn, sigdata->conn->lchan->ts->nr, - VTY_NEWLINE); - break; - case S_SCALL_EXPIRED: - vty_out(vty, "%% silent call expired paging%s", VTY_NEWLINE); - break; - } - return 0; -} - -DEFUN(show_stats, - show_stats_cmd, - "show statistics", - SHOW_STR "Display network statistics\n") -{ - struct gsm_network *net = gsmnet_from_vty(vty); - - openbsc_vty_print_statistics(vty, net); - vty_out(vty, "Location Update : %lu attach, %lu normal, %lu periodic%s", - net->msc_ctrs->ctr[MSC_CTR_LOC_UPDATE_TYPE_ATTACH].current, - net->msc_ctrs->ctr[MSC_CTR_LOC_UPDATE_TYPE_NORMAL].current, - net->msc_ctrs->ctr[MSC_CTR_LOC_UPDATE_TYPE_PERIODIC].current, - VTY_NEWLINE); - vty_out(vty, "IMSI Detach Indications : %lu%s", - net->msc_ctrs->ctr[MSC_CTR_LOC_UPDATE_TYPE_DETACH].current, - VTY_NEWLINE); - vty_out(vty, "Location Updating Results: %lu completed, %lu failed%s", - net->msc_ctrs->ctr[MSC_CTR_LOC_UPDATE_COMPLETED].current, - net->msc_ctrs->ctr[MSC_CTR_LOC_UPDATE_FAILED].current, - VTY_NEWLINE); - vty_out(vty, "Handover : %lu attempted, %lu no_channel, %lu timeout, " - "%lu completed, %lu failed%s", - net->msc_ctrs->ctr[BSC_CTR_HANDOVER_ATTEMPTED].current, - net->msc_ctrs->ctr[BSC_CTR_HANDOVER_NO_CHANNEL].current, - net->msc_ctrs->ctr[BSC_CTR_HANDOVER_TIMEOUT].current, - net->msc_ctrs->ctr[BSC_CTR_HANDOVER_COMPLETED].current, - net->msc_ctrs->ctr[BSC_CTR_HANDOVER_FAILED].current, - VTY_NEWLINE); - vty_out(vty, "SMS MO : %lu submitted, %lu no receiver%s", - net->msc_ctrs->ctr[MSC_CTR_SMS_SUBMITTED].current, - net->msc_ctrs->ctr[MSC_CTR_SMS_NO_RECEIVER].current, - VTY_NEWLINE); - vty_out(vty, "SMS MT : %lu delivered, %lu no memory, %lu other error%s", - net->msc_ctrs->ctr[MSC_CTR_SMS_DELIVERED].current, - net->msc_ctrs->ctr[MSC_CTR_SMS_RP_ERR_MEM].current, - net->msc_ctrs->ctr[MSC_CTR_SMS_RP_ERR_OTHER].current, - VTY_NEWLINE); - vty_out(vty, "MO Calls : %lu setup, %lu connect ack%s", - net->msc_ctrs->ctr[MSC_CTR_CALL_MO_SETUP].current, - net->msc_ctrs->ctr[MSC_CTR_CALL_MO_CONNECT_ACK].current, - VTY_NEWLINE); - vty_out(vty, "MT Calls : %lu setup, %lu connect%s", - net->msc_ctrs->ctr[MSC_CTR_CALL_MT_SETUP].current, - net->msc_ctrs->ctr[MSC_CTR_CALL_MT_CONNECT].current, - VTY_NEWLINE); - return CMD_SUCCESS; -} - -DEFUN(show_smsqueue, - show_smsqueue_cmd, - "show sms-queue", - SHOW_STR "Display SMSqueue statistics\n") -{ - struct gsm_network *net = gsmnet_from_vty(vty); - - sms_queue_stats(net->sms_queue, vty); - return CMD_SUCCESS; -} - -DEFUN(smsqueue_trigger, - smsqueue_trigger_cmd, - "sms-queue trigger", - "SMS Queue\n" "Trigger sending messages\n") -{ - struct gsm_network *net = gsmnet_from_vty(vty); - - sms_queue_trigger(net->sms_queue); - return CMD_SUCCESS; -} - -DEFUN(smsqueue_max, - smsqueue_max_cmd, - "sms-queue max-pending <1-500>", - "SMS Queue\n" "SMS to deliver in parallel\n" "Amount\n") -{ - struct gsm_network *net = gsmnet_from_vty(vty); - - sms_queue_set_max_pending(net->sms_queue, atoi(argv[0])); - return CMD_SUCCESS; -} - -DEFUN(smsqueue_clear, - smsqueue_clear_cmd, - "sms-queue clear", - "SMS Queue\n" "Clear the queue of pending SMS\n") -{ - struct gsm_network *net = gsmnet_from_vty(vty); - - sms_queue_clear(net->sms_queue); - return CMD_SUCCESS; -} - -DEFUN(smsqueue_fail, - smsqueue_fail_cmd, - "sms-queue max-failure <1-500>", - "SMS Queue\n" "Maximum amount of delivery failures\n" "Amount\n") -{ - struct gsm_network *net = gsmnet_from_vty(vty); - - sms_queue_set_max_failure(net->sms_queue, atoi(argv[0])); - return CMD_SUCCESS; -} - - -DEFUN(cfg_mncc_int, cfg_mncc_int_cmd, - "mncc-int", "Configure internal MNCC handler") -{ - vty->node = MNCC_INT_NODE; - - return CMD_SUCCESS; -} - -static struct cmd_node mncc_int_node = { - MNCC_INT_NODE, - "%s(config-mncc-int)# ", - 1, -}; - -static const struct value_string tchf_codec_names[] = { - { GSM48_CMODE_SPEECH_V1, "fr" }, - { GSM48_CMODE_SPEECH_EFR, "efr" }, - { GSM48_CMODE_SPEECH_AMR, "amr" }, - { 0, NULL } -}; - -static const struct value_string tchh_codec_names[] = { - { GSM48_CMODE_SPEECH_V1, "hr" }, - { GSM48_CMODE_SPEECH_AMR, "amr" }, - { 0, NULL } -}; - -static int config_write_mncc_int(struct vty *vty) -{ - uint16_t meas_port; - char *meas_host; - const char *meas_scenario; - - meas_feed_cfg_get(&meas_host, &meas_port); - meas_scenario = meas_feed_scenario_get(); - - vty_out(vty, "mncc-int%s", VTY_NEWLINE); - vty_out(vty, " default-codec tch-f %s%s", - get_value_string(tchf_codec_names, mncc_int.def_codec[0]), - VTY_NEWLINE); - vty_out(vty, " default-codec tch-h %s%s", - get_value_string(tchh_codec_names, mncc_int.def_codec[1]), - VTY_NEWLINE); - if (meas_port) - vty_out(vty, " meas-feed destination %s %u%s", - meas_host, meas_port, VTY_NEWLINE); - if (strlen(meas_scenario) > 0) - vty_out(vty, " meas-feed scenario %s%s", - meas_scenario, VTY_NEWLINE); - - - return CMD_SUCCESS; -} - -DEFUN(mnccint_def_codec_f, - mnccint_def_codec_f_cmd, - "default-codec tch-f (fr|efr|amr)", - "Set default codec\n" "Codec for TCH/F\n" - "Full-Rate\n" "Enhanced Full-Rate\n" "Adaptive Multi-Rate\n") -{ - mncc_int.def_codec[0] = get_string_value(tchf_codec_names, argv[0]); - - return CMD_SUCCESS; -} - -DEFUN(mnccint_def_codec_h, - mnccint_def_codec_h_cmd, - "default-codec tch-h (hr|amr)", - "Set default codec\n" "Codec for TCH/H\n" - "Half-Rate\n" "Adaptive Multi-Rate\n") -{ - mncc_int.def_codec[1] = get_string_value(tchh_codec_names, argv[0]); - - return CMD_SUCCESS; -} - -#define OBSOLETE_MSG "Obsolete\n" -/* this is just for backwards compatibility as the sms code moved into - * libosmocore and old config files no longer parse... */ -DEFUN_DEPRECATED(log_level_sms, log_level_sms_cmd, - "logging level sms (everything|debug|info|notice|error|fatal)", - ".HIDDEN\n" OBSOLETE_MSG OBSOLETE_MSG OBSOLETE_MSG OBSOLETE_MSG - OBSOLETE_MSG OBSOLETE_MSG OBSOLETE_MSG OBSOLETE_MSG) -{ - vty_out(vty, "%% 'logging level sms' is now called 'logging level " - "lsms', please update your config %s", VTY_NEWLINE); - - return CMD_SUCCESS; -} - -#define MEAS_STR "Measurement export related\n" -DEFUN(mnccint_meas_feed, mnccint_meas_feed_cmd, - "meas-feed destination ADDR <0-65535>", - MEAS_STR "destination\n" "address or hostname\n" "port number\n") -{ - int rc; - - rc = meas_feed_cfg_set(argv[0], atoi(argv[1])); - if (rc < 0) - return CMD_WARNING; - - return CMD_SUCCESS; -} - -DEFUN(meas_feed_scenario, meas_feed_scenario_cmd, - "meas-feed scenario NAME", - MEAS_STR "scenario\n" "Name up to 31 characters included in report\n") -{ - meas_feed_scenario_set(argv[0]); - - return CMD_SUCCESS; -} - - -DEFUN(logging_fltr_imsi, - logging_fltr_imsi_cmd, - "logging filter imsi IMSI", - LOGGING_STR FILTER_STR - "Filter log messages by IMSI\n" "IMSI to be used as filter\n") -{ - struct gsm_subscriber *vlr_subscr; - struct bsc_subscr *bsc_subscr; - struct gsm_network *gsmnet = gsmnet_from_vty(vty); - struct log_target *tgt = osmo_log_vty2tgt(vty); - const char *imsi = argv[0]; - - if (!tgt) - return CMD_WARNING; - - vlr_subscr = subscr_get_by_imsi(gsmnet->subscr_group, imsi); - bsc_subscr = bsc_subscr_find_by_imsi(gsmnet->bsc_subscribers, imsi); - - if (!vlr_subscr && !bsc_subscr) { - vty_out(vty, "%%no subscriber with IMSI(%s)%s", - argv[0], VTY_NEWLINE); - return CMD_WARNING; - } - - log_set_filter_vlr_subscr(tgt, vlr_subscr); - log_set_filter_bsc_subscr(tgt, bsc_subscr); - return CMD_SUCCESS; -} - -static struct cmd_node nitb_node = { - NITB_NODE, - "%s(config-nitb)# ", - 1, -}; - -DEFUN(cfg_nitb, cfg_nitb_cmd, - "nitb", "Configure NITB options") -{ - vty->node = NITB_NODE; - return CMD_SUCCESS; -} - -/* Note: limit on the parameter length is set by internal vty code limitations */ -DEFUN(cfg_nitb_subscr_random, cfg_nitb_subscr_random_cmd, - "subscriber-create-on-demand random <1-9999999999> <2-9999999999>", - "Set random parameters for a new record when a subscriber is first seen.\n" - "Set random parameters for a new record when a subscriber is first seen.\n" - "Minimum for subscriber extension\n""Maximum for subscriber extension\n") -{ - struct gsm_network *gsmnet = gsmnet_from_vty(vty); - uint64_t mi = atoll(argv[0]), ma = atoll(argv[1]); - gsmnet->auto_create_subscr = true; - gsmnet->auto_assign_exten = true; - if (mi >= ma) { - vty_out(vty, "Incorrect range: %s >= %s, expected MIN < MAX%s", - argv[0], argv[1], VTY_NEWLINE); - return CMD_WARNING; - } - gsmnet->ext_min = mi; - gsmnet->ext_max = ma; - return CMD_SUCCESS; -} - -DEFUN(cfg_nitb_subscr_create, cfg_nitb_subscr_create_cmd, - "subscriber-create-on-demand [no-extension]", - "Make a new record when a subscriber is first seen.\n" - "Do not automatically assign extension to created subscribers\n") -{ - struct gsm_network *gsmnet = gsmnet_from_vty(vty); - gsmnet->auto_create_subscr = true; - gsmnet->auto_assign_exten = argc ? false : true; - return CMD_SUCCESS; -} - -DEFUN(cfg_nitb_no_subscr_create, cfg_nitb_no_subscr_create_cmd, - "no subscriber-create-on-demand", - NO_STR "Make a new record when a subscriber is first seen.\n") -{ - struct gsm_network *gsmnet = gsmnet_from_vty(vty); - gsmnet->auto_create_subscr = false; - return CMD_SUCCESS; -} - -DEFUN(cfg_nitb_assign_tmsi, cfg_nitb_assign_tmsi_cmd, - "assign-tmsi", - "Assign TMSI during Location Updating.\n") -{ - struct gsm_network *gsmnet = gsmnet_from_vty(vty); - gsmnet->avoid_tmsi = 0; - return CMD_SUCCESS; -} - -DEFUN(cfg_nitb_no_assign_tmsi, cfg_nitb_no_assign_tmsi_cmd, - "no assign-tmsi", - NO_STR "Assign TMSI during Location Updating.\n") -{ - struct gsm_network *gsmnet = gsmnet_from_vty(vty); - gsmnet->avoid_tmsi = 1; - return CMD_SUCCESS; -} - -static int config_write_nitb(struct vty *vty) -{ - struct gsm_network *gsmnet = gsmnet_from_vty(vty); - - vty_out(vty, "nitb%s", VTY_NEWLINE); - if (!gsmnet->auto_create_subscr) - vty_out(vty, " no subscriber-create-on-demand%s", VTY_NEWLINE); - else - vty_out(vty, " subscriber-create-on-demand%s%s", - gsmnet->auto_assign_exten ? "" : " no-extension", - VTY_NEWLINE); - - if (gsmnet->ext_min != GSM_MIN_EXTEN || gsmnet->ext_max != GSM_MAX_EXTEN) - vty_out(vty, " subscriber-create-on-demand random %"PRIu64" %" - PRIu64"%s", gsmnet->ext_min, gsmnet->ext_max, - VTY_NEWLINE); - vty_out(vty, " %sassign-tmsi%s", - gsmnet->avoid_tmsi ? "no " : "", VTY_NEWLINE); - return CMD_SUCCESS; -} - -int bsc_vty_init_extra(void) -{ - osmo_signal_register_handler(SS_SCALL, scall_cbfn, NULL); - - install_element_ve(&show_subscr_cmd); - install_element_ve(&show_subscr_cache_cmd); - - install_element_ve(&sms_send_pend_cmd); - - install_element_ve(&subscriber_create_cmd); - install_element_ve(&subscriber_send_sms_cmd); - install_element_ve(&subscriber_silent_sms_cmd); - install_element_ve(&subscriber_silent_call_start_cmd); - install_element_ve(&subscriber_silent_call_stop_cmd); - install_element_ve(&subscriber_ussd_notify_cmd); - install_element_ve(&subscriber_update_cmd); - install_element_ve(&show_stats_cmd); - install_element_ve(&show_smsqueue_cmd); - install_element_ve(&logging_fltr_imsi_cmd); - - install_element(ENABLE_NODE, &ena_subscr_delete_cmd); - install_element(ENABLE_NODE, &ena_subscr_expire_cmd); - install_element(ENABLE_NODE, &ena_subscr_name_cmd); - install_element(ENABLE_NODE, &ena_subscr_extension_cmd); - install_element(ENABLE_NODE, &ena_subscr_authorized_cmd); - install_element(ENABLE_NODE, &ena_subscr_a3a8_cmd); - install_element(ENABLE_NODE, &ena_subscr_handover_cmd); - install_element(ENABLE_NODE, &subscriber_purge_cmd); - install_element(ENABLE_NODE, &smsqueue_trigger_cmd); - install_element(ENABLE_NODE, &smsqueue_max_cmd); - install_element(ENABLE_NODE, &smsqueue_clear_cmd); - install_element(ENABLE_NODE, &smsqueue_fail_cmd); - install_element(ENABLE_NODE, &subscriber_send_pending_sms_cmd); - install_element(ENABLE_NODE, &meas_feed_scenario_cmd); - - install_element(CONFIG_NODE, &cfg_mncc_int_cmd); - install_node(&mncc_int_node, config_write_mncc_int); - vty_install_default(MNCC_INT_NODE); - install_element(MNCC_INT_NODE, &mnccint_def_codec_f_cmd); - install_element(MNCC_INT_NODE, &mnccint_def_codec_h_cmd); - install_element(MNCC_INT_NODE, &mnccint_meas_feed_cmd); - install_element(MNCC_INT_NODE, &meas_feed_scenario_cmd); - - install_element(CFG_LOG_NODE, &log_level_sms_cmd); - install_element(CFG_LOG_NODE, &logging_fltr_imsi_cmd); - - - install_element(CONFIG_NODE, &cfg_nitb_cmd); - install_node(&nitb_node, config_write_nitb); - install_element(NITB_NODE, &cfg_nitb_subscr_create_cmd); - install_element(NITB_NODE, &cfg_nitb_subscr_random_cmd); - install_element(NITB_NODE, &cfg_nitb_no_subscr_create_cmd); - install_element(NITB_NODE, &cfg_nitb_assign_tmsi_cmd); - install_element(NITB_NODE, &cfg_nitb_no_assign_tmsi_cmd); - - return 0; -} diff --git a/openbsc/src/libtrau/Makefile.am b/openbsc/src/libtrau/Makefile.am deleted file mode 100644 index 46becd6a..00000000 --- a/openbsc/src/libtrau/Makefile.am +++ /dev/null @@ -1,31 +0,0 @@ -AM_CPPFLAGS = \ - $(all_includes) \ - -I$(top_srcdir)/include \ - -I$(top_builddir) \ - $(NULL) - -AM_CFLAGS = \ - -Wall \ - $(LIBOSMOCORE_CFLAGS) \ - $(LIBOSMOVTY_CFLAGS) \ - $(LIBOSMOABIS_CFLAGS) \ - $(LIBOSMONETIF_CFLAGS) \ - $(COVERAGE_CFLAGS) \ - $(NULL) - -AM_LDFLAGS = \ - $(LIBOSMOCORE_LIBS) \ - $(LIBOSMOGSM_LIBS) \ - $(LIBOSMOABIS_LIBS) \ - $(COVERAGE_LDFLAGS) \ - $(NULL) - -noinst_LIBRARIES = \ - libtrau.a \ - $(NULL) - -libtrau_a_SOURCES = \ - rtp_proxy.c \ - trau_mux.c \ - trau_upqueue.c \ - $(NULL) diff --git a/openbsc/src/libtrau/rtp_proxy.c b/openbsc/src/libtrau/rtp_proxy.c deleted file mode 100644 index 6b38ee5f..00000000 --- a/openbsc/src/libtrau/rtp_proxy.c +++ /dev/null @@ -1,764 +0,0 @@ -/* RTP proxy handling for ip.access nanoBTS */ - -/* (C) 2009-2013 by Harald Welte - * All Rights Reserved - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation; either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - * - */ - -#include -#include -#include -#include -#include -#include /* gettimeofday() */ -#include /* get..() */ -#include /* clock() */ -#include /* uname() */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -/* attempt to determine byte order */ -#include -#include - -static LLIST_HEAD(rtp_sockets); - -/* should we mangle the CNAME inside SDES of RTCP packets? We disable - * this by default, as it seems to be not needed */ -static int mangle_rtcp_cname = 0; - -enum rtp_bfd_priv { - RTP_PRIV_NONE, - RTP_PRIV_RTP, - RTP_PRIV_RTCP -}; - -#define RTP_ALLOC_SIZE 1500 - -#define RTCP_TYPE_SDES 202 - -#define RTCP_IE_CNAME 1 - - -#define RTP_VERSION 2 - -/* 33 for FR, all other codecs have smaller size */ -#define MAX_RTP_PAYLOAD_LEN 33 - -/* decode an rtp frame and create a new buffer with payload */ -static int rtp_decode(struct msgb *msg, uint32_t callref, struct msgb **data) -{ - struct msgb *new_msg; - struct gsm_data_frame *frame; - struct rtp_hdr *rtph = (struct rtp_hdr *)msg->data; - struct rtp_x_hdr *rtpxh; - uint8_t *payload, *payload_out; - int payload_len; - int msg_type; - int x_len; - - if (msg->len < 12) { - DEBUGPC(DLMUX, "received RTP frame too short (len = %d)\n", - msg->len); - return -EINVAL; - } - if (rtph->version != RTP_VERSION) { - DEBUGPC(DLMUX, "received RTP version %d not supported.\n", - rtph->version); - return -EINVAL; - } - payload = msg->data + sizeof(struct rtp_hdr) + (rtph->csrc_count << 2); - payload_len = msg->len - sizeof(struct rtp_hdr) - (rtph->csrc_count << 2); - if (payload_len < 0) { - DEBUGPC(DLMUX, "received RTP frame too short (len = %d, " - "csrc count = %d)\n", msg->len, rtph->csrc_count); - return -EINVAL; - } - if (rtph->extension) { - if (payload_len < sizeof(struct rtp_x_hdr)) { - DEBUGPC(DLMUX, "received RTP frame too short for " - "extension header\n"); - return -EINVAL; - } - rtpxh = (struct rtp_x_hdr *)payload; - x_len = ntohs(rtpxh->length) * 4 + sizeof(struct rtp_x_hdr); - payload += x_len; - payload_len -= x_len; - if (payload_len < 0) { - DEBUGPC(DLMUX, "received RTP frame too short, " - "extension header exceeds frame length\n"); - return -EINVAL; - } - } - if (rtph->padding) { - if (payload_len < 1) { - DEBUGPC(DLMUX, "received RTP frame too short for " - "padding length\n"); - return -EINVAL; - } - payload_len -= payload[payload_len - 1]; - if (payload_len < 0) { - DEBUGPC(DLMUX, "received RTP frame with padding " - "greater than payload\n"); - return -EINVAL; - } - } - - switch (rtph->payload_type) { - case RTP_PT_GSM_FULL: - msg_type = GSM_TCHF_FRAME; - if (payload_len != RTP_LEN_GSM_FULL) { - DEBUGPC(DLMUX, "received RTP full rate frame with " - "payload length != %d (len = %d)\n", - RTP_LEN_GSM_FULL, payload_len); - return -EINVAL; - } - break; - case RTP_PT_GSM_EFR: - msg_type = GSM_TCHF_FRAME_EFR; - if (payload_len != RTP_LEN_GSM_EFR) { - DEBUGPC(DLMUX, "received RTP extended full rate frame " - "with payload length != %d (len = %d)\n", - RTP_LEN_GSM_EFR, payload_len); - return -EINVAL; - } - break; - case RTP_PT_GSM_HALF: - msg_type = GSM_TCHH_FRAME; - if (payload_len != RTP_LEN_GSM_HALF) { - DEBUGPC(DLMUX, "received RTP half rate frame with " - "payload length != %d (len = %d)\n", - RTP_LEN_GSM_HALF, payload_len); - return -EINVAL; - } - break; - case RTP_PT_AMR: - msg_type = GSM_TCH_FRAME_AMR; - break; - default: - DEBUGPC(DLMUX, "received RTP frame with unknown payload " - "type %d\n", rtph->payload_type); - return -EINVAL; - } - - if (payload_len > MAX_RTP_PAYLOAD_LEN || - (rtph->payload_type == RTP_PT_AMR && - payload_len > MAX_RTP_PAYLOAD_LEN - 1)) { - DEBUGPC(DLMUX, "RTP payload too large (%d octets)\n", - payload_len); - return -EINVAL; - } - - /* always allocate for the maximum possible size to avoid - * fragmentation */ - new_msg = msgb_alloc(sizeof(struct gsm_data_frame) + - MAX_RTP_PAYLOAD_LEN+1, "GSM-DATA (TCH)"); - - if (!new_msg) - return -ENOMEM; - frame = (struct gsm_data_frame *) msgb_put(new_msg, sizeof(struct gsm_data_frame)); - frame->msg_type = msg_type; - frame->callref = callref; - if (rtph->payload_type == RTP_PT_AMR) { - /* for FR/HR/EFR the length is implicit. In AMR, we - * need to make it explicit by using the first byte of - * the data[] buffer as length byte */ - uint8_t *data0 = msgb_put(new_msg, 1); - *data0 = payload_len; - } - payload_out = msgb_put(new_msg, payload_len); - memcpy(payload_out, payload, payload_len); - - *data = new_msg; - return 0; -} - -/*! \brief encode and send a rtp frame - * \param[in] rs RTP socket through which we shall send - * \param[in] frame GSM RTP frame to be sent - */ -int rtp_send_frame(struct rtp_socket *rs, struct gsm_data_frame *frame) -{ - struct rtp_sub_socket *rss = &rs->rtp; - struct msgb *msg; - struct rtp_hdr *rtph; - uint8_t *payload; - int payload_type; - int payload_len; - int duration; /* in samples */ - int is_bfi = 0; - - if (rs->tx_action != RTP_SEND_DOWNSTREAM) { - /* initialize sequences */ - rs->tx_action = RTP_SEND_DOWNSTREAM; - rs->transmit.ssrc = rand(); - rs->transmit.sequence = random(); - rs->transmit.timestamp = random(); - } - - switch (frame->msg_type) { - case GSM_TCHF_FRAME: - payload_type = RTP_PT_GSM_FULL; - payload_len = RTP_LEN_GSM_FULL; - duration = RTP_GSM_DURATION; - break; - case GSM_TCHF_FRAME_EFR: - payload_type = RTP_PT_GSM_EFR; - payload_len = RTP_LEN_GSM_EFR; - duration = RTP_GSM_DURATION; - break; - case GSM_TCHH_FRAME: - payload_type = RTP_PT_GSM_HALF; - payload_len = RTP_LEN_GSM_HALF; - duration = RTP_GSM_DURATION; - break; - case GSM_TCH_FRAME_AMR: - payload_type = RTP_PT_AMR; - payload_len = frame->data[0]; - duration = RTP_GSM_DURATION; - break; - case GSM_BAD_FRAME: - payload_type = 0; - payload_len = 0; - duration = RTP_GSM_DURATION; - is_bfi = 1; - break; - default: - DEBUGPC(DLMUX, "unsupported message type %d\n", - frame->msg_type); - return -EINVAL; - } - - if (payload_len > MAX_RTP_PAYLOAD_LEN) { - DEBUGPC(DLMUX, "RTP payload too large (%d octets)\n", - payload_len); - return -EINVAL; - } - - if (is_bfi) { - /* In case of a bad frame, just count and drop packet. */ - rs->transmit.timestamp += duration; - rs->transmit.sequence++; - return 0; - } - - msg = msgb_alloc(sizeof(struct rtp_hdr) + payload_len, "RTP-GSM"); - if (!msg) - return -ENOMEM; - rtph = (struct rtp_hdr *) msgb_put(msg, sizeof(struct rtp_hdr)); - rtph->version = RTP_VERSION; - rtph->padding = 0; - rtph->extension = 0; - rtph->csrc_count = 0; - rtph->marker = 0; - rtph->payload_type = payload_type; - rtph->sequence = htons(rs->transmit.sequence++); - rtph->timestamp = htonl(rs->transmit.timestamp); - rs->transmit.timestamp += duration; - rtph->ssrc = htonl(rs->transmit.ssrc); - - payload = msgb_put(msg, payload_len); - if (frame->msg_type == GSM_TCH_FRAME_AMR) - memcpy(payload, frame->data + 1, payload_len); - else - memcpy(payload, frame->data, payload_len); - msgb_enqueue(&rss->tx_queue, msg); - rss->bfd.when |= BSC_FD_WRITE; - - return 0; -} - -/* iterate over all chunks in one RTCP message, look for CNAME IEs and - * replace all of those with 'new_cname' */ -static int rtcp_sdes_cname_mangle(struct msgb *msg, struct rtcp_hdr *rh, - uint16_t *rtcp_len, const char *new_cname) -{ - uint8_t *rtcp_end; - uint8_t *cur = (uint8_t *) rh; - uint8_t tag, len = 0; - - rtcp_end = cur + *rtcp_len; - /* move cur to end of RTP header */ - cur += sizeof(*rh); - - /* iterate over Chunks */ - while (cur+4 < rtcp_end) { - /* skip four bytes SSRC/CSRC */ - cur += 4; - - /* iterate over IE's inside the chunk */ - while (cur+1 < rtcp_end) { - tag = *cur++; - if (tag == 0) { - /* end of chunk, skip additional zero */ - while ((*cur++ == 0) && (cur < rtcp_end)) { } - break; - } - len = *cur++; - - if (tag == RTCP_IE_CNAME) { - /* we've found the CNAME, lets mangle it */ - if (len < strlen(new_cname)) { - /* we need to make more space */ - int increase = strlen(new_cname) - len; - - msgb_push(msg, increase); - memmove(cur+len+increase, cur+len, - rtcp_end - (cur+len)); - /* FIXME: we have to respect RTCP - * padding/alignment rules! */ - len += increase; - *(cur-1) += increase; - rtcp_end += increase; - *rtcp_len += increase; - } - /* copy new CNAME into message */ - memcpy(cur, new_cname, strlen(new_cname)); - /* FIXME: zero the padding in case new CNAME - * is smaller than old one !!! */ - } - cur += len; - } - } - - return 0; -} - -static int rtcp_mangle(struct msgb *msg, struct rtp_socket *rs) -{ - struct rtp_sub_socket *rss = &rs->rtcp; - struct rtcp_hdr *rtph; - uint16_t old_len; - int rc; - - if (!mangle_rtcp_cname) - return 0; - - printf("RTCP\n"); - /* iterate over list of RTCP messages */ - rtph = (struct rtcp_hdr *)msg->data; - while ((void *)rtph + sizeof(*rtph) <= (void *)msg->data + msg->len) { - old_len = (ntohs(rtph->length) + 1) * 4; - if ((void *)rtph + old_len > (void *)msg->data + msg->len) { - DEBUGPC(DLMUX, "received RTCP packet too short for " - "length element\n"); - return -EINVAL; - } - if (rtph->type == RTCP_TYPE_SDES) { - char new_cname[255]; - osmo_strlcpy(new_cname, - inet_ntoa(rss->sin_local.sin_addr), - sizeof(new_cname)); - rc = rtcp_sdes_cname_mangle(msg, rtph, &old_len, - new_cname); - if (rc < 0) - return rc; - } - rtph = (void *)rtph + old_len; - } - - return 0; -} - -/* read from incoming RTP/RTCP socket */ -static int rtp_socket_read(struct rtp_socket *rs, struct rtp_sub_socket *rss) -{ - int rc; - struct msgb *msg = msgb_alloc(RTP_ALLOC_SIZE, "RTP/RTCP"); - struct msgb *new_msg; - struct rtp_sub_socket *other_rss; - - if (!msg) - return -ENOMEM; - - rc = read(rss->bfd.fd, msg->data, RTP_ALLOC_SIZE); - if (rc == 0) { - rss->bfd.when &= ~BSC_FD_READ; - goto out_free; - } else if (rc < 0) { - /* Ignore "connection refused". this happens, If we open the - * socket faster than the remote side. */ - if (errno == ECONNREFUSED) - goto out_free; - DEBUGPC(DLMUX, "Read of RTP socket (%p) failed (errno %d, " - "%s)\n", rs, errno, strerror(errno)); - rss->bfd.when &= ~BSC_FD_READ; - goto out_free; - } - - msgb_put(msg, rc); - - switch (rs->rx_action) { - case RTP_PROXY: - if (!rs->proxy.other_sock) { - rc = -EIO; - goto out_free; - } - if (rss->bfd.priv_nr == RTP_PRIV_RTP) - other_rss = &rs->proxy.other_sock->rtp; - else if (rss->bfd.priv_nr == RTP_PRIV_RTCP) { - other_rss = &rs->proxy.other_sock->rtcp; - /* modify RTCP SDES CNAME */ - rc = rtcp_mangle(msg, rs); - if (rc < 0) - goto out_free; - } else { - rc = -EINVAL; - goto out_free; - } - msgb_enqueue(&other_rss->tx_queue, msg); - other_rss->bfd.when |= BSC_FD_WRITE; - break; - - case RTP_RECV_UPSTREAM: - if (!rs->receive.callref || !rs->receive.net) { - rc = -EIO; - goto out_free; - } - if (rss->bfd.priv_nr == RTP_PRIV_RTCP) { - if (!mangle_rtcp_cname) { - msgb_free(msg); - break; - } - /* modify RTCP SDES CNAME */ - rc = rtcp_mangle(msg, rs); - if (rc < 0) - goto out_free; - msgb_enqueue(&rss->tx_queue, msg); - rss->bfd.when |= BSC_FD_WRITE; - break; - } - if (rss->bfd.priv_nr != RTP_PRIV_RTP) { - rc = -EINVAL; - goto out_free; - } - rc = rtp_decode(msg, rs->receive.callref, &new_msg); - if (rc < 0) - goto out_free; - msgb_free(msg); - trau_tx_to_mncc(rs->receive.net, new_msg); - break; - - case RTP_NONE: /* if socket exists, but disabled by app */ - msgb_free(msg); - break; - } - - return 0; - -out_free: - msgb_free(msg); - return rc; -} - -/* \brief write from tx_queue to RTP/RTCP socket */ -static int rtp_socket_write(struct rtp_socket *rs, struct rtp_sub_socket *rss) -{ - struct msgb *msg; - int written; - - msg = msgb_dequeue(&rss->tx_queue); - if (!msg) { - rss->bfd.when &= ~BSC_FD_WRITE; - return 0; - } - - written = write(rss->bfd.fd, msg->data, msg->len); - if (written < msg->len) { - LOGP(DLMIB, LOGL_ERROR, "short write"); - msgb_free(msg); - return -EIO; - } - - msgb_free(msg); - - return 0; -} - - -/*! \brief callback for the select.c:bfd_* layer */ -static int rtp_bfd_cb(struct osmo_fd *bfd, unsigned int flags) -{ - struct rtp_socket *rs = bfd->data; - struct rtp_sub_socket *rss; - - switch (bfd->priv_nr) { - case RTP_PRIV_RTP: - rss = &rs->rtp; - break; - case RTP_PRIV_RTCP: - rss = &rs->rtcp; - break; - default: - return -EINVAL; - } - - if (flags & BSC_FD_READ) - rtp_socket_read(rs, rss); - - if (flags & BSC_FD_WRITE) - rtp_socket_write(rs, rss); - - return 0; -} - -/*! \brief initialize one rtp sub-socket */ -static void init_rss(struct rtp_sub_socket *rss, - struct rtp_socket *rs, int fd, int priv_nr) -{ - /* initialize bfd */ - rss->bfd.fd = fd; - rss->bfd.data = rs; - rss->bfd.priv_nr = priv_nr; - rss->bfd.cb = rtp_bfd_cb; -} - -/*! \brief create a new RTP/RTCP socket and bind it */ -struct rtp_socket *rtp_socket_create(void) -{ - int rc; - struct rtp_socket *rs; - - DEBUGP(DLMUX, "rtp_socket_create(): "); - - rs = talloc_zero(tall_bsc_ctx, struct rtp_socket); - if (!rs) - return NULL; - - INIT_LLIST_HEAD(&rs->rtp.tx_queue); - INIT_LLIST_HEAD(&rs->rtcp.tx_queue); - - rc = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); - if (rc < 0) - goto out_free; - - init_rss(&rs->rtp, rs, rc, RTP_PRIV_RTP); - rc = osmo_fd_register(&rs->rtp.bfd); - if (rc < 0) - goto out_rtp_socket; - - rc = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); - if (rc < 0) - goto out_rtp_bfd; - - init_rss(&rs->rtcp, rs, rc, RTP_PRIV_RTCP); - rc = osmo_fd_register(&rs->rtcp.bfd); - if (rc < 0) - goto out_rtcp_socket; - - DEBUGPC(DLMUX, "success\n"); - - rc = rtp_socket_bind(rs, INADDR_ANY); - if (rc < 0) - goto out_rtcp_bfd; - - return rs; - -out_rtcp_bfd: - osmo_fd_unregister(&rs->rtcp.bfd); -out_rtcp_socket: - close(rs->rtcp.bfd.fd); -out_rtp_bfd: - osmo_fd_unregister(&rs->rtp.bfd); -out_rtp_socket: - close(rs->rtp.bfd.fd); -out_free: - talloc_free(rs); - DEBUGPC(DLMUX, "failed\n"); - return NULL; -} - -static int rtp_sub_socket_bind(struct rtp_sub_socket *rss, uint32_t ip, - uint16_t port) -{ - int rc; - socklen_t alen = sizeof(rss->sin_local); - - rss->sin_local.sin_family = AF_INET; - rss->sin_local.sin_addr.s_addr = htonl(ip); - rss->sin_local.sin_port = htons(port); - rss->bfd.when |= BSC_FD_READ; - - rc = bind(rss->bfd.fd, (struct sockaddr *)&rss->sin_local, - sizeof(rss->sin_local)); - if (rc < 0) - return rc; - - /* retrieve the address we actually bound to, in case we - * passed INADDR_ANY as IP address */ - return getsockname(rss->bfd.fd, (struct sockaddr *)&rss->sin_local, - &alen); -} - -#define RTP_PORT_BASE 30000 -static unsigned int next_udp_port = RTP_PORT_BASE; - -/*! \brief bind a RTP socket to a specific local address - * \param[in] rs RTP socket to be bound - * \param[in] ip local IP address to which socket is to be bound - */ -int rtp_socket_bind(struct rtp_socket *rs, uint32_t ip) -{ - int rc = -EIO; - struct in_addr ia; - - ia.s_addr = htonl(ip); - DEBUGP(DLMUX, "rtp_socket_bind(rs=%p, IP=%s): ", rs, - inet_ntoa(ia)); - - /* try to bind to a consecutive pair of ports */ - for (next_udp_port = next_udp_port % 0xffff; - next_udp_port < 0xffff; next_udp_port += 2) { - rc = rtp_sub_socket_bind(&rs->rtp, ip, next_udp_port); - if (rc != 0) - continue; - - rc = rtp_sub_socket_bind(&rs->rtcp, ip, next_udp_port+1); - if (rc == 0) - break; - } - if (rc < 0) { - DEBUGPC(DLMUX, "failed\n"); - return rc; - } - - ia.s_addr = rs->rtp.sin_local.sin_addr.s_addr; - DEBUGPC(DLMUX, "BOUND_IP=%s, BOUND_PORT=%u\n", - inet_ntoa(ia), ntohs(rs->rtp.sin_local.sin_port)); - return ntohs(rs->rtp.sin_local.sin_port); -} - -static int rtp_sub_socket_connect(struct rtp_sub_socket *rss, - uint32_t ip, uint16_t port) -{ - int rc; - socklen_t alen = sizeof(rss->sin_local); - - rss->sin_remote.sin_family = AF_INET; - rss->sin_remote.sin_addr.s_addr = htonl(ip); - rss->sin_remote.sin_port = htons(port); - - rc = connect(rss->bfd.fd, (struct sockaddr *) &rss->sin_remote, - sizeof(rss->sin_remote)); - if (rc < 0) - return rc; - - return getsockname(rss->bfd.fd, (struct sockaddr *)&rss->sin_local, - &alen); -} - -/*! \brief 'connect' a RTP socket to a remote peer - * \param[in] rs RTP socket to be connected - * \param[in] ip remote IP address to which to connect - * \param[in] port remote UDP port number to which to connect - */ -int rtp_socket_connect(struct rtp_socket *rs, uint32_t ip, uint16_t port) -{ - int rc; - struct in_addr ia; - - ia.s_addr = htonl(ip); - DEBUGP(DLMUX, "rtp_socket_connect(rs=%p, ip=%s, port=%u)\n", - rs, inet_ntoa(ia), port); - - rc = rtp_sub_socket_connect(&rs->rtp, ip, port); - if (rc < 0) - return rc; - - return rtp_sub_socket_connect(&rs->rtcp, ip, port+1); -} - -/*! \brief bind two RTP/RTCP sockets together in the proxy - * \param[in] this First RTP socket - * \param[in] other Second RTP socket - */ -int rtp_socket_proxy(struct rtp_socket *this, struct rtp_socket *other) -{ - DEBUGP(DLMUX, "rtp_socket_proxy(this=%p, other=%p)\n", - this, other); - - this->rx_action = RTP_PROXY; - this->proxy.other_sock = other; - - other->rx_action = RTP_PROXY; - other->proxy.other_sock = this; - - return 0; -} - -/*! \brief bind RTP/RTCP socket to application, disabling proxy - * \param[in] this RTP socket - * \param[in] net gsm_network argument to trau_tx_to_mncc() - * \param[in] callref callref argument to trau_tx_to_mncc() - */ -int rtp_socket_upstream(struct rtp_socket *this, struct gsm_network *net, - uint32_t callref) -{ - DEBUGP(DLMUX, "rtp_socket_proxy(this=%p, callref=%u)\n", - this, callref); - - if (callref) { - this->rx_action = RTP_RECV_UPSTREAM; - this->receive.net = net; - this->receive.callref = callref; - } else - this->rx_action = RTP_NONE; - - return 0; -} - -static void free_tx_queue(struct rtp_sub_socket *rss) -{ - struct msgb *msg; - - while ((msg = msgb_dequeue(&rss->tx_queue))) - msgb_free(msg); -} - -/*! \brief Free/release a previously allocated RTP socket - * \param[in[] rs RTP/RTCP socket to be released - */ -int rtp_socket_free(struct rtp_socket *rs) -{ - DEBUGP(DLMUX, "rtp_socket_free(rs=%p)\n", rs); - - /* make sure we don't leave references dangling to us */ - if (rs->rx_action == RTP_PROXY && - rs->proxy.other_sock) - rs->proxy.other_sock->proxy.other_sock = NULL; - - osmo_fd_unregister(&rs->rtp.bfd); - close(rs->rtp.bfd.fd); - free_tx_queue(&rs->rtp); - - osmo_fd_unregister(&rs->rtcp.bfd); - close(rs->rtcp.bfd.fd); - free_tx_queue(&rs->rtcp); - - talloc_free(rs); - - return 0; -} diff --git a/openbsc/src/libtrau/trau_mux.c b/openbsc/src/libtrau/trau_mux.c deleted file mode 100644 index b37c7650..00000000 --- a/openbsc/src/libtrau/trau_mux.c +++ /dev/null @@ -1,547 +0,0 @@ -/* Simple TRAU frame reflector to route voice calls */ - -/* (C) 2009 by Harald Welte - * All Rights Reserved - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation; either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - * - */ - -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -/* this corresponds to the bit-lengths of the individual codec - * parameters as indicated in Table 1.1 of TS 06.10 */ -static const uint8_t gsm_fr_map[] = { - 6, 6, 5, 5, 4, 4, 3, 3, - 7, 2, 2, 6, 3, 3, 3, 3, - 3, 3, 3, 3, 3, 3, 3, 3, - 3, 7, 2, 2, 6, 3, 3, 3, - 3, 3, 3, 3, 3, 3, 3, 3, - 3, 3, 7, 2, 2, 6, 3, 3, - 3, 3, 3, 3, 3, 3, 3, 3, - 3, 3, 3, 7, 2, 2, 6, 3, - 3, 3, 3, 3, 3, 3, 3, 3, - 3, 3, 3, 3 -}; - - -/* - * EFR TRAU parity - * - * g(x) = x^3 + x^1 + 1 - */ -static const struct osmo_crc8gen_code gsm0860_efr_crc3 = { - .bits = 3, - .poly = 0x3, - .init = 0x0, - .remainder = 0x7, -}; - -/* EFR parity bits */ -static inline void efr_parity_bits_1(ubit_t *check_bits, const ubit_t *d_bits) -{ - memcpy(check_bits + 0 , d_bits + 0, 22); - memcpy(check_bits + 22 , d_bits + 24, 3); - check_bits[25] = d_bits[28]; -} - -static inline void efr_parity_bits_2(ubit_t *check_bits, const ubit_t *d_bits) -{ - memcpy(check_bits + 0 , d_bits + 42, 10); - memcpy(check_bits + 10 , d_bits + 90, 2); -} - -static inline void efr_parity_bits_3(ubit_t *check_bits, const ubit_t *d_bits) -{ - memcpy(check_bits + 0 , d_bits + 98, 5); - check_bits[5] = d_bits[104]; - memcpy(check_bits + 6 , d_bits + 143, 2); -} - -static inline void efr_parity_bits_4(ubit_t *check_bits, const ubit_t *d_bits) -{ - memcpy(check_bits + 0 , d_bits + 151, 10); - memcpy(check_bits + 10 , d_bits + 199, 2); -} - -static inline void efr_parity_bits_5(ubit_t *check_bits, const ubit_t *d_bits) -{ - memcpy(check_bits + 0 , d_bits + 207, 5); - check_bits[5] = d_bits[213]; - memcpy(check_bits + 6 , d_bits + 252, 2); -} - -struct map_entry { - struct llist_head list; - struct gsm_e1_subslot src, dst; -}; - -struct upqueue_entry { - struct llist_head list; - struct gsm_network *net; - struct gsm_e1_subslot src; - uint32_t callref; -}; - -static LLIST_HEAD(ss_map); -static LLIST_HEAD(ss_upqueue); - -void *tall_map_ctx, *tall_upq_ctx; - -/* map one particular subslot to another subslot */ -int trau_mux_map(const struct gsm_e1_subslot *src, - const struct gsm_e1_subslot *dst) -{ - struct map_entry *me; - - me = talloc(tall_map_ctx, struct map_entry); - if (!me) { - LOGP(DLMIB, LOGL_FATAL, "Out of memory\n"); - return -ENOMEM; - } - - DEBUGP(DCC, "Setting up TRAU mux map between (e1=%u,ts=%u,ss=%u) " - "and (e1=%u,ts=%u,ss=%u)\n", - src->e1_nr, src->e1_ts, src->e1_ts_ss, - dst->e1_nr, dst->e1_ts, dst->e1_ts_ss); - - /* make sure to get rid of any stale old mappings */ - trau_mux_unmap(src, 0); - trau_mux_unmap(dst, 0); - - memcpy(&me->src, src, sizeof(me->src)); - memcpy(&me->dst, dst, sizeof(me->dst)); - llist_add(&me->list, &ss_map); - - return 0; -} - -int trau_mux_map_lchan(const struct gsm_lchan *src, - const struct gsm_lchan *dst) -{ - struct gsm_e1_subslot *src_ss, *dst_ss; - - src_ss = &src->ts->e1_link; - dst_ss = &dst->ts->e1_link; - - return trau_mux_map(src_ss, dst_ss); -} - - -/* unmap one particular subslot from another subslot */ -int trau_mux_unmap(const struct gsm_e1_subslot *ss, uint32_t callref) -{ - struct map_entry *me, *me2; - struct upqueue_entry *ue, *ue2; - - if (ss) - llist_for_each_entry_safe(me, me2, &ss_map, list) { - if (!memcmp(&me->src, ss, sizeof(*ss)) || - !memcmp(&me->dst, ss, sizeof(*ss))) { - llist_del(&me->list); - return 0; - } - } - llist_for_each_entry_safe(ue, ue2, &ss_upqueue, list) { - if (ue->callref == callref) { - llist_del(&ue->list); - return 0; - } - if (ss && !memcmp(&ue->src, ss, sizeof(*ss))) { - llist_del(&ue->list); - return 0; - } - } - return -ENOENT; -} - -/* look-up an enty in the TRAU mux map */ -static struct gsm_e1_subslot * -lookup_trau_mux_map(const struct gsm_e1_subslot *src) -{ - struct map_entry *me; - - llist_for_each_entry(me, &ss_map, list) { - if (!memcmp(&me->src, src, sizeof(*src))) - return &me->dst; - if (!memcmp(&me->dst, src, sizeof(*src))) - return &me->src; - } - return NULL; -} - -/* look-up an enty in the TRAU upqueue */ -struct upqueue_entry * -lookup_trau_upqueue(const struct gsm_e1_subslot *src) -{ - struct upqueue_entry *ue; - - llist_for_each_entry(ue, &ss_upqueue, list) { - if (!memcmp(&ue->src, src, sizeof(*src))) - return ue; - } - return NULL; -} - -static const uint8_t c_bits_check_fr[] = { 0, 0, 0, 1, 0 }; -static const uint8_t c_bits_check_efr[] = { 1, 1, 0, 1, 0 }; - -struct msgb *trau_decode_fr(uint32_t callref, - const struct decoded_trau_frame *tf) -{ - struct msgb *msg; - struct gsm_data_frame *frame; - unsigned char *data; - int i, j, k, l, o; - - msg = msgb_alloc(sizeof(struct gsm_data_frame) + 33, - "GSM-DATA"); - if (!msg) - return NULL; - - frame = (struct gsm_data_frame *)msg->data; - memset(frame, 0, sizeof(struct gsm_data_frame)); - data = frame->data; - data[0] = 0xd << 4; - /* reassemble d-bits */ - i = 0; /* counts bits */ - j = 4; /* counts output bits */ - k = gsm_fr_map[0]-1; /* current number bit in element */ - l = 0; /* counts element bits */ - o = 0; /* offset input bits */ - while (i < 260) { - data[j/8] |= (tf->d_bits[k+o] << (7-(j%8))); - /* to avoid out-of-bounds access in gsm_fr_map[++l] */ - if (i == 259) - break; - if (--k < 0) { - o += gsm_fr_map[l]; - k = gsm_fr_map[++l]-1; - } - i++; - j++; - } - if (tf->c_bits[11]) /* BFI */ - frame->msg_type = GSM_BAD_FRAME; - else - frame->msg_type = GSM_TCHF_FRAME; - frame->callref = callref; - msgb_put(msg, sizeof(struct gsm_data_frame) + 33); - - return msg; -} - -struct msgb *trau_decode_efr(uint32_t callref, - const struct decoded_trau_frame *tf) -{ - struct msgb *msg; - struct gsm_data_frame *frame; - unsigned char *data; - int i, j, rc; - ubit_t check_bits[26]; - - msg = msgb_alloc(sizeof(struct gsm_data_frame) + 31, - "GSM-DATA"); - if (!msg) - return NULL; - - frame = (struct gsm_data_frame *)msg->data; - memset(frame, 0, sizeof(struct gsm_data_frame)); - frame->msg_type = GSM_TCHF_FRAME_EFR; - frame->callref = callref; - msgb_put(msg, sizeof(struct gsm_data_frame) + 31); - - if (tf->c_bits[11]) /* BFI */ - goto bad_frame; - - data = frame->data; - data[0] = 0xc << 4; - /* reassemble d-bits */ - for (i = 1, j = 4; i < 39; i++, j++) - data[j/8] |= (tf->d_bits[i] << (7-(j%8))); - efr_parity_bits_1(check_bits, tf->d_bits); - rc = osmo_crc8gen_check_bits(&gsm0860_efr_crc3, check_bits, 26, - tf->d_bits + 39); - if (rc) - goto bad_frame; - for (i = 42, j = 42; i < 95; i++, j++) - data[j/8] |= (tf->d_bits[i] << (7-(j%8))); - efr_parity_bits_2(check_bits, tf->d_bits); - rc = osmo_crc8gen_check_bits(&gsm0860_efr_crc3, check_bits, 12, - tf->d_bits + 95); - if (rc) - goto bad_frame; - for (i = 98, j = 95; i < 148; i++, j++) - data[j/8] |= (tf->d_bits[i] << (7-(j%8))); - efr_parity_bits_3(check_bits, tf->d_bits); - rc = osmo_crc8gen_check_bits(&gsm0860_efr_crc3, check_bits, 8, - tf->d_bits + 148); - if (rc) - goto bad_frame; - for (i = 151, j = 145; i < 204; i++, j++) - data[j/8] |= (tf->d_bits[i] << (7-(j%8))); - efr_parity_bits_4(check_bits, tf->d_bits); - rc = osmo_crc8gen_check_bits(&gsm0860_efr_crc3, check_bits, 12, - tf->d_bits + 204); - if (rc) - goto bad_frame; - for (i = 207, j = 198; i < 257; i++, j++) - data[j/8] |= (tf->d_bits[i] << (7-(j%8))); - efr_parity_bits_5(check_bits, tf->d_bits); - rc = osmo_crc8gen_check_bits(&gsm0860_efr_crc3, check_bits, 8, - tf->d_bits + 257); - if (rc) - goto bad_frame; - - return msg; - -bad_frame: - frame->msg_type = GSM_BAD_FRAME; - - return msg; -} - -/* we get called by subchan_demux */ -int trau_mux_input(struct gsm_e1_subslot *src_e1_ss, - const uint8_t *trau_bits, int num_bits) -{ - struct decoded_trau_frame tf; - uint8_t trau_bits_out[TRAU_FRAME_BITS]; - struct gsm_e1_subslot *dst_e1_ss = lookup_trau_mux_map(src_e1_ss); - struct subch_mux *mx; - struct upqueue_entry *ue; - int rc; - - /* decode TRAU, change it to downlink, re-encode */ - rc = decode_trau_frame(&tf, trau_bits); - if (rc) - return rc; - - if (!dst_e1_ss) { - struct msgb *msg = NULL; - /* frame shall be sent to upqueue */ - if (!(ue = lookup_trau_upqueue(src_e1_ss))) - return -EINVAL; - if (!ue->callref) - return -EINVAL; - if (!memcmp(tf.c_bits, c_bits_check_fr, 5)) - msg = trau_decode_fr(ue->callref, &tf); - else if (!memcmp(tf.c_bits, c_bits_check_efr, 5)) - msg = trau_decode_efr(ue->callref, &tf); - else { - DEBUGPC(DLMUX, "illegal trau (C1-C5) %s\n", - osmo_hexdump(tf.c_bits, 5)); - DEBUGPC(DLMUX, "test trau (C1-C5) %s\n", - osmo_hexdump(c_bits_check_efr, 5)); - return -EINVAL; - } - if (!msg) - return -ENOMEM; - trau_tx_to_mncc(ue->net, msg); - - return 0; - } - - mx = e1inp_get_mux(dst_e1_ss->e1_nr, dst_e1_ss->e1_ts); - if (!mx) - return -EINVAL; - - trau_frame_up2down(&tf); - encode_trau_frame(trau_bits_out, &tf); - - /* and send it to the muxer */ - return subchan_mux_enqueue(mx, dst_e1_ss->e1_ts_ss, trau_bits_out, - TRAU_FRAME_BITS); -} - -/* callback when a TRAU frame was received */ -int subch_cb(struct subch_demux *dmx, int ch, uint8_t *data, int len, - void *_priv) -{ - struct e1inp_ts *e1i_ts = _priv; - struct gsm_e1_subslot src_ss; - - src_ss.e1_nr = e1i_ts->line->num; - src_ss.e1_ts = e1i_ts->num; - src_ss.e1_ts_ss = ch; - - return trau_mux_input(&src_ss, data, len); -} - -/* add receiver instance for lchan and callref */ -int trau_recv_lchan(struct gsm_lchan *lchan, uint32_t callref) -{ - struct gsm_e1_subslot *src_ss; - struct upqueue_entry *ue; - - ue = talloc(tall_upq_ctx, struct upqueue_entry); - if (!ue) - return -ENOMEM; - - src_ss = &lchan->ts->e1_link; - - DEBUGP(DCC, "Setting up TRAU receiver (e1=%u,ts=%u,ss=%u) " - "and (callref 0x%x)\n", - src_ss->e1_nr, src_ss->e1_ts, src_ss->e1_ts_ss, - callref); - - /* make sure to get rid of any stale old mappings */ - trau_mux_unmap(src_ss, callref); - - memcpy(&ue->src, src_ss, sizeof(ue->src)); - ue->net = lchan->ts->trx->bts->network; - ue->callref = callref; - llist_add(&ue->list, &ss_upqueue); - - return 0; -} - -void trau_encode_fr(struct decoded_trau_frame *tf, - const unsigned char *data) -{ - int i, j, k, l, o; - - /* set c-bits and t-bits */ - tf->c_bits[0] = 1; - tf->c_bits[1] = 1; - tf->c_bits[2] = 1; - tf->c_bits[3] = 0; - tf->c_bits[4] = 0; - memset(&tf->c_bits[5], 0, 6); - memset(&tf->c_bits[11], 1, 10); - memset(&tf->t_bits[0], 1, 4); - /* reassemble d-bits */ - i = 0; /* counts bits */ - j = 4; /* counts input bits */ - k = gsm_fr_map[0]-1; /* current number bit in element */ - l = 0; /* counts element bits */ - o = 0; /* offset output bits */ - while (i < 260) { - tf->d_bits[k+o] = (data[j/8] >> (7-(j%8))) & 1; - /* to avoid out-of-bounds access in gsm_fr_map[++l] */ - if (i == 259) - break; - if (--k < 0) { - o += gsm_fr_map[l]; - k = gsm_fr_map[++l]-1; - } - i++; - j++; - } -} - -void trau_encode_efr(struct decoded_trau_frame *tf, - const unsigned char *data) -{ - int i, j; - ubit_t check_bits[26]; - - /* set c-bits and t-bits */ - tf->c_bits[0] = 1; - tf->c_bits[1] = 1; - tf->c_bits[2] = 0; - tf->c_bits[3] = 1; - tf->c_bits[4] = 0; - memset(&tf->c_bits[5], 0, 6); - memset(&tf->c_bits[11], 1, 10); - memset(&tf->t_bits[0], 1, 4); - /* reassemble d-bits */ - tf->d_bits[0] = 1; - for (i = 1, j = 4; i < 39; i++, j++) - tf->d_bits[i] = (data[j/8] >> (7-(j%8))) & 1; - efr_parity_bits_1(check_bits, tf->d_bits); - osmo_crc8gen_set_bits(&gsm0860_efr_crc3, check_bits, 26, - tf->d_bits + 39); - for (i = 42, j = 42; i < 95; i++, j++) - tf->d_bits[i] = (data[j/8] >> (7-(j%8))) & 1; - efr_parity_bits_2(check_bits, tf->d_bits); - osmo_crc8gen_set_bits(&gsm0860_efr_crc3, check_bits, 12, - tf->d_bits + 95); - for (i = 98, j = 95; i < 148; i++, j++) - tf->d_bits[i] = (data[j/8] >> (7-(j%8))) & 1; - efr_parity_bits_3(check_bits, tf->d_bits); - osmo_crc8gen_set_bits(&gsm0860_efr_crc3, check_bits, 8, - tf->d_bits + 148); - for (i = 151, j = 145; i < 204; i++, j++) - tf->d_bits[i] = (data[j/8] >> (7-(j%8))) & 1; - efr_parity_bits_4(check_bits, tf->d_bits); - osmo_crc8gen_set_bits(&gsm0860_efr_crc3, check_bits, 12, - tf->d_bits + 204); - for (i = 207, j = 198; i < 257; i++, j++) - tf->d_bits[i] = (data[j/8] >> (7-(j%8))) & 1; - efr_parity_bits_5(check_bits, tf->d_bits); - osmo_crc8gen_set_bits(&gsm0860_efr_crc3, check_bits, 8, - tf->d_bits + 257); -} - -int trau_send_frame(struct gsm_lchan *lchan, struct gsm_data_frame *frame) -{ - uint8_t trau_bits_out[TRAU_FRAME_BITS]; - struct gsm_e1_subslot *dst_e1_ss = &lchan->ts->e1_link; - struct subch_mux *mx; - struct decoded_trau_frame tf; - - mx = e1inp_get_mux(dst_e1_ss->e1_nr, dst_e1_ss->e1_ts); - if (!mx) - return -EINVAL; - - switch (frame->msg_type) { - case GSM_TCHF_FRAME: - trau_encode_fr(&tf, frame->data); - break; - case GSM_TCHF_FRAME_EFR: - trau_encode_efr(&tf, frame->data); - break; - default: - DEBUGPC(DLMUX, "unsupported message type %d\n", - frame->msg_type); - return -EINVAL; - } - - encode_trau_frame(trau_bits_out, &tf); - - /* and send it to the muxer */ - return subchan_mux_enqueue(mx, dst_e1_ss->e1_ts_ss, trau_bits_out, - TRAU_FRAME_BITS); -} - -/* switch trau muxer to new lchan */ -int switch_trau_mux(struct gsm_lchan *old_lchan, struct gsm_lchan *new_lchan) -{ - struct gsm_network *net = old_lchan->ts->trx->bts->network; - struct gsm_trans *trans; - - /* look up transaction with TCH frame receive enabled */ - llist_for_each_entry(trans, &net->trans_list, entry) { - if (trans->conn && trans->conn->lchan == old_lchan && trans->tch_recv) { - /* switch */ - trau_recv_lchan(new_lchan, trans->callref); - } - } - - return 0; -} diff --git a/openbsc/src/libtrau/trau_upqueue.c b/openbsc/src/libtrau/trau_upqueue.c deleted file mode 100644 index f8edaf0f..00000000 --- a/openbsc/src/libtrau/trau_upqueue.c +++ /dev/null @@ -1,27 +0,0 @@ -/* trau_upqueue.c - Pass msgb's up the chain */ - -/* (C) 2010 by Harald Welte - * All Rights Reserved - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation; either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public 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/osmo-bsc/Makefile.am b/openbsc/src/osmo-bsc/Makefile.am deleted file mode 100644 index ae9410c9..00000000 --- a/openbsc/src/osmo-bsc/Makefile.am +++ /dev/null @@ -1,55 +0,0 @@ -AM_CPPFLAGS = \ - $(all_includes) \ - -I$(top_srcdir)/include \ - -I$(top_builddir) \ - $(NULL) - -AM_CFLAGS = \ - -Wall \ - $(LIBOSMOCORE_CFLAGS) \ - $(LIBOSMOGSM_CFLAGS) \ - $(LIBOSMOVTY_CFLAGS) \ - $(LIBOSMOCTRL_CFLAGS) \ - $(LIBOSMONETIF_CFLAGS) \ - $(LIBOSMOSCCP_CFLAGS) \ - $(COVERAGE_CFLAGS) \ - $(LIBOSMOABIS_CFLAGS) \ - $(NULL) - -AM_LDFLAGS = \ - $(COVERAGE_LDFLAGS) \ - $(NULL) - -bin_PROGRAMS = \ - osmo-bsc \ - $(NULL) - -osmo_bsc_SOURCES = \ - osmo_bsc_main.c \ - osmo_bsc_vty.c \ - osmo_bsc_api.c \ - osmo_bsc_grace.c \ - osmo_bsc_msc.c \ - osmo_bsc_sccp.c \ - osmo_bsc_filter.c \ - osmo_bsc_bssap.c \ - osmo_bsc_audio.c \ - osmo_bsc_ctrl.c \ - $(NULL) - -# once again since TRAU uses CC symbol :( -osmo_bsc_LDADD = \ - $(top_builddir)/src/libfilter/libfilter.a \ - $(top_builddir)/src/libbsc/libbsc.a \ - $(top_builddir)/src/libcommon-cs/libcommon-cs.a \ - $(top_builddir)/src/libmsc/libmsc.a \ - $(top_builddir)/src/libtrau/libtrau.a \ - $(top_builddir)/src/libcommon/libcommon.a \ - $(LIBOSMOSCCP_LIBS) \ - $(LIBOSMOCORE_LIBS) \ - $(LIBOSMOGSM_LIBS) \ - $(LIBOSMOVTY_LIBS) \ - $(LIBOSMOCTRL_LIBS) \ - $(COVERAGE_LDFLAGS) \ - $(LIBOSMOABIS_LIBS) \ - $(NULL) diff --git a/openbsc/src/osmo-bsc/osmo_bsc_api.c b/openbsc/src/osmo-bsc/osmo_bsc_api.c deleted file mode 100644 index bac5e471..00000000 --- a/openbsc/src/osmo-bsc/osmo_bsc_api.c +++ /dev/null @@ -1,550 +0,0 @@ -/* (C) 2009-2015 by Holger Hans Peter Freyther - * (C) 2009-2011 by On-Waves - * All Rights Reserved - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation; either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - * - */ - -#include -#include -#include - -#include - -#include -#include - -#include - -#define return_when_not_connected(conn) \ - if (!conn->sccp_con) {\ - LOGP(DMSC, LOGL_ERROR, "MSC Connection not present.\n"); \ - return; \ - } - -#define return_when_not_connected_val(conn, ret) \ - if (!conn->sccp_con) {\ - LOGP(DMSC, LOGL_ERROR, "MSC Connection not present.\n"); \ - return ret; \ - } - -#define queue_msg_or_return(resp) \ - if (!resp) { \ - LOGP(DMSC, LOGL_ERROR, "Failed to allocate response.\n"); \ - return; \ - } \ - bsc_queue_for_msc(conn->sccp_con, resp); - -static int bsc_clear_request(struct gsm_subscriber_connection *conn, uint32_t cause); -static int complete_layer3(struct gsm_subscriber_connection *conn, - struct msgb *msg, struct bsc_msc_data *msc); - -static uint16_t get_network_code_for_msc(struct bsc_msc_data *msc) -{ - if (msc->core_mnc != -1) - return msc->core_mnc; - return msc->network->network_code; -} - -static uint16_t get_country_code_for_msc(struct bsc_msc_data *msc) -{ - if (msc->core_mcc != -1) - return msc->core_mcc; - return msc->network->country_code; -} - -static uint16_t get_lac_for_msc(struct bsc_msc_data *msc, struct gsm_bts *bts) -{ - if (msc->core_lac != -1) - return msc->core_lac; - return bts->location_area_code; -} - -static uint16_t get_ci_for_msc(struct bsc_msc_data *msc, struct gsm_bts *bts) -{ - if (msc->core_ci != -1) - return msc->core_ci; - return bts->cell_identity; -} - -static void bsc_maybe_lu_reject(struct gsm_subscriber_connection *conn, int con_type, int cause) -{ - struct msgb *msg; - - /* ignore cm service request or such */ - if (con_type != FLT_CON_TYPE_LU) - return; - - msg = gsm48_create_loc_upd_rej(cause); - if (!msg) { - LOGP(DMM, LOGL_ERROR, "Failed to create msg for LOCATION UPDATING REJECT.\n"); - return; - } - - msg->lchan = conn->lchan; - gsm0808_submit_dtap(conn, msg, 0, 0); -} - -static int bsc_filter_initial(struct osmo_bsc_data *bsc, - struct bsc_msc_data *msc, - struct gsm_subscriber_connection *conn, - struct msgb *msg, char **imsi, int *con_type, - int *lu_cause) -{ - struct bsc_filter_request req; - struct bsc_filter_reject_cause cause; - struct gsm48_hdr *gh = msgb_l3(msg); - int rc; - - req.ctx = conn; - req.black_list = NULL; - req.access_lists = bsc_access_lists(); - req.local_lst_name = msc->acc_lst_name; - req.global_lst_name = conn->bts->network->bsc_data->acc_lst_name; - req.bsc_nr = 0; - - rc = bsc_msg_filter_initial(gh, msgb_l3len(msg), &req, - con_type, imsi, &cause); - *lu_cause = cause.lu_reject_cause; - return rc; -} - -static int bsc_filter_data(struct gsm_subscriber_connection *conn, - struct msgb *msg, int *lu_cause) -{ - struct bsc_filter_request req; - struct gsm48_hdr *gh = msgb_l3(msg); - struct bsc_filter_reject_cause cause; - int rc; - - req.ctx = conn; - req.black_list = NULL; - req.access_lists = bsc_access_lists(); - req.local_lst_name = conn->sccp_con->msc->acc_lst_name; - req.global_lst_name = conn->bts->network->bsc_data->acc_lst_name; - req.bsc_nr = 0; - - rc = bsc_msg_filter_data(gh, msgb_l3len(msg), &req, - &conn->sccp_con->filter_state, - &cause); - *lu_cause = cause.lu_reject_cause; - return rc; -} - -static void bsc_sapi_n_reject(struct gsm_subscriber_connection *conn, int dlci) -{ - struct msgb *resp; - return_when_not_connected(conn); - - LOGP(DMSC, LOGL_NOTICE, "Tx MSC SAPI N REJECT DLCI=0x%02x\n", dlci); - - resp = gsm0808_create_sapi_reject(dlci); - queue_msg_or_return(resp); -} - -static void bsc_cipher_mode_compl(struct gsm_subscriber_connection *conn, - struct msgb *msg, uint8_t chosen_encr) -{ - struct msgb *resp; - return_when_not_connected(conn); - - LOGP(DMSC, LOGL_DEBUG, "CIPHER MODE COMPLETE from MS, forwarding to MSC\n"); - resp = gsm0808_create_cipher_complete(msg, chosen_encr); - queue_msg_or_return(resp); -} - -static void bsc_send_ussd_no_srv(struct gsm_subscriber_connection *conn, - struct msgb *msg, const char *text) -{ - struct gsm48_hdr *gh; - int8_t pdisc; - uint8_t mtype; - int drop_message = 1; - - if (!text) - return; - - if (!msg || msgb_l3len(msg) < sizeof(*gh)) - return; - - gh = msgb_l3(msg); - pdisc = gsm48_hdr_pdisc(gh); - mtype = gsm48_hdr_msg_type(gh); - - /* Is CM service request? */ - if (pdisc == GSM48_PDISC_MM && mtype == GSM48_MT_MM_CM_SERV_REQ) { - struct gsm48_service_request *cm; - - cm = (struct gsm48_service_request *) &gh->data[0]; - - /* Is type SMS or call? */ - if (cm->cm_service_type == GSM48_CMSERV_SMS) - drop_message = 0; - else if (cm->cm_service_type == GSM48_CMSERV_MO_CALL_PACKET) - drop_message = 0; - } - - if (drop_message) { - LOGP(DMSC, LOGL_DEBUG, "Skipping (not sending) USSD message: '%s'\n", text); - return; - } - - LOGP(DMSC, LOGL_INFO, "Sending CM Service Accept\n"); - gsm48_tx_mm_serv_ack(conn); - - LOGP(DMSC, LOGL_INFO, "Sending USSD message: '%s'\n", text); - bsc_send_ussd_notify(conn, 1, text); - bsc_send_ussd_release_complete(conn); -} - -/* - * Instruct to reserve data for a new connectiom, create the complete - * layer three message, send it to open the connection. - */ -static int bsc_compl_l3(struct gsm_subscriber_connection *conn, struct msgb *msg, - uint16_t chosen_channel) -{ - struct bsc_msc_data *msc; - - LOGP(DMSC, LOGL_INFO, "Tx MSC COMPL L3\n"); - - /* find the MSC link we want to use */ - msc = bsc_find_msc(conn, msg); - if (!msc) { - LOGP(DMSC, LOGL_ERROR, "Failed to find a MSC for a connection.\n"); - bsc_send_ussd_no_srv(conn, msg, - conn->bts->network->bsc_data->ussd_no_msc_txt); - return -1; - } - - return complete_layer3(conn, msg, msc); -} - -static int complete_layer3(struct gsm_subscriber_connection *conn, - struct msgb *msg, struct bsc_msc_data *msc) -{ - int con_type, rc, lu_cause; - char *imsi = NULL; - struct timeval tv; - struct msgb *resp; - uint16_t network_code; - uint16_t country_code; - uint16_t lac; - uint16_t ci; - enum bsc_con ret; - int send_ping = msc->advanced_ping; - - /* Advanced ping/pong handling */ - if (osmo_timer_pending(&msc->pong_timer)) - send_ping = 0; - if (msc->ping_timeout <= 0) - send_ping = 0; - if (send_ping && osmo_timer_remaining(&msc->ping_timer, NULL, &tv) == -1) - send_ping = 0; - - /* Check the filter */ - rc = bsc_filter_initial(msc->network->bsc_data, msc, conn, msg, - &imsi, &con_type, &lu_cause); - if (rc < 0) { - bsc_maybe_lu_reject(conn, con_type, lu_cause); - return BSC_API_CONN_POL_REJECT; - } - - /* allocate resource for a new connection */ - ret = bsc_create_new_connection(conn, msc, send_ping); - - if (ret != BSC_CON_SUCCESS) { - /* allocation has failed */ - if (ret == BSC_CON_REJECT_NO_LINK) - bsc_send_ussd_no_srv(conn, msg, msc->ussd_msc_lost_txt); - else if (ret == BSC_CON_REJECT_RF_GRACE) - bsc_send_ussd_no_srv(conn, msg, msc->ussd_grace_txt); - - return BSC_API_CONN_POL_REJECT; - } - - if (imsi) - conn->sccp_con->filter_state.imsi = talloc_steal(conn, imsi); - conn->sccp_con->filter_state.con_type = con_type; - - /* check return value, if failed check msg for and send USSD */ - - network_code = get_network_code_for_msc(conn->sccp_con->msc); - country_code = get_country_code_for_msc(conn->sccp_con->msc); - lac = get_lac_for_msc(conn->sccp_con->msc, conn->bts); - ci = get_ci_for_msc(conn->sccp_con->msc, conn->bts); - - bsc_scan_bts_msg(conn, msg); - - resp = gsm0808_create_layer3(msg, network_code, country_code, lac, ci); - if (!resp) { - LOGP(DMSC, LOGL_DEBUG, "Failed to create layer3 message.\n"); - sccp_connection_free(conn->sccp_con->sccp); - bsc_delete_connection(conn->sccp_con); - return BSC_API_CONN_POL_REJECT; - } - - if (bsc_open_connection(conn->sccp_con, resp) != 0) { - sccp_connection_free(conn->sccp_con->sccp); - bsc_delete_connection(conn->sccp_con); - msgb_free(resp); - return BSC_API_CONN_POL_REJECT; - } - - return BSC_API_CONN_POL_ACCEPT; -} - -/* - * Plastic surgery... we want to give up the current connection - */ -static int move_to_msc(struct gsm_subscriber_connection *_conn, - struct msgb *msg, struct bsc_msc_data *msc) -{ - struct osmo_bsc_sccp_con *old_con = _conn->sccp_con; - - /* - * 1. Give up the old connection. - * This happens by sending a clear request to the MSC, - * it should end with the MSC releasing the connection. - */ - old_con->conn = NULL; - bsc_clear_request(_conn, 0); - - /* - * 2. Attempt to create a new connection to the local - * MSC. If it fails the caller will need to handle this - * properly. - */ - _conn->sccp_con = NULL; - if (complete_layer3(_conn, msg, msc) != BSC_API_CONN_POL_ACCEPT) { - gsm0808_clear(_conn); - bsc_subscr_con_free(_conn); - return 1; - } - - return 2; -} - -static int handle_cc_setup(struct gsm_subscriber_connection *conn, - struct msgb *msg) -{ - struct gsm48_hdr *gh = msgb_l3(msg); - uint8_t pdisc = gsm48_hdr_pdisc(gh); - uint8_t mtype = gsm48_hdr_msg_type(gh); - - struct bsc_msc_data *msc; - struct gsm_mncc_number called; - struct tlv_parsed tp; - unsigned payload_len; - - char _dest_nr[35]; - - /* - * Do we have a setup message here? if not return fast. - */ - if (pdisc != GSM48_PDISC_CC || mtype != GSM48_MT_CC_SETUP) - return 0; - - payload_len = msgb_l3len(msg) - sizeof(*gh); - - tlv_parse(&tp, &gsm48_att_tlvdef, gh->data, payload_len, 0, 0); - if (!TLVP_PRESENT(&tp, GSM48_IE_CALLED_BCD)) { - LOGP(DMSC, LOGL_ERROR, "Called BCD not present in setup.\n"); - return -1; - } - - memset(&called, 0, sizeof(called)); - gsm48_decode_called(&called, - TLVP_VAL(&tp, GSM48_IE_CALLED_BCD) - 1); - - if (called.plan != 1 && called.plan != 0) - return 0; - - if (called.plan == 1 && called.type == 1) { - _dest_nr[0] = _dest_nr[1] = '0'; - memcpy(_dest_nr + 2, called.number, sizeof(called.number)); - } else - memcpy(_dest_nr, called.number, sizeof(called.number)); - - /* - * Check if the connection should be moved... - */ - llist_for_each_entry(msc, &conn->bts->network->bsc_data->mscs, entry) { - if (msc->type != MSC_CON_TYPE_LOCAL) - continue; - if (!msc->local_pref) - continue; - if (regexec(&msc->local_pref_reg, _dest_nr, 0, NULL, 0) != 0) - continue; - - return move_to_msc(conn, msg, msc); - } - - return 0; -} - - -static void bsc_dtap(struct gsm_subscriber_connection *conn, uint8_t link_id, struct msgb *msg) -{ - int lu_cause; - struct msgb *resp; - return_when_not_connected(conn); - - LOGP(DMSC, LOGL_INFO, "Tx MSC DTAP LINK_ID=0x%02x\n", link_id); - - /* - * We might want to move this connection to a new MSC. Ask someone - * to handle it. If it was handled we will return. - */ - if (handle_cc_setup(conn, msg) >= 1) - return; - - /* Check the filter */ - if (bsc_filter_data(conn, msg, &lu_cause) < 0) { - bsc_maybe_lu_reject(conn, - conn->sccp_con->filter_state.con_type, - lu_cause); - bsc_clear_request(conn, 0); - return; - } - - bsc_scan_bts_msg(conn, msg); - - resp = gsm0808_create_dtap(msg, link_id); - queue_msg_or_return(resp); -} - -static void bsc_assign_compl(struct gsm_subscriber_connection *conn, uint8_t rr_cause, - uint8_t chosen_channel, uint8_t encr_alg_id, - uint8_t speech_model) -{ - struct msgb *resp; - return_when_not_connected(conn); - - LOGP(DMSC, LOGL_INFO, "Tx MSC ASSIGN COMPL\n"); - - resp = gsm0808_create_assignment_completed(rr_cause, chosen_channel, - encr_alg_id, speech_model); - queue_msg_or_return(resp); -} - -static void bsc_assign_fail(struct gsm_subscriber_connection *conn, - uint8_t cause, uint8_t *rr_cause) -{ - struct msgb *resp; - return_when_not_connected(conn); - - LOGP(DMSC, LOGL_INFO, "Tx MSC ASSIGN FAIL\n"); - - resp = gsm0808_create_assignment_failure(cause, rr_cause); - queue_msg_or_return(resp); -} - -static int bsc_clear_request(struct gsm_subscriber_connection *conn, uint32_t cause) -{ - struct osmo_bsc_sccp_con *sccp; - struct msgb *resp; - return_when_not_connected_val(conn, 1); - - LOGP(DMSC, LOGL_INFO, "Tx MSC CLEAR REQUEST\n"); - - /* - * Remove the connection from BSC<->SCCP part, the SCCP part - * will either be cleared by channel release or MSC disconnect - */ - sccp = conn->sccp_con; - sccp->conn = NULL; - conn->sccp_con = NULL; - - resp = gsm0808_create_clear_rqst(GSM0808_CAUSE_RADIO_INTERFACE_FAILURE); - if (!resp) { - LOGP(DMSC, LOGL_ERROR, "Failed to allocate response.\n"); - return 1; - } - - bsc_queue_for_msc(sccp, resp); - return 1; -} - -static void bsc_cm_update(struct gsm_subscriber_connection *conn, - const uint8_t *cm2, uint8_t cm2_len, - const uint8_t *cm3, uint8_t cm3_len) -{ - struct msgb *resp; - return_when_not_connected(conn); - - resp = gsm0808_create_classmark_update(cm2, cm2_len, cm3, cm3_len); - - queue_msg_or_return(resp); -} - -static void bsc_mr_config(struct gsm_subscriber_connection *conn, - struct gsm_lchan *lchan, int full_rate) -{ - struct bsc_msc_data *msc; - struct gsm48_multi_rate_conf *ms_conf, *bts_conf; - - if (!conn->sccp_con) { - LOGP(DMSC, LOGL_ERROR, - "No msc data available on conn %p. Audio will be broken.\n", - conn); - return; - } - - msc = conn->sccp_con->msc; - - /* initialize the data structure */ - lchan->mr_ms_lv[0] = sizeof(*ms_conf); - lchan->mr_bts_lv[0] = sizeof(*bts_conf); - ms_conf = (struct gsm48_multi_rate_conf *) &lchan->mr_ms_lv[1]; - bts_conf = (struct gsm48_multi_rate_conf *) &lchan->mr_bts_lv[1]; - memset(ms_conf, 0, sizeof(*ms_conf)); - memset(bts_conf, 0, sizeof(*bts_conf)); - - bts_conf->ver = ms_conf->ver = 1; - bts_conf->icmi = ms_conf->icmi = 1; - - /* maybe gcc see's it is copy of _one_ byte */ - bts_conf->m4_75 = ms_conf->m4_75 = msc->amr_conf.m4_75; - bts_conf->m5_15 = ms_conf->m5_15 = msc->amr_conf.m5_15; - bts_conf->m5_90 = ms_conf->m5_90 = msc->amr_conf.m5_90; - bts_conf->m6_70 = ms_conf->m6_70 = msc->amr_conf.m6_70; - bts_conf->m7_40 = ms_conf->m7_40 = msc->amr_conf.m7_40; - bts_conf->m7_95 = ms_conf->m7_95 = msc->amr_conf.m7_95; - if (full_rate) { - bts_conf->m10_2 = ms_conf->m10_2 = msc->amr_conf.m10_2; - bts_conf->m12_2 = ms_conf->m12_2 = msc->amr_conf.m12_2; - } - - /* now copy this into the bts structure */ - memcpy(lchan->mr_bts_lv, lchan->mr_ms_lv, sizeof(lchan->mr_ms_lv)); -} - -static struct bsc_api bsc_handler = { - .sapi_n_reject = bsc_sapi_n_reject, - .cipher_mode_compl = bsc_cipher_mode_compl, - .compl_l3 = bsc_compl_l3, - .dtap = bsc_dtap, - .assign_compl = bsc_assign_compl, - .assign_fail = bsc_assign_fail, - .clear_request = bsc_clear_request, - .classmark_chg = bsc_cm_update, - .mr_config = bsc_mr_config, -}; - -struct bsc_api *osmo_bsc_api() -{ - return &bsc_handler; -} diff --git a/openbsc/src/osmo-bsc/osmo_bsc_audio.c b/openbsc/src/osmo-bsc/osmo_bsc_audio.c deleted file mode 100644 index 11602090..00000000 --- a/openbsc/src/osmo-bsc/osmo_bsc_audio.c +++ /dev/null @@ -1,73 +0,0 @@ -/* - * ipaccess audio handling - * - * (C) 2009-2010 by Holger Hans Peter Freyther - * (C) 2009-2010 by On-Waves - * All Rights Reserved - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation; either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - * - */ - -#include -#include -#include -#include -#include -#include - -#include - -static int handle_abisip_signal(unsigned int subsys, unsigned int signal, - void *handler_data, void *signal_data) -{ - struct gsm_subscriber_connection *con; - struct gsm_lchan *lchan = signal_data; - int rc; - - if (subsys != SS_ABISIP) - return 0; - - con = lchan->conn; - if (!con || !con->sccp_con) - return 0; - - switch (signal) { - case S_ABISIP_CRCX_ACK: - /* - * TODO: handle handover here... then the audio should go to - * the old mgcp port.. - */ - /* we can ask it to connect now */ - LOGP(DMSC, LOGL_DEBUG, "Connecting BTS to port: %d conn: %d\n", - con->sccp_con->rtp_port, lchan->abis_ip.conn_id); - - rc = rsl_ipacc_mdcx(lchan, ntohl(INADDR_ANY), - con->sccp_con->rtp_port, - lchan->abis_ip.rtp_payload2); - if (rc < 0) { - LOGP(DMSC, LOGL_ERROR, "Failed to send MDCX: %d\n", rc); - return rc; - } - break; - } - - return 0; -} - -int osmo_bsc_audio_init(struct gsm_network *net) -{ - osmo_signal_register_handler(SS_ABISIP, handle_abisip_signal, net); - return 0; -} diff --git a/openbsc/src/osmo-bsc/osmo_bsc_bssap.c b/openbsc/src/osmo-bsc/osmo_bsc_bssap.c deleted file mode 100644 index 100f6644..00000000 --- a/openbsc/src/osmo-bsc/osmo_bsc_bssap.c +++ /dev/null @@ -1,548 +0,0 @@ -/* GSM 08.08 BSSMAP handling */ -/* (C) 2009-2012 by Holger Hans Peter Freyther - * (C) 2009-2012 by On-Waves - * All Rights Reserved - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation; either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - * - */ - -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include - -/* - * helpers for the assignment command - */ -enum gsm0808_permitted_speech audio_support_to_gsm88(struct gsm_audio_support *audio) -{ - if (audio->hr) { - switch (audio->ver) { - case 1: - return GSM0808_PERM_HR1; - break; - case 2: - return GSM0808_PERM_HR2; - break; - case 3: - return GSM0808_PERM_HR3; - break; - default: - LOGP(DMSC, LOGL_ERROR, "Wrong speech mode: %d\n", audio->ver); - return GSM0808_PERM_FR1; - } - } else { - switch (audio->ver) { - case 1: - return GSM0808_PERM_FR1; - break; - case 2: - return GSM0808_PERM_FR2; - break; - case 3: - return GSM0808_PERM_FR3; - break; - default: - LOGP(DMSC, LOGL_ERROR, "Wrong speech mode: %d\n", audio->ver); - return GSM0808_PERM_HR1; - } - } -} - -enum gsm48_chan_mode gsm88_to_chan_mode(enum gsm0808_permitted_speech speech) -{ - switch (speech) { - case GSM0808_PERM_HR1: - case GSM0808_PERM_FR1: - return GSM48_CMODE_SPEECH_V1; - break; - case GSM0808_PERM_HR2: - case GSM0808_PERM_FR2: - return GSM48_CMODE_SPEECH_EFR; - break; - case GSM0808_PERM_HR3: - case GSM0808_PERM_FR3: - return GSM48_CMODE_SPEECH_AMR; - break; - } - - LOGP(DMSC, LOGL_FATAL, "Should not be reached.\n"); - return GSM48_CMODE_SPEECH_AMR; -} - -static int bssmap_handle_reset_ack(struct bsc_msc_data *msc, - struct msgb *msg, unsigned int length) -{ - LOGP(DMSC, LOGL_NOTICE, "Reset ACK from MSC\n"); - return 0; -} - -/* GSM 08.08 § 3.2.1.19 */ -static int bssmap_handle_paging(struct bsc_msc_data *msc, - struct msgb *msg, unsigned int payload_length) -{ - struct bsc_subscr *subscr; - struct tlv_parsed tp; - char mi_string[GSM48_MI_SIZE]; - uint32_t tmsi = GSM_RESERVED_TMSI; - unsigned int lac = GSM_LAC_RESERVED_ALL_BTS; - uint8_t data_length; - const uint8_t *data; - uint8_t chan_needed = RSL_CHANNEED_ANY; - - tlv_parse(&tp, gsm0808_att_tlvdef(), msg->l4h + 1, payload_length - 1, 0, 0); - - if (!TLVP_PRESENT(&tp, GSM0808_IE_IMSI)) { - LOGP(DMSC, LOGL_ERROR, "Mandatory IMSI not present.\n"); - return -1; - } else if ((TLVP_VAL(&tp, GSM0808_IE_IMSI)[0] & GSM_MI_TYPE_MASK) != GSM_MI_TYPE_IMSI) { - LOGP(DMSC, LOGL_ERROR, "Wrong content in the IMSI\n"); - return -1; - } - - if (!TLVP_PRESENT(&tp, GSM0808_IE_CELL_IDENTIFIER_LIST)) { - LOGP(DMSC, LOGL_ERROR, "Mandatory CELL IDENTIFIER LIST not present.\n"); - return -1; - } - - if (TLVP_PRESENT(&tp, GSM0808_IE_TMSI) && - TLVP_LEN(&tp, GSM0808_IE_TMSI) == 4) { - tmsi = ntohl(tlvp_val32_unal(&tp, GSM0808_IE_TMSI)); - } - - /* - * parse the IMSI - */ - gsm48_mi_to_string(mi_string, sizeof(mi_string), - TLVP_VAL(&tp, GSM0808_IE_IMSI), TLVP_LEN(&tp, GSM0808_IE_IMSI)); - - /* - * parse the cell identifier list - */ - data_length = TLVP_LEN(&tp, GSM0808_IE_CELL_IDENTIFIER_LIST); - data = TLVP_VAL(&tp, GSM0808_IE_CELL_IDENTIFIER_LIST); - - /* - * Support paging to all network or one BTS at one LAC - */ - if (data_length == 3 && data[0] == CELL_IDENT_LAC) { - lac = osmo_load16be(&data[1]); - } else if (data_length > 1 || (data[0] & 0x0f) != CELL_IDENT_BSS) { - LOGP(DMSC, LOGL_ERROR, "Unsupported Cell Identifier List: %s\n", osmo_hexdump(data, data_length)); - return -1; - } - - if (TLVP_PRESENT(&tp, GSM0808_IE_CHANNEL_NEEDED) && TLVP_LEN(&tp, GSM0808_IE_CHANNEL_NEEDED) == 1) - chan_needed = TLVP_VAL(&tp, GSM0808_IE_CHANNEL_NEEDED)[0] & 0x03; - - if (TLVP_PRESENT(&tp, GSM0808_IE_EMLPP_PRIORITY)) { - LOGP(DMSC, LOGL_ERROR, "eMLPP is not handled\n"); - } - - subscr = bsc_subscr_find_or_create_by_imsi(msc->network->bsc_subscribers, - mi_string); - if (!subscr) { - LOGP(DMSC, LOGL_ERROR, "Failed to allocate a subscriber for %s\n", mi_string); - return -1; - } - - subscr->lac = lac; - subscr->tmsi = tmsi; - - LOGP(DMSC, LOGL_INFO, "Paging request from MSC IMSI: '%s' TMSI: '0x%x/%u' LAC: 0x%x\n", mi_string, tmsi, tmsi, lac); - bsc_grace_paging_request(msc->network->bsc_data->rf_ctrl->policy, - subscr, chan_needed, msc); - return 0; -} - -/* - * GSM 08.08 § 3.1.9.1 and 3.2.1.21... - * release our gsm_subscriber_connection and send message - */ -static int bssmap_handle_clear_command(struct osmo_bsc_sccp_con *conn, - struct msgb *msg, unsigned int payload_length) -{ - struct msgb *resp; - - /* TODO: handle the cause of this package */ - - if (conn->conn) { - LOGP(DMSC, LOGL_INFO, "Releasing all transactions on %p\n", conn); - gsm0808_clear(conn->conn); - bsc_subscr_con_free(conn->conn); - conn->conn = NULL; - } - - /* send the clear complete message */ - resp = gsm0808_create_clear_complete(); - if (!resp) { - LOGP(DMSC, LOGL_ERROR, "Sending clear complete failed.\n"); - return -1; - } - - bsc_queue_for_msc(conn, resp); - return 0; -} - -/* - * GSM 08.08 § 3.4.7 cipher mode handling. We will have to pick - * the cipher to be used for this. In case we are already using - * a cipher we will have to send cipher mode reject to the MSC, - * otherwise we will have to pick something that we and the MS - * is supporting. Currently we are doing it in a rather static - * way by picking one ecnryption or no encrytpion. - */ -static int bssmap_handle_cipher_mode(struct osmo_bsc_sccp_con *conn, - struct msgb *msg, unsigned int payload_length) -{ - uint16_t len; - struct gsm_network *network = NULL; - const uint8_t *data; - struct tlv_parsed tp; - struct msgb *resp; - int reject_cause = -1; - int include_imeisv = 1; - - if (!conn->conn) { - LOGP(DMSC, LOGL_ERROR, "No lchan/msc_data in cipher mode command.\n"); - goto reject; - } - - if (conn->ciphering_handled) { - LOGP(DMSC, LOGL_ERROR, "Already seen ciphering command. Protocol Error.\n"); - goto reject; - } - - conn->ciphering_handled = 1; - - tlv_parse(&tp, gsm0808_att_tlvdef(), msg->l4h + 1, payload_length - 1, 0, 0); - if (!TLVP_PRESENT(&tp, GSM0808_IE_ENCRYPTION_INFORMATION)) { - LOGP(DMSC, LOGL_ERROR, "IE Encryption Information missing.\n"); - goto reject; - } - - /* - * check if our global setting is allowed - * - Currently we check for A5/0 and A5/1 - * - Copy the key if that is necessary - * - Otherwise reject - */ - len = TLVP_LEN(&tp, GSM0808_IE_ENCRYPTION_INFORMATION); - if (len < 1) { - LOGP(DMSC, LOGL_ERROR, "IE Encryption Information is too short.\n"); - goto reject; - } - - network = conn->conn->bts->network; - data = TLVP_VAL(&tp, GSM0808_IE_ENCRYPTION_INFORMATION); - - if (TLVP_PRESENT(&tp, GSM0808_IE_CIPHER_RESPONSE_MODE)) - include_imeisv = TLVP_VAL(&tp, GSM0808_IE_CIPHER_RESPONSE_MODE)[0] & 0x1; - - if (network->a5_encryption == 0 && (data[0] & 0x1) == 0x1) { - gsm0808_cipher_mode(conn->conn, 0, NULL, 0, include_imeisv); - } else if (network->a5_encryption != 0 && (data[0] & 0x2) == 0x2) { - gsm0808_cipher_mode(conn->conn, 1, &data[1], len - 1, include_imeisv); - } else { - LOGP(DMSC, LOGL_ERROR, "Can not select encryption...\n"); - goto reject; - } - - return 0; - -reject: - resp = gsm0808_create_cipher_reject(reject_cause); - if (!resp) { - LOGP(DMSC, LOGL_ERROR, "Sending the cipher reject failed.\n"); - return -1; - } - - bsc_queue_for_msc(conn, resp); - return -1; -} - -/* - * Handle the assignment request message. - * - * See §3.2.1.1 for the message type - */ -static int bssmap_handle_assignm_req(struct osmo_bsc_sccp_con *conn, - struct msgb *msg, unsigned int length) -{ - struct msgb *resp; - struct bsc_msc_data *msc; - struct tlv_parsed tp; - uint8_t *data; - uint8_t timeslot; - uint8_t multiplex; - enum gsm48_chan_mode chan_mode = GSM48_CMODE_SIGN; - int i, supported, port, full_rate = -1; - - if (!conn->conn) { - LOGP(DMSC, LOGL_ERROR, "No lchan/msc_data in cipher mode command.\n"); - return -1; - } - - tlv_parse(&tp, gsm0808_att_tlvdef(), msg->l4h + 1, length - 1, 0, 0); - - if (!TLVP_PRESENT(&tp, GSM0808_IE_CHANNEL_TYPE)) { - LOGP(DMSC, LOGL_ERROR, "Mandatory channel type not present.\n"); - goto reject; - } - - if (!TLVP_PRESENT(&tp, GSM0808_IE_CIRCUIT_IDENTITY_CODE)) { - LOGP(DMSC, LOGL_ERROR, "Identity code missing. Audio routing will not work.\n"); - goto reject; - } - - conn->cic = osmo_load16be(TLVP_VAL(&tp, GSM0808_IE_CIRCUIT_IDENTITY_CODE)); - timeslot = conn->cic & 0x1f; - multiplex = (conn->cic & ~0x1f) >> 5; - - /* - * Currently we only support a limited subset of all - * possible channel types. The limitation ends by not using - * multi-slot, limiting the channel coding, speech... - */ - if (TLVP_LEN(&tp, GSM0808_IE_CHANNEL_TYPE) < 3) { - LOGP(DMSC, LOGL_ERROR, "ChannelType len !=3 not supported: %d\n", - TLVP_LEN(&tp, GSM0808_IE_CHANNEL_TYPE)); - goto reject; - } - - /* - * Try to figure out if we support the proposed speech codecs. For - * now we will always pick the full rate codecs. - */ - - data = (uint8_t *) TLVP_VAL(&tp, GSM0808_IE_CHANNEL_TYPE); - if ((data[0] & 0xf) != 0x1) { - LOGP(DMSC, LOGL_ERROR, "ChannelType != speech: %d\n", data[0]); - goto reject; - } - - /* - * go through the list of preferred codecs of our gsm network - * and try to find it among the permitted codecs. If we found - * it we will send chan_mode to the right mode and break the - * inner loop. The outer loop will exit due chan_mode having - * the correct value. - */ - full_rate = 0; - msc = conn->msc; - for (supported = 0; - chan_mode == GSM48_CMODE_SIGN && supported < msc->audio_length; - ++supported) { - - int perm_val = audio_support_to_gsm88(msc->audio_support[supported]); - for (i = 2; i < TLVP_LEN(&tp, GSM0808_IE_CHANNEL_TYPE); ++i) { - if ((data[i] & 0x7f) == perm_val) { - chan_mode = gsm88_to_chan_mode(perm_val); - full_rate = (data[i] & 0x4) == 0; - break; - } else if ((data[i] & 0x80) == 0x00) { - break; - } - } - } - - if (chan_mode == GSM48_CMODE_SIGN) { - LOGP(DMSC, LOGL_ERROR, "No supported audio type found.\n"); - goto reject; - } - - /* map it to a MGCP Endpoint and a RTP port */ - port = mgcp_timeslot_to_endpoint(multiplex, timeslot); - conn->rtp_port = rtp_calculate_port(port, msc->rtp_base); - - return gsm0808_assign_req(conn->conn, chan_mode, full_rate); - -reject: - resp = gsm0808_create_assignment_failure(GSM0808_CAUSE_NO_RADIO_RESOURCE_AVAILABLE, NULL); - if (!resp) { - LOGP(DMSC, LOGL_ERROR, "Channel allocation failure.\n"); - return -1; - } - - bsc_queue_for_msc(conn, resp); - return -1; -} - -static int bssmap_rcvmsg_udt(struct bsc_msc_data *msc, - struct msgb *msg, unsigned int length) -{ - int ret = 0; - - if (length < 1) { - LOGP(DMSC, LOGL_ERROR, "Not enough room: %d\n", length); - return -1; - } - - LOGP(DMSC, LOGL_INFO, "Rx MSC UDT BSSMAP %s\n", - gsm0808_bssmap_name(msg->l4h[0])); - - switch (msg->l4h[0]) { - case BSS_MAP_MSG_RESET_ACKNOWLEDGE: - ret = bssmap_handle_reset_ack(msc, msg, length); - break; - case BSS_MAP_MSG_PAGING: - ret = bssmap_handle_paging(msc, msg, length); - break; - } - - return ret; -} - -static int bssmap_rcvmsg_dt1(struct osmo_bsc_sccp_con *conn, - struct msgb *msg, unsigned int length) -{ - int ret = 0; - - if (length < 1) { - LOGP(DMSC, LOGL_ERROR, "Not enough room: %d\n", length); - return -1; - } - - LOGP(DMSC, LOGL_INFO, "Rx MSC DT1 BSSMAP %s\n", - gsm0808_bssmap_name(msg->l4h[0])); - - switch (msg->l4h[0]) { - case BSS_MAP_MSG_CLEAR_CMD: - ret = bssmap_handle_clear_command(conn, msg, length); - break; - case BSS_MAP_MSG_CIPHER_MODE_CMD: - ret = bssmap_handle_cipher_mode(conn, msg, length); - break; - case BSS_MAP_MSG_ASSIGMENT_RQST: - ret = bssmap_handle_assignm_req(conn, msg, length); - break; - default: - LOGP(DMSC, LOGL_NOTICE, "Unimplemented msg type: %s\n", - gsm0808_bssmap_name(msg->l4h[0])); - break; - } - - return ret; -} - -static int dtap_rcvmsg(struct osmo_bsc_sccp_con *conn, - struct msgb *msg, unsigned int length) -{ - struct dtap_header *header; - struct msgb *gsm48; - uint8_t *data; - int rc, dtap_rc; - - LOGP(DMSC, LOGL_DEBUG, "Rx MSC DTAP: %s\n", - osmo_hexdump(msg->l3h, length)); - - if (!conn->conn) { - LOGP(DMSC, LOGL_ERROR, "No subscriber connection available\n"); - return -1; - } - - header = (struct dtap_header *) msg->l3h; - if (sizeof(*header) >= length) { - LOGP(DMSC, LOGL_ERROR, "The DTAP header does not fit. Wanted: %zu got: %u\n", sizeof(*header), length); - LOGP(DMSC, LOGL_ERROR, "hex: %s\n", osmo_hexdump(msg->l3h, length)); - return -1; - } - - if (header->length > length - sizeof(*header)) { - LOGP(DMSC, LOGL_ERROR, "The DTAP l4 information does not fit: header: %u length: %u\n", header->length, length); - LOGP(DMSC, LOGL_ERROR, "hex: %s\n", osmo_hexdump(msg->l3h, length)); - return -1; - } - - LOGP(DMSC, LOGL_INFO, "Rx MSC DTAP, SAPI: %u CHAN: %u\n", header->link_id & 0x07, header->link_id & 0xC0); - - /* forward the data */ - gsm48 = gsm48_msgb_alloc_name("GSM 04.08 DTAP RCV"); - if (!gsm48) { - LOGP(DMSC, LOGL_ERROR, "Allocation of the message failed.\n"); - return -1; - } - - gsm48->l3h = gsm48->data; - data = msgb_put(gsm48, length - sizeof(*header)); - memcpy(data, msg->l3h + sizeof(*header), length - sizeof(*header)); - - /* pass it to the filter for extra actions */ - rc = bsc_scan_msc_msg(conn->conn, gsm48); - dtap_rc = gsm0808_submit_dtap(conn->conn, gsm48, header->link_id, 1); - if (rc == BSS_SEND_USSD) - bsc_send_welcome_ussd(conn->conn); - return dtap_rc; -} - -int bsc_handle_udt(struct bsc_msc_data *msc, - struct msgb *msgb, unsigned int length) -{ - struct bssmap_header *bs; - - LOGP(DMSC, LOGL_DEBUG, "Rx MSC UDT: %s\n", - osmo_hexdump(msgb->l3h, length)); - - if (length < sizeof(*bs)) { - LOGP(DMSC, LOGL_ERROR, "The header is too short.\n"); - return -1; - } - - bs = (struct bssmap_header *) msgb->l3h; - if (bs->length < length - sizeof(*bs)) - return -1; - - switch (bs->type) { - case BSSAP_MSG_BSS_MANAGEMENT: - msgb->l4h = &msgb->l3h[sizeof(*bs)]; - bssmap_rcvmsg_udt(msc, msgb, length - sizeof(*bs)); - break; - default: - LOGP(DMSC, LOGL_NOTICE, "Unimplemented msg type: %s\n", - gsm0808_bssmap_name(bs->type)); - } - - return 0; -} - -int bsc_handle_dt1(struct osmo_bsc_sccp_con *conn, - struct msgb *msg, unsigned int len) -{ - if (len < sizeof(struct bssmap_header)) { - LOGP(DMSC, LOGL_ERROR, "The header is too short.\n"); - } - - switch (msg->l3h[0]) { - case BSSAP_MSG_BSS_MANAGEMENT: - msg->l4h = &msg->l3h[sizeof(struct bssmap_header)]; - bssmap_rcvmsg_dt1(conn, msg, len - sizeof(struct bssmap_header)); - break; - case BSSAP_MSG_DTAP: - dtap_rcvmsg(conn, msg, len); - break; - default: - LOGP(DMSC, LOGL_NOTICE, "Unimplemented BSSAP msg type: %s\n", - gsm0808_bssap_name(msg->l3h[0])); - } - - return -1; -} diff --git a/openbsc/src/osmo-bsc/osmo_bsc_ctrl.c b/openbsc/src/osmo-bsc/osmo_bsc_ctrl.c deleted file mode 100644 index c23ed218..00000000 --- a/openbsc/src/osmo-bsc/osmo_bsc_ctrl.c +++ /dev/null @@ -1,680 +0,0 @@ -/* (C) 2011 by Daniel Willmann - * (C) 2011 by Holger Hans Peter Freyther - * (C) 2011 by On-Waves - * All Rights Reserved - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation; either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - * - */ - -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include - -#include -#include -#include -#include - -void osmo_bsc_send_trap(struct ctrl_cmd *cmd, struct bsc_msc_connection *msc_con) -{ - struct ctrl_cmd *trap; - struct ctrl_handle *ctrl; - struct bsc_msc_data *msc_data; - - msc_data = (struct bsc_msc_data *) msc_con->write_queue.bfd.data; - ctrl = msc_data->network->ctrl; - - trap = ctrl_cmd_trap(cmd); - if (!trap) { - LOGP(DCTRL, LOGL_ERROR, "Failed to create trap.\n"); - return; - } - - ctrl_cmd_send_to_all(ctrl, trap); - ctrl_cmd_send(&msc_con->write_queue, trap); - - talloc_free(trap); -} - -CTRL_CMD_DEFINE_RO(msc_connection_status, "msc_connection_status"); -static int msc_connection_status = 0; - -static int get_msc_connection_status(struct ctrl_cmd *cmd, void *data) -{ - if (msc_connection_status) - cmd->reply = "connected"; - else - cmd->reply = "disconnected"; - return CTRL_CMD_REPLY; -} - -static int msc_connection_status_trap_cb(unsigned int subsys, unsigned int signal, void *handler_data, void *signal_data) -{ - struct ctrl_cmd *cmd; - struct gsm_network *gsmnet = (struct gsm_network *)handler_data; - - if (signal == S_MSC_LOST && msc_connection_status == 1) { - LOGP(DCTRL, LOGL_DEBUG, "MSC connection lost, sending TRAP.\n"); - msc_connection_status = 0; - } else if (signal == S_MSC_CONNECTED && msc_connection_status == 0) { - LOGP(DCTRL, LOGL_DEBUG, "MSC connection (re)established, sending TRAP.\n"); - msc_connection_status = 1; - } else { - return 0; - } - - cmd = ctrl_cmd_create(tall_bsc_ctx, CTRL_TYPE_TRAP); - if (!cmd) { - LOGP(DCTRL, LOGL_ERROR, "Trap creation failed.\n"); - return 0; - } - - cmd->id = "0"; - cmd->variable = "msc_connection_status"; - - get_msc_connection_status(cmd, NULL); - - ctrl_cmd_send_to_all(gsmnet->ctrl, cmd); - - talloc_free(cmd); - - return 0; -} - -CTRL_CMD_DEFINE_RO(bts_connection_status, "bts_connection_status"); -static int bts_connection_status = 0; - -static int get_bts_connection_status(struct ctrl_cmd *cmd, void *data) -{ - if (bts_connection_status) - cmd->reply = "connected"; - else - cmd->reply = "disconnected"; - return CTRL_CMD_REPLY; -} - -static int bts_connection_status_trap_cb(unsigned int subsys, unsigned int signal, void *handler_data, void *signal_data) -{ - struct ctrl_cmd *cmd; - struct gsm_network *gsmnet = (struct gsm_network *)handler_data; - struct gsm_bts *bts; - int bts_current_status; - - if (signal != S_L_INP_TEI_DN && signal != S_L_INP_TEI_UP) { - return 0; - } - - bts_current_status = 0; - /* Check if OML on at least one BTS is up */ - llist_for_each_entry(bts, &gsmnet->bts_list, list) { - if (bts->oml_link) { - bts_current_status = 1; - break; - } - } - if (bts_connection_status == 0 && bts_current_status == 1) { - LOGP(DCTRL, LOGL_DEBUG, "BTS connection (re)established, sending TRAP.\n"); - } else if (bts_connection_status == 1 && bts_current_status == 0) { - LOGP(DCTRL, LOGL_DEBUG, "No more BTS connected, sending TRAP.\n"); - } else { - return 0; - } - - cmd = ctrl_cmd_create(tall_bsc_ctx, CTRL_TYPE_TRAP); - if (!cmd) { - LOGP(DCTRL, LOGL_ERROR, "Trap creation failed.\n"); - return 0; - } - - bts_connection_status = bts_current_status; - - cmd->id = "0"; - cmd->variable = "bts_connection_status"; - - get_bts_connection_status(cmd, NULL); - - ctrl_cmd_send_to_all(gsmnet->ctrl, cmd); - - talloc_free(cmd); - - return 0; -} - -static int get_bts_loc(struct ctrl_cmd *cmd, void *data); - -static void generate_location_state_trap(struct gsm_bts *bts, struct bsc_msc_connection *msc_con) -{ - struct ctrl_cmd *cmd; - const char *oper, *admin, *policy; - - cmd = ctrl_cmd_create(msc_con, CTRL_TYPE_TRAP); - if (!cmd) { - LOGP(DCTRL, LOGL_ERROR, "Failed to create TRAP command.\n"); - return; - } - - cmd->id = "0"; - cmd->variable = talloc_asprintf(cmd, "bts.%i.location-state", bts->nr); - - /* Prepare the location reply */ - cmd->node = bts; - get_bts_loc(cmd, NULL); - - oper = osmo_bsc_rf_get_opstate_name(osmo_bsc_rf_get_opstate_by_bts(bts)); - admin = osmo_bsc_rf_get_adminstate_name(osmo_bsc_rf_get_adminstate_by_bts(bts)); - policy = osmo_bsc_rf_get_policy_name(osmo_bsc_rf_get_policy_by_bts(bts)); - - cmd->reply = talloc_asprintf_append(cmd->reply, - ",%s,%s,%s,%d,%d", - oper, admin, policy, - bts->network->country_code, - bts->network->network_code); - - osmo_bsc_send_trap(cmd, msc_con); - talloc_free(cmd); -} - -void bsc_gen_location_state_trap(struct gsm_bts *bts) -{ - struct bsc_msc_data *msc; - - llist_for_each_entry(msc, &bts->network->bsc_data->mscs, entry) - generate_location_state_trap(bts, msc->msc_con); -} - -static int location_equal(struct bts_location *a, struct bts_location *b) -{ - return ((a->tstamp == b->tstamp) && (a->valid == b->valid) && (a->lat == b->lat) && - (a->lon == b->lon) && (a->height == b->height)); -} - -static void cleanup_locations(struct llist_head *locations) -{ - struct bts_location *myloc, *tmp; - int invalpos = 0, i = 0; - - LOGP(DCTRL, LOGL_DEBUG, "Checking position list.\n"); - llist_for_each_entry_safe(myloc, tmp, locations, list) { - i++; - if (i > 3) { - LOGP(DCTRL, LOGL_DEBUG, "Deleting old position.\n"); - llist_del(&myloc->list); - talloc_free(myloc); - } else if (myloc->valid == BTS_LOC_FIX_INVALID) { - /* Only capture the newest of subsequent invalid positions */ - invalpos++; - if (invalpos > 1) { - LOGP(DCTRL, LOGL_DEBUG, "Deleting subsequent invalid position.\n"); - invalpos--; - i--; - llist_del(&myloc->list); - talloc_free(myloc); - } - } else { - invalpos = 0; - } - } - LOGP(DCTRL, LOGL_DEBUG, "Found %i positions.\n", i); -} - -CTRL_CMD_DEFINE(bts_loc, "location"); -static int get_bts_loc(struct ctrl_cmd *cmd, void *data) -{ - struct bts_location *curloc; - struct gsm_bts *bts = (struct gsm_bts *) cmd->node; - if (!bts) { - cmd->reply = "bts not found."; - return CTRL_CMD_ERROR; - } - - if (llist_empty(&bts->loc_list)) { - cmd->reply = talloc_asprintf(cmd, "0,invalid,0,0,0"); - return CTRL_CMD_REPLY; - } else { - curloc = llist_entry(bts->loc_list.next, struct bts_location, list); - } - - cmd->reply = talloc_asprintf(cmd, "%lu,%s,%f,%f,%f", curloc->tstamp, - get_value_string(bts_loc_fix_names, curloc->valid), curloc->lat, curloc->lon, curloc->height); - if (!cmd->reply) { - cmd->reply = "OOM"; - return CTRL_CMD_ERROR; - } - - return CTRL_CMD_REPLY; -} - -static int set_bts_loc(struct ctrl_cmd *cmd, void *data) -{ - char *saveptr, *lat, *lon, *height, *tstamp, *valid, *tmp; - struct bts_location *curloc, *lastloc; - int ret; - struct gsm_bts *bts = (struct gsm_bts *) cmd->node; - if (!bts) { - cmd->reply = "bts not found."; - return CTRL_CMD_ERROR; - } - - tmp = talloc_strdup(cmd, cmd->value); - if (!tmp) - goto oom; - - curloc = talloc_zero(tall_bsc_ctx, struct bts_location); - if (!curloc) { - talloc_free(tmp); - goto oom; - } - INIT_LLIST_HEAD(&curloc->list); - - - tstamp = strtok_r(tmp, ",", &saveptr); - valid = strtok_r(NULL, ",", &saveptr); - lat = strtok_r(NULL, ",", &saveptr); - lon = strtok_r(NULL, ",", &saveptr); - height = strtok_r(NULL, "\0", &saveptr); - - curloc->tstamp = atol(tstamp); - curloc->valid = get_string_value(bts_loc_fix_names, valid); - curloc->lat = atof(lat); - curloc->lon = atof(lon); - curloc->height = atof(height); - talloc_free(tmp); - - lastloc = llist_entry(bts->loc_list.next, struct bts_location, list); - - /* Add location to the end of the list */ - llist_add(&curloc->list, &bts->loc_list); - - ret = get_bts_loc(cmd, data); - - if (!location_equal(curloc, lastloc)) - bsc_gen_location_state_trap(bts); - - cleanup_locations(&bts->loc_list); - - return ret; - -oom: - cmd->reply = "OOM"; - return CTRL_CMD_ERROR; -} - -static int verify_bts_loc(struct ctrl_cmd *cmd, const char *value, void *data) -{ - char *saveptr, *latstr, *lonstr, *heightstr, *tstampstr, *validstr, *tmp; - time_t tstamp; - int valid; - double lat, lon, height __attribute__((unused)); - - tmp = talloc_strdup(cmd, value); - if (!tmp) - return 1; - - tstampstr = strtok_r(tmp, ",", &saveptr); - validstr = strtok_r(NULL, ",", &saveptr); - latstr = strtok_r(NULL, ",", &saveptr); - lonstr = strtok_r(NULL, ",", &saveptr); - heightstr = strtok_r(NULL, "\0", &saveptr); - - if ((tstampstr == NULL) || (validstr == NULL) || (latstr == NULL) || - (lonstr == NULL) || (heightstr == NULL)) - goto err; - - tstamp = atol(tstampstr); - valid = get_string_value(bts_loc_fix_names, validstr); - lat = atof(latstr); - lon = atof(lonstr); - height = atof(heightstr); - talloc_free(tmp); - tmp = NULL; - - if (((tstamp == 0) && (valid != BTS_LOC_FIX_INVALID)) || (lat < -90) || (lat > 90) || - (lon < -180) || (lon > 180) || (valid < 0)) { - goto err; - } - - return 0; - -err: - talloc_free(tmp); - cmd->reply = talloc_strdup(cmd, "The format is ,(invalid|fix2d|fix3d),,,"); - return 1; -} - -CTRL_CMD_DEFINE(net_timezone, "timezone"); -static int get_net_timezone(struct ctrl_cmd *cmd, void *data) -{ - struct gsm_network *net = (struct gsm_network*)cmd->node; - - struct gsm_tz *tz = &net->tz; - if (tz->override) - cmd->reply = talloc_asprintf(cmd, "%d,%d,%d", - tz->hr, tz->mn, tz->dst); - else - cmd->reply = talloc_asprintf(cmd, "off"); - - if (!cmd->reply) { - cmd->reply = "OOM"; - return CTRL_CMD_ERROR; - } - - return CTRL_CMD_REPLY; -} - -static int set_net_timezone(struct ctrl_cmd *cmd, void *data) -{ - char *saveptr, *hourstr, *minstr, *dststr, *tmp = 0; - int override; - struct gsm_network *net = (struct gsm_network*)cmd->node; - - tmp = talloc_strdup(cmd, cmd->value); - if (!tmp) - goto oom; - - hourstr = strtok_r(tmp, ",", &saveptr); - minstr = strtok_r(NULL, ",", &saveptr); - dststr = strtok_r(NULL, ",", &saveptr); - - override = 0; - - if (hourstr != NULL) - override = strcasecmp(hourstr, "off") != 0; - - struct gsm_tz *tz = &net->tz; - tz->override = override; - - if (override) { - tz->hr = hourstr ? atol(hourstr) : 0; - tz->mn = minstr ? atol(minstr) : 0; - tz->dst = dststr ? atol(dststr) : 0; - } - - talloc_free(tmp); - tmp = NULL; - - return get_net_timezone(cmd, data); - -oom: - cmd->reply = "OOM"; - return CTRL_CMD_ERROR; -} - -static int verify_net_timezone(struct ctrl_cmd *cmd, const char *value, void *data) -{ - char *saveptr, *hourstr, *minstr, *dststr, *tmp; - int override, tz_hours, tz_mins, tz_dst; - - tmp = talloc_strdup(cmd, value); - if (!tmp) - return 1; - - hourstr = strtok_r(tmp, ",", &saveptr); - minstr = strtok_r(NULL, ",", &saveptr); - dststr = strtok_r(NULL, ",", &saveptr); - - if (hourstr == NULL) - goto err; - - override = strcasecmp(hourstr, "off") != 0; - - if (!override) { - talloc_free(tmp); - return 0; - } - - if (minstr == NULL || dststr == NULL) - goto err; - - tz_hours = atol(hourstr); - tz_mins = atol(minstr); - tz_dst = atol(dststr); - - talloc_free(tmp); - tmp = NULL; - - if ((tz_hours < -19) || (tz_hours > 19) || - (tz_mins < 0) || (tz_mins >= 60) || (tz_mins % 15 != 0) || - (tz_dst < 0) || (tz_dst > 2)) - goto err; - - return 0; - -err: - talloc_free(tmp); - cmd->reply = talloc_strdup(cmd, "The format is ,, or 'off' where -19 <= hours <= 19, mins in {0, 15, 30, 45}, and 0 <= dst <= 2"); - return 1; -} - -CTRL_CMD_DEFINE(net_notification, "notification"); -static int get_net_notification(struct ctrl_cmd *cmd, void *data) -{ - cmd->reply = "There is nothing to read"; - return CTRL_CMD_ERROR; -} - -static int set_net_notification(struct ctrl_cmd *cmd, void *data) -{ - struct ctrl_cmd *trap; - struct gsm_network *net; - - net = cmd->node; - - trap = ctrl_cmd_create(tall_bsc_ctx, CTRL_TYPE_TRAP); - if (!trap) { - LOGP(DCTRL, LOGL_ERROR, "Trap creation failed\n"); - goto handled; - } - - trap->id = "0"; - trap->variable = "notification"; - trap->reply = talloc_strdup(trap, cmd->value); - - /* - * This should only be sent to local systems. In the future - * we might even ask for systems to register to receive - * the notifications. - */ - ctrl_cmd_send_to_all(net->ctrl, trap); - talloc_free(trap); - -handled: - return CTRL_CMD_HANDLED; -} - -static int verify_net_notification(struct ctrl_cmd *cmd, const char *value, void *data) -{ - return 0; -} - -CTRL_CMD_DEFINE(net_inform_msc, "inform-msc-v1"); -static int get_net_inform_msc(struct ctrl_cmd *cmd, void *data) -{ - cmd->reply = "There is nothing to read"; - return CTRL_CMD_ERROR; -} - -static int set_net_inform_msc(struct ctrl_cmd *cmd, void *data) -{ - struct gsm_network *net; - struct bsc_msc_data *msc; - - net = cmd->node; - llist_for_each_entry(msc, &net->bsc_data->mscs, entry) { - struct ctrl_cmd *trap; - - trap = ctrl_cmd_create(tall_bsc_ctx, CTRL_TYPE_TRAP); - if (!trap) { - LOGP(DCTRL, LOGL_ERROR, "Trap creation failed\n"); - continue; - } - - trap->id = "0"; - trap->variable = "inform-msc-v1"; - trap->reply = talloc_strdup(trap, cmd->value); - ctrl_cmd_send(&msc->msc_con->write_queue, trap); - talloc_free(trap); - } - - - return CTRL_CMD_HANDLED; -} - -static int verify_net_inform_msc(struct ctrl_cmd *cmd, const char *value, void *data) -{ - return 0; -} - -CTRL_CMD_DEFINE(net_ussd_notify, "ussd-notify-v1"); -static int get_net_ussd_notify(struct ctrl_cmd *cmd, void *data) -{ - cmd->reply = "There is nothing to read"; - return CTRL_CMD_ERROR; -} - -static int set_net_ussd_notify(struct ctrl_cmd *cmd, void *data) -{ - struct gsm_subscriber_connection *conn; - struct gsm_network *net; - char *saveptr = NULL; - char *cic_str, *alert_str, *text_str; - int cic, alert; - - /* Verify has done the test for us */ - cic_str = strtok_r(cmd->value, ",", &saveptr); - alert_str = strtok_r(NULL, ",", &saveptr); - text_str = strtok_r(NULL, ",", &saveptr); - - if (!cic_str || !alert_str || !text_str) { - cmd->reply = "Programming issue. How did this pass verify?"; - return CTRL_CMD_ERROR; - } - - cmd->reply = "No connection found"; - - cic = atoi(cic_str); - alert = atoi(alert_str); - - net = cmd->node; - llist_for_each_entry(conn, &net->subscr_conns, entry) { - if (!conn->sccp_con) - continue; - - if (conn->sccp_con->cic != cic) - continue; - - /* - * This is a hack. My E71 does not like to immediately - * receive a release complete on a TCH. So schedule a - * release complete to clear any previous attempt. The - * right thing would be to track invokeId and only send - * the release complete when we get a returnResultLast - * for this invoke id. - */ - bsc_send_ussd_release_complete(conn); - bsc_send_ussd_notify(conn, alert, text_str); - cmd->reply = "Found a connection"; - break; - } - - return CTRL_CMD_REPLY; -} - -static int verify_net_ussd_notify(struct ctrl_cmd *cmd, const char *value, void *data) -{ - char *saveptr = NULL; - char *inp, *cic, *alert, *text; - - OSMO_ASSERT(cmd); - inp = talloc_strdup(cmd, value); - - cic = strtok_r(inp, ",", &saveptr); - alert = strtok_r(NULL, ",", &saveptr); - text = strtok_r(NULL, ",", &saveptr); - - talloc_free(inp); - if (!cic || !alert || !text) - return 1; - return 0; -} - -static int msc_signal_handler(unsigned int subsys, unsigned int signal, - void *handler_data, void *signal_data) -{ - struct msc_signal_data *msc; - struct gsm_network *net; - struct gsm_bts *bts; - - if (subsys != SS_MSC) - return 0; - if (signal != S_MSC_AUTHENTICATED) - return 0; - - msc = signal_data; - - net = msc->data->network; - llist_for_each_entry(bts, &net->bts_list, list) - generate_location_state_trap(bts, msc->data->msc_con); - - return 0; -} - -int bsc_ctrl_cmds_install(struct gsm_network *net) -{ - int rc; - - rc = bsc_base_ctrl_cmds_install(); - if (rc) - goto end; - rc = ctrl_cmd_install(CTRL_NODE_BTS, &cmd_bts_loc); - if (rc) - goto end; - rc = ctrl_cmd_install(CTRL_NODE_ROOT, &cmd_net_timezone); - if (rc) - goto end; - rc = ctrl_cmd_install(CTRL_NODE_ROOT, &cmd_msc_connection_status); - if (rc) - goto end; - rc = osmo_signal_register_handler(SS_MSC, &msc_connection_status_trap_cb, net); - if (rc) - goto end; - rc = osmo_signal_register_handler(SS_MSC, msc_signal_handler, NULL); - if (rc) - goto end; - rc = ctrl_cmd_install(CTRL_NODE_ROOT, &cmd_bts_connection_status); - if (rc) - goto end; - rc = ctrl_cmd_install(CTRL_NODE_ROOT, &cmd_net_notification); - if (rc) - goto end; - rc = ctrl_cmd_install(CTRL_NODE_ROOT, &cmd_net_inform_msc); - if (rc) - goto end; - rc = ctrl_cmd_install(CTRL_NODE_ROOT, &cmd_net_ussd_notify); - if (rc) - goto end; - rc = osmo_signal_register_handler(SS_L_INPUT, &bts_connection_status_trap_cb, net); - -end: - return rc; -} diff --git a/openbsc/src/osmo-bsc/osmo_bsc_filter.c b/openbsc/src/osmo-bsc/osmo_bsc_filter.c deleted file mode 100644 index 2c84b169..00000000 --- a/openbsc/src/osmo-bsc/osmo_bsc_filter.c +++ /dev/null @@ -1,381 +0,0 @@ -/* (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 - -static void handle_lu_request(struct gsm_subscriber_connection *conn, - struct msgb *msg) -{ - struct gsm48_hdr *gh; - struct gsm48_loc_upd_req *lu; - struct gsm48_loc_area_id lai; - struct gsm_network *net; - - if (msgb_l3len(msg) < sizeof(*gh) + sizeof(*lu)) { - LOGP(DMSC, LOGL_ERROR, "LU too small to look at: %u\n", msgb_l3len(msg)); - return; - } - - net = conn->bts->network; - - gh = msgb_l3(msg); - lu = (struct gsm48_loc_upd_req *) gh->data; - - gsm48_generate_lai(&lai, net->country_code, net->network_code, - conn->bts->location_area_code); - - if (memcmp(&lai, &lu->lai, sizeof(lai)) != 0) { - LOGP(DMSC, LOGL_DEBUG, "Marking con for welcome USSD.\n"); - conn->sccp_con->new_subscriber = 1; - } -} - -/* extract a subscriber from the paging response */ -static struct bsc_subscr *extract_sub(struct gsm_subscriber_connection *conn, - struct msgb *msg) -{ - uint8_t mi_type; - char mi_string[GSM48_MI_SIZE]; - struct gsm48_hdr *gh; - struct gsm48_pag_resp *resp; - struct bsc_subscr *subscr; - - if (msgb_l3len(msg) < sizeof(*gh) + sizeof(*resp)) { - LOGP(DMSC, LOGL_ERROR, "PagingResponse too small: %u\n", msgb_l3len(msg)); - return NULL; - } - - gh = msgb_l3(msg); - resp = (struct gsm48_pag_resp *) &gh->data[0]; - - gsm48_paging_extract_mi(resp, msgb_l3len(msg) - sizeof(*gh), - mi_string, &mi_type); - DEBUGP(DRR, "PAGING RESPONSE: MI(%s)=%s\n", - gsm48_mi_type_name(mi_type), mi_string); - - switch (mi_type) { - case GSM_MI_TYPE_TMSI: - subscr = bsc_subscr_find_by_tmsi(conn->network->bsc_subscribers, - tmsi_from_string(mi_string)); - break; - case GSM_MI_TYPE_IMSI: - subscr = bsc_subscr_find_by_imsi(conn->network->bsc_subscribers, - mi_string); - break; - default: - subscr = NULL; - break; - } - - return subscr; -} - -/* we will need to stop the paging request */ -static int handle_page_resp(struct gsm_subscriber_connection *conn, struct msgb *msg) -{ - struct bsc_subscr *subscr = extract_sub(conn, msg); - - if (!subscr) { - LOGP(DMSC, LOGL_ERROR, "Non active subscriber got paged.\n"); - return -1; - } - - paging_request_stop(&conn->network->bts_list, conn->bts, subscr, conn, - msg); - bsc_subscr_put(subscr); - return 0; -} - -static int is_cm_service_for_emerg(struct msgb *msg) -{ - struct gsm48_service_request *cm; - struct gsm48_hdr *gh = msgb_l3(msg); - - if (msgb_l3len(msg) < sizeof(*gh) + sizeof(*cm)) { - LOGP(DMSC, LOGL_ERROR, "CM ServiceRequest does not fit.\n"); - return 0; - } - - cm = (struct gsm48_service_request *) &gh->data[0]; - return cm->cm_service_type == GSM48_CMSERV_EMERGENCY; -} - -struct bsc_msc_data *bsc_find_msc(struct gsm_subscriber_connection *conn, - struct msgb *msg) -{ - struct gsm48_hdr *gh; - int8_t pdisc; - uint8_t mtype; - struct osmo_bsc_data *bsc; - struct bsc_msc_data *msc, *pag_msc; - struct bsc_subscr *subscr; - int is_emerg = 0; - - bsc = conn->bts->network->bsc_data; - - if (msgb_l3len(msg) < sizeof(*gh)) { - LOGP(DMSC, LOGL_ERROR, "There is no GSM48 header here.\n"); - return NULL; - } - - gh = msgb_l3(msg); - pdisc = gsm48_hdr_pdisc(gh); - mtype = gsm48_hdr_msg_type(gh); - - /* - * We are asked to select a MSC here but they are not equal. We - * want to respond to a paging request on the MSC where we got the - * request from. This is where we need to decide where this connection - * will go. - */ - if (pdisc == GSM48_PDISC_RR && mtype == GSM48_MT_RR_PAG_RESP) - goto paging; - else if (pdisc == GSM48_PDISC_MM && mtype == GSM48_MT_MM_CM_SERV_REQ) { - is_emerg = is_cm_service_for_emerg(msg); - goto round_robin; - } else - goto round_robin; - -round_robin: - llist_for_each_entry(msc, &bsc->mscs, entry) { - if (!msc->msc_con->is_authenticated) - continue; - if (!is_emerg && msc->type != MSC_CON_TYPE_NORMAL) - continue; - if (is_emerg && !msc->allow_emerg) - continue; - - /* force round robin by moving it to the end */ - llist_move_tail(&msc->entry, &bsc->mscs); - return msc; - } - - return NULL; - -paging: - subscr = extract_sub(conn, msg); - - if (!subscr) { - LOGP(DMSC, LOGL_ERROR, "Got paged but no subscriber found.\n"); - return NULL; - } - - pag_msc = paging_get_data(conn->bts, subscr); - bsc_subscr_put(subscr); - - llist_for_each_entry(msc, &bsc->mscs, entry) { - if (msc != pag_msc) - continue; - - /* - * We don't check if the MSC is connected. In case it - * is not the connection will be dropped. - */ - - /* force round robin by moving it to the end */ - llist_move_tail(&msc->entry, &bsc->mscs); - return msc; - } - - LOGP(DMSC, LOGL_ERROR, "Got paged but no request found.\n"); - return NULL; -} - - -/** - * This is used to scan a message for extra functionality of the BSC. This - * includes scanning for location updating requests/acceptd and then send - * a welcome USSD message to the subscriber. - */ -int bsc_scan_bts_msg(struct gsm_subscriber_connection *conn, struct msgb *msg) -{ - struct gsm48_hdr *gh = msgb_l3(msg); - uint8_t pdisc = gsm48_hdr_pdisc(gh); - uint8_t mtype = gsm48_hdr_msg_type(gh); - - if (pdisc == GSM48_PDISC_MM) { - if (mtype == GSM48_MT_MM_LOC_UPD_REQUEST) - handle_lu_request(conn, msg); - } else if (pdisc == GSM48_PDISC_RR) { - if (mtype == GSM48_MT_RR_PAG_RESP) - handle_page_resp(conn, msg); - } - - return 0; -} - -static int send_welcome_ussd(struct gsm_subscriber_connection *conn) -{ - struct osmo_bsc_sccp_con *bsc_con; - - bsc_con = conn->sccp_con; - if (!bsc_con) { - LOGP(DMSC, LOGL_DEBUG, "No SCCP connection associated.\n"); - return 0; - } - - if (!bsc_con->msc->ussd_welcome_txt) { - LOGP(DMSC, LOGL_DEBUG, "No USSD Welcome text defined.\n"); - return 0; - } - - return BSS_SEND_USSD; -} - -int bsc_send_welcome_ussd(struct gsm_subscriber_connection *conn) -{ - bsc_send_ussd_notify(conn, 1, conn->sccp_con->msc->ussd_welcome_txt); - bsc_send_ussd_release_complete(conn); - - return 0; -} - -static int bsc_patch_mm_info(struct gsm_subscriber_connection *conn, - uint8_t *data, unsigned int length) -{ - struct tlv_parsed tp; - int parse_res; - struct gsm_bts *bts = conn->bts; - int tzunits; - uint8_t tzbsd = 0; - uint8_t dst = 0; - - parse_res = tlv_parse(&tp, &gsm48_mm_att_tlvdef, data, length, 0, 0); - if (parse_res <= 0 && parse_res != -3) - /* FIXME: -3 means unknown IE error, so this accepts messages - * with unknown IEs. But parsing has aborted with the unknown - * IE and the message is broken or parsed incompletely. */ - return 0; - - /* Is TZ patching enabled? */ - struct gsm_tz *tz = &bts->network->tz; - if (!tz->override) - return 0; - - /* Convert tz.hr and tz.mn to units */ - if (tz->hr < 0) { - tzunits = -tz->hr*4; - tzbsd |= 0x08; - } else - tzunits = tz->hr*4; - - tzunits = tzunits + (tz->mn/15); - - tzbsd |= (tzunits % 10)*0x10 + (tzunits / 10); - - /* Convert DST value */ - if (tz->dst >= 0 && tz->dst <= 2) - dst = tz->dst; - - if (TLVP_PRESENT(&tp, GSM48_IE_UTC)) { - LOGP(DMSC, LOGL_DEBUG, - "Changing 'Local time zone' from 0x%02x to 0x%02x.\n", - TLVP_VAL(&tp, GSM48_IE_UTC)[6], tzbsd); - ((uint8_t *)(TLVP_VAL(&tp, GSM48_IE_UTC)))[0] = tzbsd; - } - if (TLVP_PRESENT(&tp, GSM48_IE_NET_TIME_TZ)) { - LOGP(DMSC, LOGL_DEBUG, - "Changing 'Universal time and local time zone' TZ from " - "0x%02x to 0x%02x.\n", - TLVP_VAL(&tp, GSM48_IE_NET_TIME_TZ)[6], tzbsd); - ((uint8_t *)(TLVP_VAL(&tp, GSM48_IE_NET_TIME_TZ)))[6] = tzbsd; - } -#ifdef GSM48_IE_NET_DST - if (TLVP_PRESENT(&tp, GSM48_IE_NET_DST)) { - LOGP(DMSC, LOGL_DEBUG, - "Changing 'Network daylight saving time' from " - "0x%02x to 0x%02x.\n", - TLVP_VAL(&tp, GSM48_IE_NET_DST)[0], dst); - ((uint8_t *)(TLVP_VAL(&tp, GSM48_IE_NET_DST)))[0] = dst; - } -#endif - - return 0; -} - -static int has_core_identity(struct bsc_msc_data *msc) -{ - if (msc->core_mnc != -1) - return 1; - if (msc->core_mcc != -1) - return 1; - if (msc->core_lac != -1) - return 1; - if (msc->core_ci != -1) - return 1; - return 0; -} - -/** - * Messages coming back from the MSC. - */ -int bsc_scan_msc_msg(struct gsm_subscriber_connection *conn, struct msgb *msg) -{ - struct bsc_msc_data *msc; - struct gsm_network *net; - struct gsm48_loc_area_id *lai; - struct gsm48_hdr *gh; - uint8_t pdisc; - uint8_t mtype; - int length = msgb_l3len(msg); - - if (length < sizeof(*gh)) { - LOGP(DMSC, LOGL_ERROR, "GSM48 header does not fit.\n"); - return -1; - } - - gh = (struct gsm48_hdr *) msgb_l3(msg); - length -= (const char *)&gh->data[0] - (const char *)gh; - - pdisc = gsm48_hdr_pdisc(gh); - if (pdisc != GSM48_PDISC_MM) - return 0; - - mtype = gsm48_hdr_msg_type(gh); - net = conn->bts->network; - msc = conn->sccp_con->msc; - - if (mtype == GSM48_MT_MM_LOC_UPD_ACCEPT) { - if (has_core_identity(msc)) { - if (msgb_l3len(msg) >= sizeof(*gh) + sizeof(*lai)) { - /* overwrite LAI in the message */ - lai = (struct gsm48_loc_area_id *) &gh->data[0]; - gsm48_generate_lai(lai, net->country_code, - net->network_code, - conn->bts->location_area_code); - } - } - - if (conn->sccp_con->new_subscriber) - return send_welcome_ussd(conn); - return 0; - } else if (mtype == GSM48_MT_MM_INFO) { - bsc_patch_mm_info(conn, &gh->data[0], length); - } - - return 0; -} diff --git a/openbsc/src/osmo-bsc/osmo_bsc_grace.c b/openbsc/src/osmo-bsc/osmo_bsc_grace.c deleted file mode 100644 index 63afa20d..00000000 --- a/openbsc/src/osmo-bsc/osmo_bsc_grace.c +++ /dev/null @@ -1,169 +0,0 @@ -/* - * (C) 2010-2013 by Holger Hans Peter Freyther - * (C) 2010-2013 by On-Waves - * All Rights Reserved - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation; either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - * - */ - -#include -#include -#include -#include -#include -#include -#include - -int bsc_grace_allow_new_connection(struct gsm_network *network, struct gsm_bts *bts) -{ - if (bts->excl_from_rf_lock) - return 1; - return network->bsc_data->rf_ctrl->policy == S_RF_ON; -} - - -static int normal_paging(struct bsc_subscr *subscr, int chan_needed, - struct bsc_msc_data *msc) -{ - /* we can't page by lac.. we need to page everything */ - if (msc->core_lac != -1) { - struct gsm_bts *bts; - - llist_for_each_entry(bts, &msc->network->bts_list, list) - paging_request_bts(bts, subscr, chan_needed, NULL, msc); - - return 0; - } - - return paging_request(msc->network, subscr, chan_needed, NULL, msc); -} - -static int locked_paging(struct bsc_subscr *subscr, int chan_needed, - struct bsc_msc_data *msc) -{ - struct gsm_bts *bts = NULL; - - /* - * Check if there is any BTS that is on for the given lac. Start - * with NULL and iterate through all bts. - */ - llist_for_each_entry(bts, &msc->network->bts_list, list) { - /* - * continue if the BTS is not excluded from the lock - */ - if (!bts->excl_from_rf_lock) - continue; - - /* in case of no lac patching is in place, check the BTS */ - if (msc->core_lac == -1 && subscr->lac != bts->location_area_code) - continue; - - /* - * now page on this bts - */ - paging_request_bts(bts, subscr, chan_needed, NULL, msc); - }; - - /* All bts are either off or in the grace period */ - return 0; -} - -/** - * Try to not page if everything the cell is not on. - */ -int bsc_grace_paging_request(enum signal_rf rf_policy, - struct bsc_subscr *subscr, - int chan_needed, - struct bsc_msc_data *msc) -{ - if (rf_policy == S_RF_ON) - return normal_paging(subscr, chan_needed, msc); - return locked_paging(subscr, chan_needed, msc); -} - -static int handle_sub(struct gsm_lchan *lchan, const char *text) -{ - struct gsm_subscriber_connection *conn; - - /* only send it to TCH */ - if (lchan->type != GSM_LCHAN_TCH_H && lchan->type != GSM_LCHAN_TCH_F) - return -1; - - /* only send on the primary channel */ - conn = lchan->conn; - if (!conn) - return -1; - - if (conn->lchan != lchan) - return -1; - - /* only when active */ - if (lchan->state != LCHAN_S_ACTIVE) - return -1; - - bsc_send_ussd_notify(conn, 0, text); - bsc_send_ussd_release_complete(conn); - - return 0; -} - -/* - * The place to handle the grace mode. Right now we will send - * USSD messages to the subscriber, in the future we might start - * a timer to have different modes for the grace period. - */ -static int handle_grace(struct gsm_network *network) -{ - int ts_nr, lchan_nr; - struct gsm_bts *bts; - struct gsm_bts_trx *trx; - - if (!network->bsc_data->mid_call_txt) - return 0; - - llist_for_each_entry(bts, &network->bts_list, list) { - llist_for_each_entry(trx, &bts->trx_list, list) { - for (ts_nr = 0; ts_nr < TRX_NR_TS; ++ts_nr) { - struct gsm_bts_trx_ts *ts = &trx->ts[ts_nr]; - for (lchan_nr = 0; lchan_nr < TS_MAX_LCHAN; ++lchan_nr) { - handle_sub(&ts->lchan[lchan_nr], - network->bsc_data->mid_call_txt); - } - } - } - } - return 0; -} - -static int handle_rf_signal(unsigned int subsys, unsigned int signal, - void *handler_data, void *signal_data) -{ - struct rf_signal_data *sig; - - if (subsys != SS_RF) - return -1; - - sig = signal_data; - - if (signal == S_RF_GRACE) - handle_grace(sig->net); - - return 0; -} - -static __attribute__((constructor)) void on_dso_load_grace(void) -{ - osmo_signal_register_handler(SS_RF, handle_rf_signal, NULL); -} diff --git a/openbsc/src/osmo-bsc/osmo_bsc_main.c b/openbsc/src/osmo-bsc/osmo_bsc_main.c deleted file mode 100644 index ee094d67..00000000 --- a/openbsc/src/osmo-bsc/osmo_bsc_main.c +++ /dev/null @@ -1,301 +0,0 @@ -/* (C) 2008-2009 by Harald Welte - * (C) 2009-2011 by Holger Hans Peter Freyther - * (C) 2009-2011 by On-Waves - * All Rights Reserved - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation; either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - * - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include - -#include -#include -#include -#include -#include - -#include - -#include - -#define _GNU_SOURCE -#include - -#include -#include -#include -#include -#include - - -#include "../../bscconfig.h" - -struct gsm_network *bsc_gsmnet = 0; -static const char *config_file = "openbsc.cfg"; -static const char *rf_ctrl = NULL; -extern const char *openbsc_copyright; -static int daemonize = 0; -static struct llist_head access_lists; - -struct llist_head *bsc_access_lists(void) -{ - return &access_lists; -} - -static void print_usage() -{ - printf("Usage: osmo-bsc\n"); -} - -static void print_help() -{ - printf(" Some useful help...\n"); - printf(" -h --help this text\n"); - printf(" -D --daemonize Fork the process into a background daemon\n"); - printf(" -d option --debug=DRLL:DCC:DMM:DRR:DRSL:DNM enable debugging\n"); - printf(" -s --disable-color\n"); - printf(" -T --timestamp. Print a timestamp in the debug output.\n"); - printf(" -c --config-file filename The config file to use.\n"); - printf(" -l --local=IP. The local address of the MGCP.\n"); - printf(" -e --log-level number. Set a global loglevel.\n"); - printf(" -r --rf-ctl NAME. A unix domain socket to listen for cmds.\n"); - printf(" -t --testmode. A special mode to provoke failures at the MSC.\n"); -} - -static void handle_options(int argc, char **argv) -{ - while (1) { - int option_index = 0, c; - static struct option long_options[] = { - {"help", 0, 0, 'h'}, - {"debug", 1, 0, 'd'}, - {"daemonize", 0, 0, 'D'}, - {"config-file", 1, 0, 'c'}, - {"disable-color", 0, 0, 's'}, - {"timestamp", 0, 0, 'T'}, - {"local", 1, 0, 'l'}, - {"log-level", 1, 0, 'e'}, - {"rf-ctl", 1, 0, 'r'}, - {"testmode", 0, 0, 't'}, - {0, 0, 0, 0} - }; - - c = getopt_long(argc, argv, "hd:DsTc:e:r:t", - long_options, &option_index); - if (c == -1) - break; - - switch (c) { - case 'h': - print_usage(); - print_help(); - exit(0); - case 's': - log_set_use_color(osmo_stderr_target, 0); - break; - case 'd': - log_parse_category_mask(osmo_stderr_target, optarg); - break; - case 'D': - daemonize = 1; - break; - case 'c': - config_file = optarg; - break; - case 'T': - log_set_print_timestamp(osmo_stderr_target, 1); - break; - case 'e': - log_set_log_level(osmo_stderr_target, atoi(optarg)); - break; - case 'r': - rf_ctrl = optarg; - break; - default: - /* ignore */ - break; - } - } -} - -extern int bsc_vty_go_parent(struct vty *vty); - -static struct vty_app_info vty_info = { - .name = "OsmoBSC", - .version = PACKAGE_VERSION, - .go_parent_cb = bsc_vty_go_parent, - .is_config_node = bsc_vty_is_config_node, -}; - -extern int bsc_shutdown_net(struct gsm_network *net); -static void signal_handler(int signal) -{ - struct bsc_msc_data *msc; - - fprintf(stdout, "signal %u received\n", signal); - - switch (signal) { - case SIGINT: - bsc_shutdown_net(bsc_gsmnet); - osmo_signal_dispatch(SS_L_GLOBAL, S_L_GLOBAL_SHUTDOWN, NULL); - sleep(3); - exit(0); - break; - case SIGABRT: - /* in case of abort, we want to obtain a talloc report - * and then return to the caller, who will abort the process */ - case SIGUSR1: - talloc_report(tall_vty_ctx, stderr); - talloc_report_full(tall_bsc_ctx, stderr); - break; - case SIGUSR2: - if (!bsc_gsmnet->bsc_data) - return; - llist_for_each_entry(msc, &bsc_gsmnet->bsc_data->mscs, entry) - bsc_msc_lost(msc->msc_con); - break; - default: - break; - } -} - -int main(int argc, char **argv) -{ - struct bsc_msc_data *msc; - struct osmo_bsc_data *data; - int rc; - - tall_bsc_ctx = talloc_named_const(NULL, 1, "openbsc"); - msgb_talloc_ctx_init(tall_bsc_ctx, 0); - - /* Allocate global gsm_network struct */ - rc = bsc_network_alloc(NULL); - if (rc) { - fprintf(stderr, "Allocation failed. exiting.\n"); - exit(1); - } - - osmo_init_logging(&log_info); - osmo_stats_init(tall_bsc_ctx); - - bts_init(); - libosmo_abis_init(tall_bsc_ctx); - - /* enable filters */ - - /* This needs to precede handle_options() */ - vty_info.copyright = openbsc_copyright; - vty_init(&vty_info); - bsc_vty_init(bsc_gsmnet); - bsc_msg_lst_vty_init(tall_bsc_ctx, &access_lists, BSC_NODE); - ctrl_vty_init(tall_bsc_ctx); - - INIT_LLIST_HEAD(&access_lists); - - /* parse options */ - handle_options(argc, argv); - - /* seed the PRNG */ - srand(time(NULL)); - - /* initialize SCCP */ - sccp_set_log_area(DSCCP); - - /* Read the config */ - rc = bsc_network_configure(config_file); - if (rc < 0) { - fprintf(stderr, "Bootstrapping the network failed. exiting.\n"); - exit(1); - } - bsc_api_init(bsc_gsmnet, osmo_bsc_api()); - - /* start control interface after reading config for - * ctrl_vty_get_bind_addr() */ - bsc_gsmnet->ctrl = bsc_controlif_setup(bsc_gsmnet, - ctrl_vty_get_bind_addr(), - OSMO_CTRL_PORT_NITB_BSC); - if (!bsc_gsmnet->ctrl) { - fprintf(stderr, "Failed to init the control interface. Exiting.\n"); - exit(1); - } - - rc = bsc_ctrl_cmds_install(bsc_gsmnet); - if (rc < 0) { - fprintf(stderr, "Failed to install control commands. Exiting.\n"); - exit(1); - } - - data = bsc_gsmnet->bsc_data; - if (rf_ctrl) - osmo_talloc_replace_string(data, &data->rf_ctrl_name, rf_ctrl); - - data->rf_ctrl = osmo_bsc_rf_create(data->rf_ctrl_name, bsc_gsmnet); - if (!data->rf_ctrl) { - fprintf(stderr, "Failed to create the RF service.\n"); - exit(1); - } - - llist_for_each_entry(msc, &bsc_gsmnet->bsc_data->mscs, entry) { - if (osmo_bsc_msc_init(msc) != 0) { - LOGP(DNAT, LOGL_ERROR, "Failed to start up. Exiting.\n"); - exit(1); - } - } - - - if (osmo_bsc_sccp_init(bsc_gsmnet) != 0) { - LOGP(DNM, LOGL_ERROR, "Failed to register SCCP.\n"); - exit(1); - } - - if (osmo_bsc_audio_init(bsc_gsmnet) != 0) { - LOGP(DMSC, LOGL_ERROR, "Failed to register audio support.\n"); - exit(1); - } - - signal(SIGINT, &signal_handler); - signal(SIGABRT, &signal_handler); - signal(SIGUSR1, &signal_handler); - signal(SIGUSR2, &signal_handler); - osmo_init_ignore_signals(); - - if (daemonize) { - rc = osmo_daemonize(); - if (rc < 0) { - perror("Error during daemonize"); - exit(1); - } - } - - while (1) { - osmo_select_main(0); - } - - return 0; -} diff --git a/openbsc/src/osmo-bsc/osmo_bsc_msc.c b/openbsc/src/osmo-bsc/osmo_bsc_msc.c deleted file mode 100644 index 8d02624b..00000000 --- a/openbsc/src/osmo-bsc/osmo_bsc_msc.c +++ /dev/null @@ -1,586 +0,0 @@ -/* - * Handle the connection to the MSC. This include ping/timeout/reconnect - * (C) 2008-2009 by Harald Welte - * (C) 2009-2015 by Holger Hans Peter Freyther - * (C) 2009-2015 by On-Waves - * All Rights Reserved - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation; either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - * - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -#include - -#include - -#include - -#include -#include -#include - - -static void initialize_if_needed(struct bsc_msc_connection *conn); -static void send_lacs(struct gsm_network *net, struct bsc_msc_connection *conn); -static void send_id_get_response(struct bsc_msc_data *data, int fd, struct msgb *inp); -static void send_ping(struct bsc_msc_data *data); -static void schedule_ping_pong(struct bsc_msc_data *data); - -/* - * MGCP forwarding code - */ -static int mgcp_do_read(struct osmo_fd *fd) -{ - struct bsc_msc_data *data = (struct bsc_msc_data *) fd->data; - struct msgb *mgcp; - int ret; - - mgcp = msgb_alloc_headroom(4096, 128, "mgcp_from_gw"); - if (!mgcp) { - LOGP(DMGCP, LOGL_ERROR, "Failed to allocate MGCP message.\n"); - return -1; - } - - ret = read(fd->fd, mgcp->data, 4096 - 128); - if (ret <= 0) { - LOGP(DMGCP, LOGL_ERROR, "Failed to read: %d/%s\n", errno, strerror(errno)); - msgb_free(mgcp); - return -1; - } else if (ret > 4096 - 128) { - LOGP(DMGCP, LOGL_ERROR, "Too much data: %d\n", ret); - msgb_free(mgcp); - return -1; - } - - mgcp->l2h = msgb_put(mgcp, ret); - msc_queue_write(data->msc_con, mgcp, IPAC_PROTO_MGCP_OLD); - return 0; -} - -static int mgcp_do_write(struct osmo_fd *fd, struct msgb *msg) -{ - int ret; - - LOGP(DMGCP, LOGL_DEBUG, "Sending msg to MGCP GW size: %u\n", msg->len); - - ret = write(fd->fd, msg->data, msg->len); - if (ret != msg->len) - LOGP(DMGCP, LOGL_ERROR, "Failed to forward message to MGCP GW (%s).\n", strerror(errno)); - - return ret; -} - -static void mgcp_forward(struct bsc_msc_data *data, struct msgb *msg) -{ - struct msgb *mgcp; - - if (msgb_l2len(msg) > 4096) { - LOGP(DMGCP, LOGL_ERROR, "Can not forward too big message.\n"); - return; - } - - mgcp = msgb_alloc(4096, "mgcp_to_gw"); - if (!mgcp) { - LOGP(DMGCP, LOGL_ERROR, "Failed to send message.\n"); - return; - } - - msgb_put(mgcp, msgb_l2len(msg)); - memcpy(mgcp->data, msg->l2h, mgcp->len); - if (osmo_wqueue_enqueue(&data->mgcp_agent, mgcp) != 0) { - LOGP(DMGCP, LOGL_FATAL, "Could not queue message to MGCP GW.\n"); - msgb_free(mgcp); - } -} - -static int mgcp_create_port(struct bsc_msc_data *data) -{ - int on; - struct sockaddr_in addr; - - data->mgcp_agent.bfd.fd = socket(AF_INET, SOCK_DGRAM, 0); - if (data->mgcp_agent.bfd.fd < 0) { - LOGP(DMGCP, LOGL_FATAL, "Failed to create UDP socket errno: %d\n", errno); - return -1; - } - - on = 1; - setsockopt(data->mgcp_agent.bfd.fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)); - - /* try to bind the socket */ - memset(&addr, 0, sizeof(addr)); - addr.sin_family = AF_INET; - addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK); - addr.sin_port = 0; - - if (bind(data->mgcp_agent.bfd.fd, (struct sockaddr *) &addr, sizeof(addr)) < 0) { - LOGP(DMGCP, LOGL_FATAL, "Failed to bind to any port.\n"); - close(data->mgcp_agent.bfd.fd); - data->mgcp_agent.bfd.fd = -1; - return -1; - } - - /* connect to the remote */ - addr.sin_port = htons(2427); - if (connect(data->mgcp_agent.bfd.fd, (struct sockaddr *) & addr, sizeof(addr)) < 0) { - LOGP(DMGCP, LOGL_FATAL, "Failed to connect to local MGCP GW. %s\n", strerror(errno)); - close(data->mgcp_agent.bfd.fd); - data->mgcp_agent.bfd.fd = -1; - return -1; - } - - osmo_wqueue_init(&data->mgcp_agent, 10); - data->mgcp_agent.bfd.when = BSC_FD_READ; - data->mgcp_agent.bfd.data = data; - data->mgcp_agent.read_cb = mgcp_do_read; - data->mgcp_agent.write_cb = mgcp_do_write; - - if (osmo_fd_register(&data->mgcp_agent.bfd) != 0) { - LOGP(DMGCP, LOGL_FATAL, "Failed to register BFD\n"); - close(data->mgcp_agent.bfd.fd); - data->mgcp_agent.bfd.fd = -1; - return -1; - } - - return 0; -} - -/* - * Send data to the network - */ -int msc_queue_write(struct bsc_msc_connection *conn, struct msgb *msg, int proto) -{ - ipa_prepend_header(msg, proto); - if (osmo_wqueue_enqueue(&conn->write_queue, msg) != 0) { - LOGP(DMSC, LOGL_FATAL, "Failed to queue IPA/%d\n", proto); - msgb_free(msg); - return -1; - } - - return 0; -} - -int msc_queue_write_with_ping(struct bsc_msc_connection *conn, - struct msgb *msg, int proto) -{ - struct bsc_msc_data *data; - uint8_t val; - - /* prepend the header */ - ipa_prepend_header(msg, proto); - if (osmo_wqueue_enqueue(&conn->write_queue, msg) != 0) { - LOGP(DMSC, LOGL_FATAL, "Failed to queue IPA/%d\n", proto); - msgb_free(msg); - return -1; - } - - /* add the ping as the other message */ - val = IPAC_MSGT_PING; - msgb_l16tv_put(msg, 1, IPAC_PROTO_IPACCESS, &val); - - data = (struct bsc_msc_data *) conn->write_queue.bfd.data; - schedule_ping_pong(data); - return 0; -} - -static int msc_alink_do_write(struct osmo_fd *fd, struct msgb *msg) -{ - int ret; - - LOGP(DMSC, LOGL_DEBUG, "Sending SCCP to MSC: %u\n", msgb_l2len(msg)); - LOGP(DLMI, LOGL_DEBUG, "MSC TX %s\n", osmo_hexdump(msg->data, msg->len)); - - ret = write(fd->fd, msg->data, msg->len); - if (ret < msg->len) - perror("MSC: Failed to send SCCP"); - - return ret; -} - -static void handle_ctrl(struct bsc_msc_data *msc, struct msgb *msg) -{ - int ret; - struct ctrl_cmd *cmd; - - cmd = ctrl_cmd_parse(msc->msc_con, msg); - if (!cmd) { - LOGP(DMSC, LOGL_ERROR, "Failed to parse control message.\n"); - cmd = talloc_zero(msc->msc_con, struct ctrl_cmd); - if (!cmd) { - LOGP(DMSC, LOGL_ERROR, "OOM!\n"); - return; - } - cmd->type = CTRL_TYPE_ERROR; - cmd->id = "err"; - cmd->reply = "Failed to parse control message."; - - ctrl_cmd_send(&msc->msc_con->write_queue, cmd); - talloc_free(cmd); - - return; - } - - ret = ctrl_cmd_handle(msc->network->ctrl, cmd, msc->network); - if (ret != CTRL_CMD_HANDLED) - ctrl_cmd_send(&msc->msc_con->write_queue, cmd); - talloc_free(cmd); -} - -static void osmo_ext_handle(struct bsc_msc_data *msc, struct msgb *msg) -{ - struct ipaccess_head *hh; - struct ipaccess_head_ext *hh_ext; - - hh = (struct ipaccess_head *) msg->data; - hh_ext = (struct ipaccess_head_ext *) hh->data; - if (msg->len < sizeof(*hh) + sizeof(*hh_ext)) { - LOGP(DMSC, LOGL_ERROR, "Packet too short for extended header.\n"); - return; - } - - msg->l2h = hh_ext->data; - if (hh_ext->proto == IPAC_PROTO_EXT_MGCP) - mgcp_forward(msc, msg); - else if (hh_ext->proto == IPAC_PROTO_EXT_LAC) - send_lacs(msc->network, msc->msc_con); - else if (hh_ext->proto == IPAC_PROTO_EXT_CTRL) - handle_ctrl(msc, msg); -} - -static int ipaccess_a_fd_cb(struct osmo_fd *bfd) -{ - struct msgb *msg = NULL; - struct ipaccess_head *hh; - struct bsc_msc_data *data = (struct bsc_msc_data *) bfd->data; - int ret; - - ret = ipa_msg_recv_buffered(bfd->fd, &msg, &data->msc_con->pending_msg); - if (ret <= 0) { - if (ret == -EAGAIN) - return 0; - if (ret == 0) { - LOGP(DMSC, LOGL_ERROR, "The connection to the MSC was lost.\n"); - bsc_msc_lost(data->msc_con); - return -1; - } - - LOGP(DMSC, LOGL_ERROR, "Failed to parse ip access message: %d\n", ret); - return -1; - } - - LOGP(DLMI, LOGL_DEBUG, "From MSC: %s proto: %d\n", osmo_hexdump(msg->data, msg->len), msg->l2h[0]); - - /* handle base message handling */ - hh = (struct ipaccess_head *) msg->data; - - /* initialize the networking. This includes sending a GSM08.08 message */ - msg->cb[0] = (unsigned long) data; - if (hh->proto == IPAC_PROTO_IPACCESS) { - ipa_ccm_rcvmsg_base(msg, bfd); - if (msg->l2h[0] == IPAC_MSGT_ID_ACK) - initialize_if_needed(data->msc_con); - else if (msg->l2h[0] == IPAC_MSGT_ID_GET) { - send_id_get_response(data, bfd->fd, msg); - } else if (msg->l2h[0] == IPAC_MSGT_PONG) { - osmo_timer_del(&data->pong_timer); - } - } else if (hh->proto == IPAC_PROTO_SCCP) { - sccp_system_incoming_ctx(msg, data->msc_con); - } else if (hh->proto == IPAC_PROTO_MGCP_OLD) { - mgcp_forward(data, msg); - } else if (hh->proto == IPAC_PROTO_OSMO) { - osmo_ext_handle(data, msg); - } - - msgb_free(msg); - return 0; -} - -static void send_ping(struct bsc_msc_data *data) -{ - struct msgb *msg; - - msg = msgb_alloc_headroom(4096, 128, "ping"); - if (!msg) { - LOGP(DMSC, LOGL_ERROR, "Failed to create PING.\n"); - return; - } - - msg->l2h = msgb_put(msg, 1); - msg->l2h[0] = IPAC_MSGT_PING; - - msc_queue_write(data->msc_con, msg, IPAC_PROTO_IPACCESS); -} - -static void schedule_ping_pong(struct bsc_msc_data *data) -{ - /* send another ping in 20 seconds */ - osmo_timer_schedule(&data->ping_timer, data->ping_timeout, 0); - - /* also start a pong timer */ - osmo_timer_schedule(&data->pong_timer, data->pong_timeout, 0); -} - -static void msc_ping_timeout_cb(void *_data) -{ - struct bsc_msc_data *data = (struct bsc_msc_data *) _data; - if (data->ping_timeout <= 0) - return; - - send_ping(data); - schedule_ping_pong(data); -} - -static void msc_pong_timeout_cb(void *_data) -{ - struct bsc_msc_data *data = (struct bsc_msc_data *) _data; - - LOGP(DMSC, LOGL_ERROR, "MSC didn't answer PING. Closing connection.\n"); - bsc_msc_lost(data->msc_con); -} - -static void msc_connection_connected(struct bsc_msc_connection *con) -{ - struct msc_signal_data sig; - struct bsc_msc_data *data; - int ret, on; - on = 1; - ret = setsockopt(con->write_queue.bfd.fd, IPPROTO_TCP, TCP_NODELAY, &on, sizeof(on)); - if (ret != 0) - LOGP(DMSC, LOGL_ERROR, "Failed to set TCP_NODELAY: %s\n", strerror(errno)); - - data = (struct bsc_msc_data *) con->write_queue.bfd.data; - msc_ping_timeout_cb(data); - - sig.data = data; - osmo_signal_dispatch(SS_MSC, S_MSC_CONNECTED, &sig); -} - -/* - * The connection to the MSC was lost and we will need to free all - * resources and then attempt to reconnect. - */ -static void msc_connection_was_lost(struct bsc_msc_connection *msc) -{ - struct msc_signal_data sig; - struct bsc_msc_data *data; - - LOGP(DMSC, LOGL_ERROR, "Lost MSC connection. Freing stuff.\n"); - - data = (struct bsc_msc_data *) msc->write_queue.bfd.data; - osmo_timer_del(&data->ping_timer); - osmo_timer_del(&data->pong_timer); - - sig.data = data; - osmo_signal_dispatch(SS_MSC, S_MSC_LOST, &sig); - - msc->is_authenticated = 0; - bsc_msc_schedule_connect(msc); -} - -static void send_lacs(struct gsm_network *net, struct bsc_msc_connection *conn) -{ - struct ipac_ext_lac_cmd *lac; - struct gsm_bts *bts; - struct msgb *msg; - int lacs = 0; - - if (llist_empty(&net->bts_list)) { - LOGP(DMSC, LOGL_ERROR, "No BTSs configured. Not sending LACs.\n"); - return; - } - - msg = msgb_alloc_headroom(4096, 128, "LAC Command"); - if (!msg) { - LOGP(DMSC, LOGL_ERROR, "Failed to create the LAC command.\n"); - return; - } - - lac = (struct ipac_ext_lac_cmd *) msgb_put(msg, sizeof(*lac)); - lac->add_remove = 1; - - llist_for_each_entry(bts, &net->bts_list, list) { - if (lacs++ == 0) - lac->lac = htons(bts->location_area_code); - else - msgb_put_u16(msg, htons(bts->location_area_code)); - } - - lac->nr_extra_lacs = lacs - 1; - ipa_prepend_header_ext(msg, IPAC_PROTO_EXT_LAC); - msc_queue_write(conn, msg, IPAC_PROTO_OSMO); -} - -static void initialize_if_needed(struct bsc_msc_connection *conn) -{ - struct msgb *msg; - - if (!conn->is_authenticated) { - /* send a gsm 08.08 reset message from here */ - msg = gsm0808_create_reset(); - if (!msg) { - LOGP(DMSC, LOGL_ERROR, "Failed to create the reset message.\n"); - return; - } - - sccp_write(msg, &sccp_ssn_bssap, &sccp_ssn_bssap, 0, conn); - msgb_free(msg); - conn->is_authenticated = 1; - } -} - -static int answer_challenge(struct bsc_msc_data *data, struct msgb *inp, struct osmo_auth_vector *vec) -{ - int ret; - struct tlv_parsed tvp; - const uint8_t *mrand; - uint8_t mrand_len; - struct osmo_sub_auth_data auth = { - .type = OSMO_AUTH_TYPE_GSM, - .algo = OSMO_AUTH_ALG_MILENAGE, - }; - - ret = ipa_ccm_idtag_parse_off(&tvp, - inp->l2h + 1, - msgb_l2len(inp) - 1, 1); - if (ret < 0) { - LOGP(DMSC, LOGL_ERROR, "ignoring IPA response " - "message with malformed TLVs: %s\n", osmo_hexdump(inp->l2h + 1, - msgb_l2len(inp) - 1)); - return 0; - } - - mrand = TLVP_VAL(&tvp, 0x23); - mrand_len = TLVP_LEN(&tvp, 0x23); - if (mrand_len != 16) { - LOGP(DMSC, LOGL_ERROR, - "RAND is not 16 bytes. Was %d\n", - mrand_len); - return 0; - } - - /* copy the key */ - memcpy(auth.u.umts.opc, data->bsc_key, 16); - memcpy(auth.u.umts.k, data->bsc_key, 16); - memset(auth.u.umts.amf, 0, 2); - auth.u.umts.sqn = 0; - - /* generate the result */ - memset(vec, 0, sizeof(*vec)); - osmo_auth_gen_vec(vec, &auth, mrand); - return 1; -} - - -static void send_id_get_response(struct bsc_msc_data *data, int fd, struct msgb *inp) -{ - struct msc_signal_data sig; - struct msgb *msg; - struct osmo_auth_vector vec; - int valid = 0; - - if (data->bsc_key_present) - valid = answer_challenge(data, inp, &vec); - - msg = bsc_msc_id_get_resp(valid, data->bsc_token, - vec.res, valid ? vec.res_len : 0); - if (!msg) - return; - msc_queue_write(data->msc_con, msg, IPAC_PROTO_IPACCESS); - - sig.data = data; - osmo_signal_dispatch(SS_MSC, S_MSC_AUTHENTICATED, &sig); -} - -int osmo_bsc_msc_init(struct bsc_msc_data *data) -{ - if (mgcp_create_port(data) != 0) - return -1; - - data->msc_con = bsc_msc_create(data, &data->dests); - if (!data->msc_con) { - LOGP(DMSC, LOGL_ERROR, "Creating the MSC network connection failed.\n"); - return -1; - } - - osmo_timer_setup(&data->ping_timer, msc_ping_timeout_cb, data); - osmo_timer_setup(&data->pong_timer, msc_pong_timeout_cb, data); - - data->msc_con->write_queue.bfd.data = data; - data->msc_con->connection_loss = msc_connection_was_lost; - data->msc_con->connected = msc_connection_connected; - data->msc_con->write_queue.read_cb = ipaccess_a_fd_cb; - data->msc_con->write_queue.write_cb = msc_alink_do_write; - bsc_msc_connect(data->msc_con); - - return 0; -} - -struct bsc_msc_data *osmo_msc_data_find(struct gsm_network *net, int nr) -{ - struct bsc_msc_data *msc_data; - - llist_for_each_entry(msc_data, &net->bsc_data->mscs, entry) - if (msc_data->nr == nr) - return msc_data; - return NULL; -} - -struct bsc_msc_data *osmo_msc_data_alloc(struct gsm_network *net, int nr) -{ - struct bsc_msc_data *msc_data; - - /* check if there is already one */ - msc_data = osmo_msc_data_find(net, nr); - if (msc_data) - return msc_data; - - msc_data = talloc_zero(net, struct bsc_msc_data); - if (!msc_data) - return NULL; - - llist_add_tail(&msc_data->entry, &net->bsc_data->mscs); - - /* Init back pointer */ - msc_data->network = net; - - INIT_LLIST_HEAD(&msc_data->dests); - msc_data->ping_timeout = 20; - msc_data->pong_timeout = 5; - msc_data->core_mnc = -1; - msc_data->core_mcc = -1; - msc_data->core_ci = -1; - msc_data->core_lac = -1; - msc_data->rtp_base = 4000; - - msc_data->nr = nr; - msc_data->allow_emerg = 1; - - /* Defaults for the audio setup */ - msc_data->amr_conf.m5_90 = 1; - - return msc_data; -} diff --git a/openbsc/src/osmo-bsc/osmo_bsc_sccp.c b/openbsc/src/osmo-bsc/osmo_bsc_sccp.c deleted file mode 100644 index e242390e..00000000 --- a/openbsc/src/osmo-bsc/osmo_bsc_sccp.c +++ /dev/null @@ -1,328 +0,0 @@ -/* Interaction with the SCCP subsystem */ -/* - * (C) 2009-2014 by Holger Hans Peter Freyther - * (C) 2009-2014 by On-Waves - * All Rights Reserved - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation; either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - * - */ - -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include - -#include - -/* SCCP helper */ -#define SCCP_IT_TIMER 60 - -static LLIST_HEAD(active_connections); - -static void free_queued(struct osmo_bsc_sccp_con *conn) -{ - struct msgb *msg; - - while (!llist_empty(&conn->sccp_queue)) { - /* this is not allowed to fail */ - msg = msgb_dequeue(&conn->sccp_queue); - msgb_free(msg); - } - - conn->sccp_queue_size = 0; -} - -static void send_queued(struct osmo_bsc_sccp_con *conn) -{ - struct msgb *msg; - - while (!llist_empty(&conn->sccp_queue)) { - /* this is not allowed to fail */ - msg = msgb_dequeue(&conn->sccp_queue); - sccp_connection_write(conn->sccp, msg); - msgb_free(msg); - conn->sccp_queue_size -= 1; - } -} - -static void msc_outgoing_sccp_data(struct sccp_connection *conn, - struct msgb *msg, unsigned int len) -{ - struct osmo_bsc_sccp_con *bsc_con = - (struct osmo_bsc_sccp_con *) conn->data_ctx; - - bsc_handle_dt1(bsc_con, msg, len); -} - -static void msc_outgoing_sccp_state(struct sccp_connection *conn, int old_state) -{ - struct osmo_bsc_sccp_con *con_data; - - if (conn->connection_state >= SCCP_CONNECTION_STATE_RELEASE_COMPLETE) { - con_data = (struct osmo_bsc_sccp_con *) conn->data_ctx; - if (con_data->conn) { - LOGP(DMSC, LOGL_ERROR, - "ERROR: The lchan is still associated.\n"); - gsm0808_clear(con_data->conn); - bsc_subscr_con_free(con_data->conn); - con_data->conn = NULL; - } - - con_data->sccp = NULL; - free_queued(con_data); - sccp_connection_free(conn); - bsc_delete_connection(con_data); - } else if (conn->connection_state == SCCP_CONNECTION_STATE_ESTABLISHED) { - LOGP(DMSC, LOGL_DEBUG, "Connection established: %p\n", conn); - con_data = (struct osmo_bsc_sccp_con *) conn->data_ctx; - - osmo_timer_del(&con_data->sccp_cc_timeout); - osmo_timer_schedule(&con_data->sccp_it_timeout, SCCP_IT_TIMER, 0); - - send_queued(con_data); - } -} - -static void bsc_sccp_force_free(struct osmo_bsc_sccp_con *data) -{ - if (data->conn) { - gsm0808_clear(data->conn); - bsc_subscr_con_free(data->conn); - data->conn = NULL; - } - - free_queued(data); - sccp_connection_force_free(data->sccp); - data->sccp = NULL; - bsc_delete_connection(data); -} - -static void sccp_it_timeout(void *_data) -{ - struct osmo_bsc_sccp_con *data = - (struct osmo_bsc_sccp_con *) _data; - - sccp_connection_send_it(data->sccp); - osmo_timer_schedule(&data->sccp_it_timeout, SCCP_IT_TIMER, 0); -} - -static void sccp_cc_timeout(void *_data) -{ - struct osmo_bsc_sccp_con *data = - (struct osmo_bsc_sccp_con *) _data; - - if (data->sccp->connection_state >= SCCP_CONNECTION_STATE_ESTABLISHED) - return; - - LOGP(DMSC, LOGL_ERROR, "The connection was never established.\n"); - bsc_sccp_force_free(data); -} - -static void msc_sccp_write_ipa(struct sccp_connection *conn, struct msgb *msg, - void *global_ctx, void *ctx) -{ - struct bsc_msc_connection *msc_con; - - if (conn) { - struct osmo_bsc_sccp_con *bsc_con = conn->data_ctx; - msc_con = bsc_con->msc->msc_con; - if (bsc_con->send_ping) { - bsc_con->send_ping = 0; - msc_queue_write_with_ping(msc_con, msg, IPAC_PROTO_SCCP); - return; - } - } else { - msc_con = ctx; - } - - msc_queue_write(msc_con, msg, IPAC_PROTO_SCCP); -} - -static int msc_sccp_accept(struct sccp_connection *connection, void *data) -{ - LOGP(DMSC, LOGL_DEBUG, "Rejecting incoming SCCP connection.\n"); - return -1; -} - -static int msc_sccp_read(struct msgb *msgb, unsigned int length, void *data) -{ - struct bsc_msc_data *msc = (struct bsc_msc_data *) msgb->cb[0]; - return bsc_handle_udt(msc, msgb, length); -} - -int bsc_queue_for_msc(struct osmo_bsc_sccp_con *conn, struct msgb *msg) -{ - struct sccp_connection *sccp = conn->sccp; - - if (sccp->connection_state > SCCP_CONNECTION_STATE_ESTABLISHED) { - LOGP(DMSC, LOGL_ERROR, "Connection closing, dropping packet on: %p\n", sccp); - msgb_free(msg); - } else if (sccp->connection_state == SCCP_CONNECTION_STATE_ESTABLISHED - && conn->sccp_queue_size == 0) { - sccp_connection_write(sccp, msg); - msgb_free(msg); - } else if (conn->sccp_queue_size > 10) { - LOGP(DMSC, LOGL_ERROR, "Connection closing, dropping packet on: %p\n", sccp); - msgb_free(msg); - } else { - LOGP(DMSC, LOGL_DEBUG, "Queueing packet on %p. Queue size: %d\n", sccp, conn->sccp_queue_size); - conn->sccp_queue_size += 1; - msgb_enqueue(&conn->sccp_queue, msg); - } - - return 0; -} - -enum bsc_con bsc_create_new_connection(struct gsm_subscriber_connection *conn, - struct bsc_msc_data *msc, int send_ping) -{ - struct osmo_bsc_sccp_con *bsc_con; - struct sccp_connection *sccp; - - /* This should not trigger */ - if (!msc || !msc->msc_con->is_authenticated) { - LOGP(DMSC, LOGL_ERROR, - "How did this happen? MSC is not connected. Dropping.\n"); - return BSC_CON_REJECT_NO_LINK; - } - - if (!bsc_grace_allow_new_connection(conn->bts->network, conn->bts)) { - LOGP(DMSC, LOGL_NOTICE, "BSC in grace period. No new connections.\n"); - return BSC_CON_REJECT_RF_GRACE; - } - - sccp = sccp_connection_socket(); - if (!sccp) { - LOGP(DMSC, LOGL_ERROR, "Failed to allocate memory.\n"); - return BSC_CON_NO_MEM; - } - - bsc_con = talloc_zero(conn->bts, struct osmo_bsc_sccp_con); - if (!bsc_con) { - LOGP(DMSC, LOGL_ERROR, "Failed to allocate.\n"); - sccp_connection_free(sccp); - return BSC_CON_NO_MEM; - } - - /* callbacks */ - sccp->state_cb = msc_outgoing_sccp_state; - sccp->data_cb = msc_outgoing_sccp_data; - sccp->data_ctx = bsc_con; - - bsc_con->send_ping = send_ping; - - /* prepare the timers */ - osmo_timer_setup(&bsc_con->sccp_it_timeout, sccp_it_timeout, bsc_con); - osmo_timer_setup(&bsc_con->sccp_cc_timeout, sccp_cc_timeout, bsc_con); - - INIT_LLIST_HEAD(&bsc_con->sccp_queue); - - bsc_con->sccp = sccp; - bsc_con->msc = msc; - bsc_con->conn = conn; - llist_add_tail(&bsc_con->entry, &active_connections); - conn->sccp_con = bsc_con; - return BSC_CON_SUCCESS; -} - -int bsc_open_connection(struct osmo_bsc_sccp_con *conn, struct msgb *msg) -{ - osmo_timer_schedule(&conn->sccp_cc_timeout, 10, 0); - sccp_connection_connect(conn->sccp, &sccp_ssn_bssap, msg); - msgb_free(msg); - return 0; -} - -int bsc_delete_connection(struct osmo_bsc_sccp_con *sccp) -{ - if (!sccp) - return 0; - - if (sccp->conn) - LOGP(DMSC, LOGL_ERROR, "Should have been cleared.\n"); - - llist_del(&sccp->entry); - osmo_timer_del(&sccp->sccp_it_timeout); - osmo_timer_del(&sccp->sccp_cc_timeout); - talloc_free(sccp); - return 0; -} - -static void bsc_notify_msc_lost(struct osmo_bsc_sccp_con *con) -{ - struct gsm_subscriber_connection *conn = con->conn; - - /* send USSD notification if string configured and con->data is set */ - if (!conn) - return; - - /* check for config string */ - if (!con->msc->ussd_msc_lost_txt) - return; - if (con->msc->ussd_msc_lost_txt[0] == '\0') - return; - - /* send USSD notification */ - bsc_send_ussd_notify(conn, 1, conn->sccp_con->msc->ussd_msc_lost_txt); - bsc_send_ussd_release_complete(conn); -} - -static void bsc_notify_and_close_conns(struct bsc_msc_connection *msc_con) -{ - struct osmo_bsc_sccp_con *con, *tmp; - - llist_for_each_entry_safe(con, tmp, &active_connections, entry) { - if (con->msc->msc_con != msc_con) - continue; - - bsc_notify_msc_lost(con); - bsc_sccp_force_free(con); - } -} - -static int handle_msc_signal(unsigned int subsys, unsigned int signal, - void *handler_data, void *signal_data) -{ - struct msc_signal_data *msc; - - if (subsys != SS_MSC) - return 0; - - msc = signal_data; - if (signal == S_MSC_LOST) - bsc_notify_and_close_conns(msc->data->msc_con); - - return 0; -} - -int osmo_bsc_sccp_init(struct gsm_network *gsmnet) -{ - sccp_set_log_area(DSCCP); - sccp_system_init(msc_sccp_write_ipa, gsmnet); - sccp_connection_set_incoming(&sccp_ssn_bssap, msc_sccp_accept, NULL); - sccp_set_read(&sccp_ssn_bssap, msc_sccp_read, gsmnet); - - osmo_signal_register_handler(SS_MSC, handle_msc_signal, gsmnet); - - return 0; -} diff --git a/openbsc/src/osmo-bsc/osmo_bsc_vty.c b/openbsc/src/osmo-bsc/osmo_bsc_vty.c deleted file mode 100644 index 2e2e99bf..00000000 --- a/openbsc/src/osmo-bsc/osmo_bsc_vty.c +++ /dev/null @@ -1,945 +0,0 @@ -/* Osmo BSC VTY Configuration */ -/* (C) 2009-2015 by Holger Hans Peter Freyther - * (C) 2009-2014 by On-Waves - * All Rights Reserved - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation; either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - * - */ - -#include -#include -#include -#include -#include -#include -#include - -#include -#include - -#include - - -#define IPA_STR "IP.ACCESS specific\n" - -extern struct gsm_network *bsc_gsmnet; - -static struct osmo_bsc_data *osmo_bsc_data(struct vty *vty) -{ - return bsc_gsmnet->bsc_data; -} - -static struct bsc_msc_data *bsc_msc_data(struct vty *vty) -{ - return vty->index; -} - -static struct cmd_node bsc_node = { - BSC_NODE, - "%s(config-bsc)# ", - 1, -}; - -static struct cmd_node msc_node = { - MSC_NODE, - "%s(config-msc)# ", - 1, -}; - -DEFUN(cfg_net_msc, cfg_net_msc_cmd, - "msc [<0-1000>]", "Configure MSC details\n" "MSC connection to configure\n") -{ - int index = argc == 1 ? atoi(argv[0]) : 0; - struct bsc_msc_data *msc; - - msc = osmo_msc_data_alloc(bsc_gsmnet, index); - if (!msc) { - vty_out(vty, "%%Failed to allocate MSC data.%s", VTY_NEWLINE); - return CMD_WARNING; - } - - vty->index = msc; - vty->node = MSC_NODE; - return CMD_SUCCESS; -} - -DEFUN(cfg_net_bsc, cfg_net_bsc_cmd, - "bsc", "Configure BSC\n") -{ - vty->node = BSC_NODE; - return CMD_SUCCESS; -} - -static void write_msc_amr_options(struct vty *vty, struct bsc_msc_data *msc) -{ -#define WRITE_AMR(vty, msc, name, var) \ - vty_out(vty, " amr-config %s %s%s", \ - name, msc->amr_conf.var ? "allowed" : "forbidden", \ - VTY_NEWLINE); - - WRITE_AMR(vty, msc, "12_2k", m12_2); - WRITE_AMR(vty, msc, "10_2k", m10_2); - WRITE_AMR(vty, msc, "7_95k", m7_95); - WRITE_AMR(vty, msc, "7_40k", m7_40); - WRITE_AMR(vty, msc, "6_70k", m6_70); - WRITE_AMR(vty, msc, "5_90k", m5_90); - WRITE_AMR(vty, msc, "5_15k", m5_15); - WRITE_AMR(vty, msc, "4_75k", m4_75); -#undef WRITE_AMR -} - -static void write_msc(struct vty *vty, struct bsc_msc_data *msc) -{ - struct bsc_msc_dest *dest; - - vty_out(vty, "msc %d%s", msc->nr, VTY_NEWLINE); - if (msc->bsc_token) - vty_out(vty, " token %s%s", msc->bsc_token, VTY_NEWLINE); - if (msc->bsc_key_present) - vty_out(vty, " auth-key %s%s", - osmo_hexdump(msc->bsc_key, sizeof(msc->bsc_key)), VTY_NEWLINE); - if (msc->core_mnc != -1) - vty_out(vty, " core-mobile-network-code %d%s", - msc->core_mnc, VTY_NEWLINE); - if (msc->core_mcc != -1) - vty_out(vty, " core-mobile-country-code %d%s", - msc->core_mcc, VTY_NEWLINE); - if (msc->core_lac != -1) - vty_out(vty, " core-location-area-code %d%s", - msc->core_lac, VTY_NEWLINE); - if (msc->core_ci != -1) - vty_out(vty, " core-cell-identity %d%s", - msc->core_ci, VTY_NEWLINE); - vty_out(vty, " ip.access rtp-base %d%s", msc->rtp_base, VTY_NEWLINE); - - if (msc->ping_timeout == -1) - vty_out(vty, " no timeout-ping%s", VTY_NEWLINE); - else { - vty_out(vty, " timeout-ping %d%s", msc->ping_timeout, VTY_NEWLINE); - vty_out(vty, " timeout-pong %d%s", msc->pong_timeout, VTY_NEWLINE); - if (msc->advanced_ping) - vty_out(vty, " timeout-ping advanced%s", VTY_NEWLINE); - else - vty_out(vty, " no timeout-ping advanced%s", VTY_NEWLINE); - } - - if (msc->ussd_welcome_txt) - vty_out(vty, " bsc-welcome-text %s%s", msc->ussd_welcome_txt, VTY_NEWLINE); - else - vty_out(vty, " no bsc-welcome-text%s", VTY_NEWLINE); - - if (msc->ussd_msc_lost_txt && msc->ussd_msc_lost_txt[0]) - vty_out(vty, " bsc-msc-lost-text %s%s", msc->ussd_msc_lost_txt, VTY_NEWLINE); - else - vty_out(vty, " no bsc-msc-lost-text%s", VTY_NEWLINE); - - if (msc->ussd_grace_txt && msc->ussd_grace_txt[0]) - vty_out(vty, " bsc-grace-text %s%s", msc->ussd_grace_txt, VTY_NEWLINE); - else - vty_out(vty, " no bsc-grace-text%s", VTY_NEWLINE); - - if (msc->audio_length != 0) { - int i; - - vty_out(vty, " codec-list "); - for (i = 0; i < msc->audio_length; ++i) { - if (i != 0) - vty_out(vty, " "); - - if (msc->audio_support[i]->hr) - vty_out(vty, "hr%.1u", msc->audio_support[i]->ver); - else - vty_out(vty, "fr%.1u", msc->audio_support[i]->ver); - } - vty_out(vty, "%s", VTY_NEWLINE); - - } - - llist_for_each_entry(dest, &msc->dests, list) - vty_out(vty, " dest %s %d %d%s", dest->ip, dest->port, - dest->dscp, VTY_NEWLINE); - - vty_out(vty, " type %s%s", msc->type == MSC_CON_TYPE_NORMAL ? - "normal" : "local", VTY_NEWLINE); - vty_out(vty, " allow-emergency %s%s", msc->allow_emerg ? - "allow" : "deny", VTY_NEWLINE); - - if (msc->local_pref) - vty_out(vty, " local-prefix %s%s", msc->local_pref, VTY_NEWLINE); - - if (msc->acc_lst_name) - vty_out(vty, " access-list-name %s%s", msc->acc_lst_name, VTY_NEWLINE); - - /* write amr options */ - write_msc_amr_options(vty, msc); -} - -static int config_write_msc(struct vty *vty) -{ - struct bsc_msc_data *msc; - struct osmo_bsc_data *bsc = osmo_bsc_data(vty); - - llist_for_each_entry(msc, &bsc->mscs, entry) - write_msc(vty, msc); - - return CMD_SUCCESS; -} - -static int config_write_bsc(struct vty *vty) -{ - struct osmo_bsc_data *bsc = osmo_bsc_data(vty); - - vty_out(vty, "bsc%s", VTY_NEWLINE); - if (bsc->mid_call_txt) - vty_out(vty, " mid-call-text %s%s", bsc->mid_call_txt, VTY_NEWLINE); - vty_out(vty, " mid-call-timeout %d%s", bsc->mid_call_timeout, VTY_NEWLINE); - if (bsc->rf_ctrl_name) - vty_out(vty, " bsc-rf-socket %s%s", - bsc->rf_ctrl_name, VTY_NEWLINE); - - if (bsc->auto_off_timeout != -1) - vty_out(vty, " bsc-auto-rf-off %d%s", - bsc->auto_off_timeout, VTY_NEWLINE); - - if (bsc->ussd_no_msc_txt && bsc->ussd_no_msc_txt[0]) - vty_out(vty, " missing-msc-text %s%s", bsc->ussd_no_msc_txt, VTY_NEWLINE); - else - vty_out(vty, " no missing-msc-text%s", VTY_NEWLINE); - if (bsc->acc_lst_name) - vty_out(vty, " access-list-name %s%s", bsc->acc_lst_name, VTY_NEWLINE); - - return CMD_SUCCESS; -} - -DEFUN(cfg_net_bsc_token, - cfg_net_bsc_token_cmd, - "token TOKEN", - "A token for the BSC to be sent to the MSC\n" "A token\n") -{ - struct bsc_msc_data *data = bsc_msc_data(vty); - - osmo_talloc_replace_string(osmo_bsc_data(vty), &data->bsc_token, argv[0]); - return CMD_SUCCESS; -} - -DEFUN(cfg_net_bsc_key, - cfg_net_bsc_key_cmd, - "auth-key KEY", - "Authentication (secret) key configuration\n" - "Security key\n") -{ - struct bsc_msc_data *data = bsc_msc_data(vty); - - osmo_hexparse(argv[0], data->bsc_key, sizeof(data->bsc_key)); - data->bsc_key_present = 1; - return CMD_SUCCESS; -} - -DEFUN(cfg_net_no_bsc_key, cfg_net_bsc_no_key_cmd, - "no auth-key", - NO_STR "Authentication (secret) key configuration\n") -{ - struct bsc_msc_data *data = bsc_msc_data(vty); - - memset(data->bsc_key, 0, sizeof(data->bsc_key)); - data->bsc_key_present = 0; - return CMD_SUCCESS; -} - -DEFUN(cfg_net_bsc_ncc, - cfg_net_bsc_ncc_cmd, - "core-mobile-network-code <1-999>", - "Use this network code for the core network\n" "MNC value\n") -{ - struct bsc_msc_data *data = bsc_msc_data(vty); - data->core_mnc = atoi(argv[0]); - return CMD_SUCCESS; -} - -DEFUN(cfg_net_bsc_mcc, - cfg_net_bsc_mcc_cmd, - "core-mobile-country-code <1-999>", - "Use this country code for the core network\n" "MCC value\n") -{ - struct bsc_msc_data *data = bsc_msc_data(vty); - data->core_mcc = atoi(argv[0]); - return CMD_SUCCESS; -} - -DEFUN(cfg_net_bsc_lac, - cfg_net_bsc_lac_cmd, - "core-location-area-code <0-65535>", - "Use this location area code for the core network\n" "LAC value\n") -{ - struct bsc_msc_data *data = bsc_msc_data(vty); - data->core_lac = atoi(argv[0]); - return CMD_SUCCESS; -} - -DEFUN(cfg_net_bsc_ci, - cfg_net_bsc_ci_cmd, - "core-cell-identity <0-65535>", - "Use this cell identity for the core network\n" "CI value\n") -{ - struct bsc_msc_data *data = bsc_msc_data(vty); - data->core_ci = atoi(argv[0]); - return CMD_SUCCESS; -} - -DEFUN(cfg_net_bsc_rtp_base, - cfg_net_bsc_rtp_base_cmd, - "ip.access rtp-base <1-65000>", - IPA_STR - "Set the rtp-base port for the RTP stream\n" - "Port number\n") -{ - struct bsc_msc_data *data = bsc_msc_data(vty); - data->rtp_base = atoi(argv[0]); - return CMD_SUCCESS; -} - -DEFUN(cfg_net_bsc_codec_list, - cfg_net_bsc_codec_list_cmd, - "codec-list .LIST", - "Set the allowed audio codecs\n" - "List of audio codecs, e.g. fr3 fr1 hr3\n") -{ - struct bsc_msc_data *data = bsc_msc_data(vty); - int saw_fr, saw_hr; - int i; - - saw_fr = saw_hr = 0; - - /* free the old list... if it exists */ - if (data->audio_support) { - talloc_free(data->audio_support); - data->audio_support = NULL; - data->audio_length = 0; - } - - /* create a new array */ - data->audio_support = - talloc_zero_array(osmo_bsc_data(vty), struct gsm_audio_support *, argc); - data->audio_length = argc; - - for (i = 0; i < argc; ++i) { - /* check for hrX or frX */ - if (strlen(argv[i]) != 3 - || argv[i][1] != 'r' - || (argv[i][0] != 'h' && argv[i][0] != 'f') - || argv[i][2] < 0x30 - || argv[i][2] > 0x39) - goto error; - - data->audio_support[i] = talloc_zero(data->audio_support, - struct gsm_audio_support); - data->audio_support[i]->ver = atoi(argv[i] + 2); - - if (strncmp("hr", argv[i], 2) == 0) { - data->audio_support[i]->hr = 1; - saw_hr = 1; - } else if (strncmp("fr", argv[i], 2) == 0) { - data->audio_support[i]->hr = 0; - saw_fr = 1; - } - - if (saw_hr && saw_fr) { - vty_out(vty, "Can not have full-rate and half-rate codec.%s", - VTY_NEWLINE); - return CMD_ERR_INCOMPLETE; - } - } - - return CMD_SUCCESS; - -error: - vty_out(vty, "Codec name must be hrX or frX. Was '%s'%s", - argv[i], VTY_NEWLINE); - return CMD_ERR_INCOMPLETE; -} - -DEFUN(cfg_net_msc_dest, - cfg_net_msc_dest_cmd, - "dest A.B.C.D <1-65000> <0-255>", - "Add a destination to a MUX/MSC\n" - "IP Address\n" "Port\n" "DSCP\n") -{ - struct bsc_msc_dest *dest; - struct bsc_msc_data *data = bsc_msc_data(vty); - - dest = talloc_zero(osmo_bsc_data(vty), struct bsc_msc_dest); - if (!dest) { - vty_out(vty, "%%Failed to create structure.%s", VTY_NEWLINE); - return CMD_WARNING; - } - - dest->ip = talloc_strdup(dest, argv[0]); - if (!dest->ip) { - vty_out(vty, "%%Failed to copy dest ip.%s", VTY_NEWLINE); - talloc_free(dest); - return CMD_WARNING; - } - - dest->port = atoi(argv[1]); - dest->dscp = atoi(argv[2]); - llist_add_tail(&dest->list, &data->dests); - return CMD_SUCCESS; -} - -DEFUN(cfg_net_msc_no_dest, - cfg_net_msc_no_dest_cmd, - "no dest A.B.C.D <1-65000> <0-255>", - NO_STR "Remove a destination to a MUX/MSC\n" - "IP Address\n" "Port\n" "DSCP\n") -{ - struct bsc_msc_dest *dest, *tmp; - struct bsc_msc_data *data = bsc_msc_data(vty); - - int port = atoi(argv[1]); - int dscp = atoi(argv[2]); - - llist_for_each_entry_safe(dest, tmp, &data->dests, list) { - if (port != dest->port || dscp != dest->dscp - || strcmp(dest->ip, argv[0]) != 0) - continue; - - llist_del(&dest->list); - talloc_free(dest); - } - - return CMD_SUCCESS; -} - -DEFUN(cfg_net_msc_no_ping_time, - cfg_net_msc_no_ping_time_cmd, - "no timeout-ping", - NO_STR "Disable the ping/pong handling on A-link\n") -{ - struct bsc_msc_data *data = bsc_msc_data(vty); - data->ping_timeout = -1; - return CMD_SUCCESS; -} - -DEFUN(cfg_net_msc_ping_time, - cfg_net_msc_ping_time_cmd, - "timeout-ping <1-2147483647>", - "Set the PING interval, negative for not sending PING\n" - "Timeout in seconds\n") -{ - struct bsc_msc_data *data = bsc_msc_data(vty); - data->ping_timeout = atoi(argv[0]); - return CMD_SUCCESS; -} - -DEFUN(cfg_net_msc_pong_time, - cfg_net_msc_pong_time_cmd, - "timeout-pong <1-2147483647>", - "Set the time to wait for a PONG\n" "Timeout in seconds\n") -{ - struct bsc_msc_data *data = bsc_msc_data(vty); - data->pong_timeout = atoi(argv[0]); - return CMD_SUCCESS; -} - -DEFUN(cfg_net_msc_advanced_ping, - cfg_net_msc_advanced_ping_cmd, - "timeout-ping advanced", - "Ping timeout handling\nEnable advanced mode during SCCP\n") -{ - struct bsc_msc_data *data = bsc_msc_data(vty); - - if (data->ping_timeout == -1) { - vty_out(vty, "%%ping handling is disabled. Enable it first.%s", - VTY_NEWLINE); - return CMD_WARNING; - } - - data->advanced_ping = 1; - return CMD_SUCCESS; -} - -DEFUN(cfg_no_net_msc_advanced_ping, - cfg_no_net_msc_advanced_ping_cmd, - "no timeout-ping advanced", - NO_STR "Ping timeout handling\nEnable advanced mode during SCCP\n") -{ - struct bsc_msc_data *data = bsc_msc_data(vty); - data->advanced_ping = 0; - return CMD_SUCCESS; -} - -DEFUN(cfg_net_msc_welcome_ussd, - cfg_net_msc_welcome_ussd_cmd, - "bsc-welcome-text .TEXT", - "Set the USSD notification to be sent\n" "Text to be sent\n") -{ - struct bsc_msc_data *data = bsc_msc_data(vty); - char *str = argv_concat(argv, argc, 0); - if (!str) - return CMD_WARNING; - - osmo_talloc_replace_string(osmo_bsc_data(vty), &data->ussd_welcome_txt, str); - talloc_free(str); - return CMD_SUCCESS; -} - -DEFUN(cfg_net_msc_no_welcome_ussd, - cfg_net_msc_no_welcome_ussd_cmd, - "no bsc-welcome-text", - NO_STR "Clear the USSD notification to be sent\n") -{ - struct bsc_msc_data *data = bsc_msc_data(vty); - - talloc_free(data->ussd_welcome_txt); - data->ussd_welcome_txt = NULL; - - return CMD_SUCCESS; -} - -DEFUN(cfg_net_msc_lost_ussd, - cfg_net_msc_lost_ussd_cmd, - "bsc-msc-lost-text .TEXT", - "Set the USSD notification to be sent on MSC connection loss\n" "Text to be sent\n") -{ - struct bsc_msc_data *data = bsc_msc_data(vty); - char *str = argv_concat(argv, argc, 0); - if (!str) - return CMD_WARNING; - - osmo_talloc_replace_string(osmo_bsc_data(vty), &data->ussd_msc_lost_txt, str); - talloc_free(str); - return CMD_SUCCESS; -} - -DEFUN(cfg_net_msc_no_lost_ussd, - cfg_net_msc_no_lost_ussd_cmd, - "no bsc-msc-lost-text", - NO_STR "Clear the USSD notification to be sent on MSC connection loss\n") -{ - struct bsc_msc_data *data = bsc_msc_data(vty); - - talloc_free(data->ussd_msc_lost_txt); - data->ussd_msc_lost_txt = 0; - - return CMD_SUCCESS; -} - -DEFUN(cfg_net_msc_grace_ussd, - cfg_net_msc_grace_ussd_cmd, - "bsc-grace-text .TEXT", - "Set the USSD notification to be sent when the MSC has entered the grace period\n" "Text to be sent\n") -{ - struct bsc_msc_data *data = bsc_msc_data(vty); - char *str = argv_concat(argv, argc, 0); - if (!str) - return CMD_WARNING; - - osmo_talloc_replace_string(osmo_bsc_data(vty), &data->ussd_grace_txt, str); - talloc_free(str); - return CMD_SUCCESS; -} - -DEFUN(cfg_net_msc_no_grace_ussd, - cfg_net_msc_no_grace_ussd_cmd, - "no bsc-grace-text", - NO_STR "Clear the USSD notification to be sent when the MSC has entered the grace period\n") -{ - struct bsc_msc_data *data = bsc_msc_data(vty); - - talloc_free(data->ussd_grace_txt); - data->ussd_grace_txt = NULL; - - return CMD_SUCCESS; -} - -DEFUN(cfg_net_bsc_missing_msc_ussd, - cfg_net_bsc_missing_msc_ussd_cmd, - "missing-msc-text .TEXT", - "Set the USSD notification to be send when a MSC has not been found.\n" "Text to be sent\n") -{ - struct osmo_bsc_data *data = osmo_bsc_data(vty); - char *txt = argv_concat(argv, argc, 0); - if (!txt) - return CMD_WARNING; - - osmo_talloc_replace_string(data, &data->ussd_no_msc_txt, txt); - talloc_free(txt); - return CMD_SUCCESS; -} - -DEFUN(cfg_net_bsc_no_missing_msc_text, - cfg_net_bsc_no_missing_msc_text_cmd, - "no missing-msc-text", - NO_STR "Clear the USSD notification to be send when a MSC has not been found.\n") -{ - struct osmo_bsc_data *data = osmo_bsc_data(vty); - - talloc_free(data->ussd_no_msc_txt); - data->ussd_no_msc_txt = 0; - - return CMD_SUCCESS; -} - - -DEFUN(cfg_net_msc_type, - cfg_net_msc_type_cmd, - "type (normal|local)", - "Select the MSC type\n" - "Plain GSM MSC\n" "Special MSC for local call routing\n") -{ - struct bsc_msc_data *data = bsc_msc_data(vty); - - if (strcmp(argv[0], "normal") == 0) - data->type = MSC_CON_TYPE_NORMAL; - else if (strcmp(argv[0], "local") == 0) - data->type = MSC_CON_TYPE_LOCAL; - - return CMD_SUCCESS; -} - -DEFUN(cfg_net_msc_emerg, - cfg_net_msc_emerg_cmd, - "allow-emergency (allow|deny)", - "Allow CM ServiceRequests with type emergency\n" - "Allow\n" "Deny\n") -{ - struct bsc_msc_data *data = bsc_msc_data(vty); - data->allow_emerg = strcmp("allow", argv[0]) == 0; - return CMD_SUCCESS; -} - -DEFUN(cfg_net_msc_local_prefix, - cfg_net_msc_local_prefix_cmd, - "local-prefix REGEXP", - "Prefix for local numbers\n" "REGEXP used\n") -{ - struct bsc_msc_data *msc = bsc_msc_data(vty); - - if (gsm_parse_reg(msc, &msc->local_pref_reg, &msc->local_pref, argc, argv) != 0) { - vty_out(vty, "%%Failed to parse the regexp: '%s'%s", - argv[0], VTY_NEWLINE); - return CMD_WARNING; - } - - return CMD_SUCCESS; -} - -#define AMR_CONF_STR "AMR Multirate Configuration\n" -#define AMR_COMMAND(name) \ - DEFUN(cfg_net_msc_amr_##name, \ - cfg_net_msc_amr_##name##_cmd, \ - "amr-config " #name "k (allowed|forbidden)", \ - AMR_CONF_STR "Bitrate\n" "Allowed\n" "Forbidden\n") \ -{ \ - struct bsc_msc_data *msc = bsc_msc_data(vty); \ - \ - msc->amr_conf.m##name = strcmp(argv[0], "allowed") == 0; \ - return CMD_SUCCESS; \ -} - -AMR_COMMAND(12_2) -AMR_COMMAND(10_2) -AMR_COMMAND(7_95) -AMR_COMMAND(7_40) -AMR_COMMAND(6_70) -AMR_COMMAND(5_90) -AMR_COMMAND(5_15) -AMR_COMMAND(4_75) - -DEFUN(cfg_msc_acc_lst_name, - cfg_msc_acc_lst_name_cmd, - "access-list-name NAME", - "Set the name of the access list to use.\n" - "The name of the to be used access list.") -{ - struct bsc_msc_data *msc = bsc_msc_data(vty); - - osmo_talloc_replace_string(msc, &msc->acc_lst_name, argv[0]); - return CMD_SUCCESS; -} - -DEFUN(cfg_msc_no_acc_lst_name, - cfg_msc_no_acc_lst_name_cmd, - "no access-list-name", - NO_STR "Remove the access list from the NAT.\n") -{ - struct bsc_msc_data *msc = bsc_msc_data(vty); - - if (msc->acc_lst_name) { - talloc_free(msc->acc_lst_name); - msc->acc_lst_name = NULL; - } - - return CMD_SUCCESS; -} - -DEFUN(cfg_net_bsc_mid_call_text, - cfg_net_bsc_mid_call_text_cmd, - "mid-call-text .TEXT", - "Set the USSD notification to be send.\n" "Text to be sent\n") -{ - struct osmo_bsc_data *data = osmo_bsc_data(vty); - char *txt = argv_concat(argv, argc, 0); - if (!txt) - return CMD_WARNING; - - osmo_talloc_replace_string(data, &data->mid_call_txt, txt); - talloc_free(txt); - return CMD_SUCCESS; -} - -DEFUN(cfg_net_bsc_mid_call_timeout, - cfg_net_bsc_mid_call_timeout_cmd, - "mid-call-timeout NR", - "Switch from Grace to Off in NR seconds.\n" "Timeout in seconds\n") -{ - struct osmo_bsc_data *data = osmo_bsc_data(vty); - data->mid_call_timeout = atoi(argv[0]); - return CMD_SUCCESS; -} - -DEFUN(cfg_net_rf_socket, - cfg_net_rf_socket_cmd, - "bsc-rf-socket PATH", - "Set the filename for the RF control interface.\n" "RF Control path\n") -{ - struct osmo_bsc_data *data = osmo_bsc_data(vty); - - osmo_talloc_replace_string(data, &data->rf_ctrl_name, argv[0]); - return CMD_SUCCESS; -} - -DEFUN(cfg_net_rf_off_time, - cfg_net_rf_off_time_cmd, - "bsc-auto-rf-off <1-65000>", - "Disable RF on MSC Connection\n" "Timeout\n") -{ - struct osmo_bsc_data *data = osmo_bsc_data(vty); - data->auto_off_timeout = atoi(argv[0]); - return CMD_SUCCESS; -} - -DEFUN(cfg_net_no_rf_off_time, - cfg_net_no_rf_off_time_cmd, - "no bsc-auto-rf-off", - NO_STR "Disable RF on MSC Connection\n") -{ - struct osmo_bsc_data *data = osmo_bsc_data(vty); - data->auto_off_timeout = -1; - return CMD_SUCCESS; -} - -DEFUN(cfg_bsc_acc_lst_name, - cfg_bsc_acc_lst_name_cmd, - "access-list-name NAME", - "Set the name of the access list to use.\n" - "The name of the to be used access list.") -{ - struct osmo_bsc_data *bsc = osmo_bsc_data(vty); - - osmo_talloc_replace_string(bsc, &bsc->acc_lst_name, argv[0]); - return CMD_SUCCESS; -} - -DEFUN(cfg_bsc_no_acc_lst_name, - cfg_bsc_no_acc_lst_name_cmd, - "no access-list-name", - NO_STR "Remove the access list from the BSC\n") -{ - struct osmo_bsc_data *bsc = osmo_bsc_data(vty); - - if (bsc->acc_lst_name) { - talloc_free(bsc->acc_lst_name); - bsc->acc_lst_name = NULL; - } - - return CMD_SUCCESS; -} - -DEFUN(show_statistics, - show_statistics_cmd, - "show statistics", - SHOW_STR "Statistics about the BSC\n") -{ - openbsc_vty_print_statistics(vty, bsc_gsmnet); - return CMD_SUCCESS; -} - -DEFUN(show_mscs, - show_mscs_cmd, - "show mscs", - SHOW_STR "MSC Connections and State\n") -{ - struct bsc_msc_data *msc; - llist_for_each_entry(msc, &bsc_gsmnet->bsc_data->mscs, entry) { - vty_out(vty, "MSC Nr: %d is connected: %d auth: %d.%s", - msc->nr, - msc->msc_con ? msc->msc_con->is_connected : -1, - msc->msc_con ? msc->msc_con->is_authenticated : -1, - VTY_NEWLINE); - } - - return CMD_SUCCESS; -} - -DEFUN(show_pos, - show_pos_cmd, - "show position", - SHOW_STR "Position information of the BTS\n") -{ - struct gsm_bts *bts; - struct bts_location *curloc; - struct tm time; - char timestr[50]; - - llist_for_each_entry(bts, &bsc_gsmnet->bts_list, list) { - if (llist_empty(&bts->loc_list)) { - vty_out(vty, "BTS Nr: %d position invalid%s", bts->nr, - VTY_NEWLINE); - continue; - } - curloc = llist_entry(bts->loc_list.next, struct bts_location, list); - if (gmtime_r(&curloc->tstamp, &time) == NULL) { - vty_out(vty, "Time conversion failed for BTS %d%s", bts->nr, - VTY_NEWLINE); - continue; - } - if (asctime_r(&time, timestr) == NULL) { - vty_out(vty, "Time conversion failed for BTS %d%s", bts->nr, - VTY_NEWLINE); - continue; - } - /* Last character in asctime is \n */ - timestr[strlen(timestr)-1] = 0; - - vty_out(vty, "BTS Nr: %d position: %s time: %s%s", bts->nr, - get_value_string(bts_loc_fix_names, curloc->valid), timestr, - VTY_NEWLINE); - vty_out(vty, " lat: %f lon: %f height: %f%s", curloc->lat, curloc->lon, - curloc->height, VTY_NEWLINE); - } - return CMD_SUCCESS; -} - -DEFUN(gen_position_trap, - gen_position_trap_cmd, - "generate-location-state-trap <0-255>", - "Generate location state report\n" - "BTS to report\n") -{ - int bts_nr; - struct gsm_bts *bts; - struct gsm_network *net = bsc_gsmnet; - - bts_nr = atoi(argv[0]); - if (bts_nr >= net->num_bts) { - vty_out(vty, "%% can't find BTS '%s'%s", argv[0], - VTY_NEWLINE); - return CMD_WARNING; - } - - bts = gsm_bts_num(net, bts_nr); - bsc_gen_location_state_trap(bts); - return CMD_SUCCESS; -} - -DEFUN(logging_fltr_imsi, - logging_fltr_imsi_cmd, - "logging filter imsi IMSI", - LOGGING_STR FILTER_STR - "Filter log messages by IMSI\n" "IMSI to be used as filter\n") -{ - struct bsc_subscr *bsc_subscr; - struct log_target *tgt = osmo_log_vty2tgt(vty); - const char *imsi = argv[0]; - - bsc_subscr = bsc_subscr_find_by_imsi(bsc_gsmnet->bsc_subscribers, imsi); - - if (!bsc_subscr) { - vty_out(vty, "%%no subscriber with IMSI(%s)%s", - imsi, VTY_NEWLINE); - return CMD_WARNING; - } - - log_set_filter_bsc_subscr(tgt, bsc_subscr); - return CMD_SUCCESS; -} - -int bsc_vty_init_extra(void) -{ - install_element(CONFIG_NODE, &cfg_net_msc_cmd); - install_element(CONFIG_NODE, &cfg_net_bsc_cmd); - - install_node(&bsc_node, config_write_bsc); - vty_install_default(BSC_NODE); - install_element(BSC_NODE, &cfg_net_bsc_mid_call_text_cmd); - install_element(BSC_NODE, &cfg_net_bsc_mid_call_timeout_cmd); - install_element(BSC_NODE, &cfg_net_rf_socket_cmd); - install_element(BSC_NODE, &cfg_net_rf_off_time_cmd); - install_element(BSC_NODE, &cfg_net_no_rf_off_time_cmd); - install_element(BSC_NODE, &cfg_net_bsc_missing_msc_ussd_cmd); - install_element(BSC_NODE, &cfg_net_bsc_no_missing_msc_text_cmd); - install_element(BSC_NODE, &cfg_bsc_acc_lst_name_cmd); - install_element(BSC_NODE, &cfg_bsc_no_acc_lst_name_cmd); - - install_node(&msc_node, config_write_msc); - vty_install_default(MSC_NODE); - install_element(MSC_NODE, &cfg_net_bsc_token_cmd); - install_element(MSC_NODE, &cfg_net_bsc_key_cmd); - install_element(MSC_NODE, &cfg_net_bsc_no_key_cmd); - install_element(MSC_NODE, &cfg_net_bsc_ncc_cmd); - install_element(MSC_NODE, &cfg_net_bsc_mcc_cmd); - install_element(MSC_NODE, &cfg_net_bsc_lac_cmd); - install_element(MSC_NODE, &cfg_net_bsc_ci_cmd); - install_element(MSC_NODE, &cfg_net_bsc_rtp_base_cmd); - install_element(MSC_NODE, &cfg_net_bsc_codec_list_cmd); - install_element(MSC_NODE, &cfg_net_msc_dest_cmd); - install_element(MSC_NODE, &cfg_net_msc_no_dest_cmd); - install_element(MSC_NODE, &cfg_net_msc_no_ping_time_cmd); - install_element(MSC_NODE, &cfg_net_msc_ping_time_cmd); - install_element(MSC_NODE, &cfg_net_msc_pong_time_cmd); - install_element(MSC_NODE, &cfg_net_msc_advanced_ping_cmd); - install_element(MSC_NODE, &cfg_no_net_msc_advanced_ping_cmd); - install_element(MSC_NODE, &cfg_net_msc_welcome_ussd_cmd); - install_element(MSC_NODE, &cfg_net_msc_no_welcome_ussd_cmd); - install_element(MSC_NODE, &cfg_net_msc_lost_ussd_cmd); - install_element(MSC_NODE, &cfg_net_msc_no_lost_ussd_cmd); - install_element(MSC_NODE, &cfg_net_msc_grace_ussd_cmd); - install_element(MSC_NODE, &cfg_net_msc_no_grace_ussd_cmd); - install_element(MSC_NODE, &cfg_net_msc_type_cmd); - install_element(MSC_NODE, &cfg_net_msc_emerg_cmd); - install_element(MSC_NODE, &cfg_net_msc_local_prefix_cmd); - install_element(MSC_NODE, &cfg_net_msc_amr_12_2_cmd); - install_element(MSC_NODE, &cfg_net_msc_amr_10_2_cmd); - install_element(MSC_NODE, &cfg_net_msc_amr_7_95_cmd); - install_element(MSC_NODE, &cfg_net_msc_amr_7_40_cmd); - install_element(MSC_NODE, &cfg_net_msc_amr_6_70_cmd); - install_element(MSC_NODE, &cfg_net_msc_amr_5_90_cmd); - install_element(MSC_NODE, &cfg_net_msc_amr_5_15_cmd); - install_element(MSC_NODE, &cfg_net_msc_amr_4_75_cmd); - install_element(MSC_NODE, &cfg_msc_acc_lst_name_cmd); - install_element(MSC_NODE, &cfg_msc_no_acc_lst_name_cmd); - - install_element_ve(&show_statistics_cmd); - install_element_ve(&show_mscs_cmd); - install_element_ve(&show_pos_cmd); - install_element_ve(&logging_fltr_imsi_cmd); - - install_element(ENABLE_NODE, &gen_position_trap_cmd); - - install_element(CFG_LOG_NODE, &logging_fltr_imsi_cmd); - - return 0; -} diff --git a/openbsc/src/osmo-bsc_mgcp/Makefile.am b/openbsc/src/osmo-bsc_mgcp/Makefile.am deleted file mode 100644 index a19a4ebc..00000000 --- a/openbsc/src/osmo-bsc_mgcp/Makefile.am +++ /dev/null @@ -1,35 +0,0 @@ -AM_CPPFLAGS = \ - $(all_includes) \ - -I$(top_srcdir)/include \ - -I$(top_builddir) \ - $(NULL) - -AM_CFLAGS = \ - -Wall \ - $(LIBOSMOCORE_CFLAGS) \ - $(LIBOSMOGSM_CFLAGS) \ - $(LIBOSMOVTY_CFLAGS) \ - $(LIBOSMOABIS_CFLAGS) \ - $(LIBOSMONETIF_CFLAGS) \ - $(COVERAGE_CFLAGS) \ - $(NULL) - -bin_PROGRAMS = \ - osmo-bsc_mgcp \ - $(NULL) - -osmo_bsc_mgcp_SOURCES = \ - mgcp_main.c \ - $(NULL) - -osmo_bsc_mgcp_LDADD = \ - $(top_builddir)/src/libcommon/libcommon.a \ - $(top_builddir)/src/libmgcp/libmgcp.a \ - $(LIBOSMOVTY_LIBS) \ - $(LIBOSMOCORE_LIBS) \ - $(LIBOSMOGSM_LIBS) \ - $(LIBOSMONETIF_LIBS) \ - $(LIBBCG729_LIBS) \ - $(LIBRARY_GSM) \ - -lrt \ - $(NULL) diff --git a/openbsc/src/osmo-bsc_mgcp/mgcp_main.c b/openbsc/src/osmo-bsc_mgcp/mgcp_main.c deleted file mode 100644 index 4ea07007..00000000 --- a/openbsc/src/osmo-bsc_mgcp/mgcp_main.c +++ /dev/null @@ -1,311 +0,0 @@ -/* A Media Gateway Control Protocol Media Gateway: RFC 3435 */ -/* The main method to drive it as a standalone process */ - -/* - * (C) 2009-2011 by Holger Hans Peter Freyther - * (C) 2009-2011 by On-Waves - * All Rights Reserved - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation; either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - * - */ - -#include -#include -#include -#include -#include -#include -#include - -#include - -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include - -#include "../../bscconfig.h" - -#ifdef BUILD_MGCP_TRANSCODING -#include "openbsc/mgcp_transcode.h" -#endif - -#define _GNU_SOURCE -#include - -#warning "Make use of the rtp proxy code" - -static struct mgcp_config *cfg; -static struct mgcp_trunk_config *reset_trunk; -static int reset_endpoints = 0; -static int daemonize = 0; - -const char *openbsc_copyright = - "Copyright (C) 2009-2010 Holger Freyther and On-Waves\r\n" - "Contributions by Daniel Willmann, Jan Lübbe, Stefan Schmidt\r\n" - "Dieter Spaar, Andreas Eversberg, Harald Welte\r\n\r\n" - "License AGPLv3+: GNU AGPL version 3 or later \r\n" - "This is free software: you are free to change and redistribute it.\r\n" - "There is NO WARRANTY, to the extent permitted by law.\r\n"; - -static char *config_file = "mgcp.cfg"; - -/* used by msgb and mgcp */ -void *tall_bsc_ctx = NULL; - -static void print_help() -{ - printf("Some useful help...\n"); - printf(" -h --help is printing this text.\n"); - printf(" -c --config-file filename The config file to use.\n"); - printf(" -s --disable-color\n"); - printf(" -D --daemonize Fork the process into a background daemon\n"); - printf(" -V --version Print the version number\n"); -} - -static void handle_options(int argc, char **argv) -{ - while (1) { - int option_index = 0, c; - static struct option long_options[] = { - {"help", 0, 0, 'h'}, - {"config-file", 1, 0, 'c'}, - {"daemonize", 0, 0, 'D'}, - {"version", 0, 0, 'V'}, - {"disable-color", 0, 0, 's'}, - {0, 0, 0, 0}, - }; - - c = getopt_long(argc, argv, "hc:VD", long_options, &option_index); - - if (c == -1) - break; - - switch(c) { - case 'h': - print_help(); - exit(0); - break; - case 'c': - config_file = talloc_strdup(tall_bsc_ctx, optarg); - break; - case 's': - log_set_use_color(osmo_stderr_target, 0); - break; - case 'V': - print_version(1); - exit(0); - break; - case 'D': - daemonize = 1; - break; - default: - /* ignore */ - break; - }; - } -} - -/* simply remember this */ -static int mgcp_rsip_cb(struct mgcp_trunk_config *tcfg) -{ - reset_endpoints = 1; - reset_trunk = tcfg; - - return 0; -} - -static int read_call_agent(struct osmo_fd *fd, unsigned int what) -{ - struct sockaddr_in addr; - socklen_t slen = sizeof(addr); - struct msgb *msg; - struct msgb *resp; - int i; - - msg = (struct msgb *) fd->data; - - /* read one less so we can use it as a \0 */ - int rc = recvfrom(cfg->gw_fd.bfd.fd, msg->data, msg->data_len - 1, 0, - (struct sockaddr *) &addr, &slen); - if (rc < 0) { - perror("Gateway failed to read"); - return -1; - } else if (slen > sizeof(addr)) { - fprintf(stderr, "Gateway received message from outerspace: %zu %zu\n", - (size_t) slen, sizeof(addr)); - return -1; - } - - /* handle message now */ - msg->l2h = msgb_put(msg, rc); - resp = mgcp_handle_message(cfg, msg); - msgb_reset(msg); - - if (resp) { - sendto(cfg->gw_fd.bfd.fd, resp->l2h, msgb_l2len(resp), 0, (struct sockaddr *) &addr, sizeof(addr)); - msgb_free(resp); - } - - if (reset_endpoints) { - LOGP(DMGCP, LOGL_NOTICE, - "Asked to reset endpoints: %d/%d\n", - reset_trunk->trunk_nr, reset_trunk->trunk_type); - reset_endpoints = 0; - - /* is checking in_addr.s_addr == INADDR_LOOPBACK making it more secure? */ - for (i = 1; i < reset_trunk->number_endpoints; ++i) - mgcp_release_endp(&reset_trunk->endpoints[i]); - } - - return 0; -} - -extern int bsc_vty_go_parent(struct vty *vty); - -static struct vty_app_info vty_info = { - .name = "OpenBSC MGCP", - .version = PACKAGE_VERSION, - .go_parent_cb = bsc_vty_go_parent, - .is_config_node = bsc_vty_is_config_node, -}; - -int main(int argc, char **argv) -{ - struct gsm_network dummy_network; - struct sockaddr_in addr; - int on = 1, rc; - - tall_bsc_ctx = talloc_named_const(NULL, 1, "mgcp-callagent"); - msgb_talloc_ctx_init(tall_bsc_ctx, 0); - - osmo_init_ignore_signals(); - osmo_init_logging(&log_info); - - cfg = mgcp_config_alloc(); - if (!cfg) - return -1; - -#ifdef BUILD_MGCP_TRANSCODING - cfg->setup_rtp_processing_cb = &mgcp_transcoding_setup; - cfg->rtp_processing_cb = &mgcp_transcoding_process_rtp; - cfg->get_net_downlink_format_cb = &mgcp_transcoding_net_downlink_format; -#endif - - vty_info.copyright = openbsc_copyright; - vty_init(&vty_info); - logging_vty_add_cmds(NULL); - osmo_stats_vty_add_cmds(&log_info); - mgcp_vty_init(); - - handle_options(argc, argv); - - rate_ctr_init(tall_bsc_ctx); - osmo_stats_init(tall_bsc_ctx); - - rc = mgcp_parse_config(config_file, cfg, MGCP_BSC); - if (rc < 0) - return rc; - - /* start telnet after reading config for vty_get_bind_addr() */ - rc = telnet_init_dynif(tall_bsc_ctx, &dummy_network, - vty_get_bind_addr(), OSMO_VTY_PORT_BSC_MGCP); - if (rc < 0) - return rc; - - /* set some callbacks */ - cfg->reset_cb = mgcp_rsip_cb; - - /* we need to bind a socket */ - if (rc == 0) { - cfg->gw_fd.bfd.when = BSC_FD_READ; - cfg->gw_fd.bfd.cb = read_call_agent; - cfg->gw_fd.bfd.fd = socket(AF_INET, SOCK_DGRAM, 0); - if (cfg->gw_fd.bfd.fd < 0) { - perror("Gateway failed to listen"); - return -1; - } - - setsockopt(cfg->gw_fd.bfd.fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)); - - memset(&addr, 0, sizeof(addr)); - addr.sin_family = AF_INET; - addr.sin_port = htons(cfg->source_port); - inet_aton(cfg->source_addr, &addr.sin_addr); - - if (bind(cfg->gw_fd.bfd.fd, (struct sockaddr *) &addr, sizeof(addr)) < 0) { - perror("Gateway failed to bind"); - return -1; - } - - cfg->gw_fd.bfd.data = msgb_alloc(4096, "mgcp-msg"); - if (!cfg->gw_fd.bfd.data) { - fprintf(stderr, "Gateway memory error.\n"); - return -1; - } - - if (cfg->call_agent_addr) { - addr.sin_port = htons(2727); - inet_aton(cfg->call_agent_addr, &addr.sin_addr); - if (connect(cfg->gw_fd.bfd.fd, (struct sockaddr *) &addr, sizeof(addr)) < 0) { - LOGP(DMGCP, LOGL_ERROR, "Failed to connect to: '%s'. errno: %d\n", - cfg->call_agent_addr, errno); - close(cfg->gw_fd.bfd.fd); - cfg->gw_fd.bfd.fd = -1; - return -1; - } - } - - if (osmo_fd_register(&cfg->gw_fd.bfd) != 0) { - LOGP(DMGCP, LOGL_FATAL, "Failed to register the fd\n"); - return -1; - } - - LOGP(DMGCP, LOGL_NOTICE, "Configured for MGCP.\n"); - } - - /* initialisation */ - srand(time(NULL)); - - if (daemonize) { - rc = osmo_daemonize(); - if (rc < 0) { - perror("Error during daemonize"); - exit(1); - } - } - - /* main loop */ - while (1) { - osmo_select_main(0); - } - - - return 0; -} diff --git a/openbsc/src/osmo-bsc_nat/Makefile.am b/openbsc/src/osmo-bsc_nat/Makefile.am deleted file mode 100644 index be33d289..00000000 --- a/openbsc/src/osmo-bsc_nat/Makefile.am +++ /dev/null @@ -1,58 +0,0 @@ -AM_CPPFLAGS = \ - $(all_includes) \ - -I$(top_srcdir)/include \ - -I$(top_builddir) \ - $(NULL) - -AM_CFLAGS = \ - -Wall \ - $(LIBOSMOCORE_CFLAGS) \ - $(LIBOSMOGSM_CFLAGS) \ - $(LIBOSMOVTY_CFLAGS) \ - $(LIBOSMOCTRL_CFLAGS) \ - $(LIBOSMOSCCP_CFLAGS) \ - $(LIBOSMOABIS_CFLAGS) \ - $(LIBOSMONETIF_CFLAGS) \ - $(LIBCRYPTO_CFLAGS) \ - $(COVERAGE_CFLAGS) \ - $(NULL) - -AM_LDFLAGS = \ - $(COVERAGE_LDFLAGS) \ - $(NULL) - -bin_PROGRAMS = \ - osmo-bsc_nat \ - $(NULL) - -osmo_bsc_nat_SOURCES = \ - bsc_filter.c \ - bsc_mgcp_utils.c \ - bsc_nat.c \ - bsc_nat_utils.c \ - bsc_nat_vty.c \ - bsc_sccp.c \ - bsc_ussd.c \ - bsc_nat_ctrl.c \ - bsc_nat_rewrite.c \ - bsc_nat_rewrite_trie.c \ - bsc_nat_filter.c \ - $(NULL) - -osmo_bsc_nat_LDADD = \ - $(top_builddir)/src/libmgcp/libmgcp.a \ - $(top_builddir)/src/libfilter/libfilter.a \ - $(top_builddir)/src/libbsc/libbsc.a \ - $(top_builddir)/src/libcommon-cs/libcommon-cs.a \ - $(top_builddir)/src/libtrau/libtrau.a \ - $(top_builddir)/src/libcommon/libcommon.a \ - $(LIBOSMOSCCP_LIBS) \ - $(LIBOSMOCORE_LIBS) \ - $(LIBOSMOGSM_LIBS) \ - $(LIBOSMOVTY_LIBS) \ - $(LIBOSMOCTRL_LIBS) \ - $(LIBOSMOABIS_LIBS) \ - $(LIBOSMONETIF_LIBS) \ - $(LIBCRYPTO_LIBS) \ - -lrt \ - $(NULL) diff --git a/openbsc/src/osmo-bsc_nat/bsc_filter.c b/openbsc/src/osmo-bsc_nat/bsc_filter.c deleted file mode 100644 index 6a9e99fb..00000000 --- a/openbsc/src/osmo-bsc_nat/bsc_filter.c +++ /dev/null @@ -1,218 +0,0 @@ -/* BSC Multiplexer/NAT */ - -/* - * (C) 2010 by Holger Hans Peter Freyther - * (C) 2010 by On-Waves - * All Rights Reserved - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation; either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - * - */ - -#include -#include -#include -#include - -#include -#include - -#include - -/* - * The idea is to have a simple struct describing a IPA packet with - * SCCP SSN and the GSM 08.08 payload and decide. We will both have - * a white and a blacklist of packets we want to handle. - * - * TODO: Implement a "NOT" in the filter language. - */ - -#define ALLOW_ANY -1 - -#define FILTER_TO_BSC 1 -#define FILTER_TO_MSC 2 -#define FILTER_TO_BOTH 3 - - -struct bsc_pkt_filter { - int ipa_proto; - int dest_ssn; - int bssap; - int gsm; - int filter_dir; -}; - -static struct bsc_pkt_filter black_list[] = { - /* filter reset messages to the MSC */ - { IPAC_PROTO_SCCP, SCCP_SSN_BSSAP, 0, BSS_MAP_MSG_RESET, FILTER_TO_MSC }, - - /* filter reset ack messages to the BSC */ - { IPAC_PROTO_SCCP, SCCP_SSN_BSSAP, 0, BSS_MAP_MSG_RESET_ACKNOWLEDGE, FILTER_TO_BSC }, - - /* filter ip access */ - { IPAC_PROTO_IPACCESS, ALLOW_ANY, ALLOW_ANY, ALLOW_ANY, FILTER_TO_MSC }, -}; - -static struct bsc_pkt_filter white_list[] = { - /* allow IPAC_PROTO_SCCP messages to both sides */ - { IPAC_PROTO_SCCP, ALLOW_ANY, ALLOW_ANY, ALLOW_ANY, FILTER_TO_BOTH }, - - /* allow MGCP messages to both sides */ - { IPAC_PROTO_MGCP_OLD, ALLOW_ANY, ALLOW_ANY, ALLOW_ANY, FILTER_TO_BOTH }, -}; - -struct bsc_nat_parsed *bsc_nat_parse(struct msgb *msg) -{ - struct sccp_parse_result result; - struct bsc_nat_parsed *parsed; - struct ipaccess_head *hh; - - /* quick fail */ - if (msg->len < 4) - return NULL; - - parsed = talloc_zero(msg, struct bsc_nat_parsed); - if (!parsed) - return NULL; - - /* more init */ - parsed->ipa_proto = parsed->called_ssn = parsed->calling_ssn = -1; - parsed->sccp_type = parsed->bssap = parsed->gsm_type = -1; - - /* start parsing */ - hh = (struct ipaccess_head *) msg->data; - parsed->ipa_proto = hh->proto; - - msg->l2h = &hh->data[0]; - - /* do a size check on the input */ - if (ntohs(hh->len) != msgb_l2len(msg)) { - LOGP(DLINP, LOGL_ERROR, "Wrong input length?\n"); - talloc_free(parsed); - return NULL; - } - - /* analyze sccp down here */ - if (parsed->ipa_proto == IPAC_PROTO_SCCP) { - memset(&result, 0, sizeof(result)); - if (sccp_parse_header(msg, &result) != 0) { - talloc_free(parsed); - return 0; - } - - if (msg->l3h && msgb_l3len(msg) < 3) { - LOGP(DNAT, LOGL_ERROR, "Not enough space or GSM payload\n"); - talloc_free(parsed); - return 0; - } - - parsed->sccp_type = sccp_determine_msg_type(msg); - parsed->src_local_ref = result.source_local_reference; - parsed->dest_local_ref = result.destination_local_reference; - if (parsed->dest_local_ref) - parsed->original_dest_ref = *parsed->dest_local_ref; - parsed->called_ssn = result.called.ssn; - parsed->calling_ssn = result.calling.ssn; - - /* in case of connection confirm we have no payload */ - if (msg->l3h) { - parsed->bssap = msg->l3h[0]; - parsed->gsm_type = msg->l3h[2]; - } - } - - return parsed; -} - -int bsc_nat_filter_ipa(int dir, struct msgb *msg, struct bsc_nat_parsed *parsed) -{ - int i; - - /* go through the blacklist now */ - for (i = 0; i < ARRAY_SIZE(black_list); ++i) { - /* ignore the rule? */ - if (black_list[i].filter_dir != FILTER_TO_BOTH - && black_list[i].filter_dir != dir) - continue; - - /* the proto is not blacklisted */ - if (black_list[i].ipa_proto != ALLOW_ANY - && black_list[i].ipa_proto != parsed->ipa_proto) - continue; - - if (parsed->ipa_proto == IPAC_PROTO_SCCP) { - /* the SSN is not blacklisted */ - if (black_list[i].dest_ssn != ALLOW_ANY - && black_list[i].dest_ssn != parsed->called_ssn) - continue; - - /* bssap */ - if (black_list[i].bssap != ALLOW_ANY - && black_list[i].bssap != parsed->bssap) - continue; - - /* gsm */ - if (black_list[i].gsm != ALLOW_ANY - && black_list[i].gsm != parsed->gsm_type) - continue; - - /* blacklisted */ - LOGP(DNAT, LOGL_INFO, "Blacklisted with rule %d\n", i); - return 1; - } else { - /* blacklisted, we have no content sniffing yet */ - LOGP(DNAT, LOGL_INFO, "Blacklisted with rule %d\n", i); - return 1; - } - } - - /* go through the whitelust now */ - for (i = 0; i < ARRAY_SIZE(white_list); ++i) { - /* ignore the rule? */ - if (white_list[i].filter_dir != FILTER_TO_BOTH - && white_list[i].filter_dir != dir) - continue; - - /* the proto is not whitelisted */ - if (white_list[i].ipa_proto != ALLOW_ANY - && white_list[i].ipa_proto != parsed->ipa_proto) - continue; - - if (parsed->ipa_proto == IPAC_PROTO_SCCP) { - /* the SSN is not whitelisted */ - if (white_list[i].dest_ssn != ALLOW_ANY - && white_list[i].dest_ssn != parsed->called_ssn) - continue; - - /* bssap */ - if (white_list[i].bssap != ALLOW_ANY - && white_list[i].bssap != parsed->bssap) - continue; - - /* gsm */ - if (white_list[i].gsm != ALLOW_ANY - && white_list[i].gsm != parsed->gsm_type) - continue; - - /* whitelisted */ - LOGP(DNAT, LOGL_INFO, "Whitelisted with rule %d\n", i); - return 0; - } else { - /* whitelisted */ - return 0; - } - } - - return 1; -} diff --git a/openbsc/src/osmo-bsc_nat/bsc_mgcp_utils.c b/openbsc/src/osmo-bsc_nat/bsc_mgcp_utils.c deleted file mode 100644 index 48847865..00000000 --- a/openbsc/src/osmo-bsc_nat/bsc_mgcp_utils.c +++ /dev/null @@ -1,1152 +0,0 @@ -/** - * This file contains helper routines for MGCP Gateway handling. - * - * The first thing to remember is that each BSC has its own namespace/range - * of endpoints. Whenever a BSSMAP ASSIGNMENT REQUEST is received this code - * will be called to select an endpoint on the BSC. The mapping from original - * multiplex/timeslot to BSC multiplex'/timeslot' will be stored. - * - * The second part is to take messages on the public MGCP GW interface - * and forward them to the right BSC. This requires the MSC to first - * assign the timeslot. This assumption has been true so far. We are using - * the policy_cb of the MGCP protocol code to decide if the request should - * be immediately answered or delayed. An extension "Z: noanswer" is used - * to request the BSC to not respond. This is saving some bytes of bandwidth - * and as we are using TCP to forward the message we know it will arrive. - * The mgcp_do_read method reads these messages and hands them to the protocol - * parsing code which will call the mentioned policy_cb. The bsc_mgcp_forward - * method is used on the way back from the BSC to the network. - * - * The third part is to patch messages forwarded to the BSC. This includes - * the endpoint number, the ports to be used inside the SDP file and maybe - * some other bits. - * - */ -/* - * (C) 2010-2012 by Holger Hans Peter Freyther - * (C) 2010-2012 by On-Waves - * All Rights Reserved - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation; either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - * - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -#include - -#include -#include -#include - -#include -#include - -#include -#include - -static void send_direct(struct bsc_nat *nat, struct msgb *output) -{ - if (osmo_wqueue_enqueue(&nat->mgcp_cfg->gw_fd, output) != 0) { - LOGP(DMGCP, LOGL_ERROR, "Failed to queue MGCP msg.\n"); - msgb_free(output); - } -} - -static void mgcp_queue_for_call_agent(struct bsc_nat *nat, struct msgb *output) -{ - if (nat->mgcp_ipa) - bsc_nat_send_mgcp_to_msc(nat, output); - else - send_direct(nat, output); -} - -int bsc_mgcp_nr_multiplexes(int max_endpoints) -{ - int div = max_endpoints / 32; - - if ((max_endpoints % 32) != 0) - div += 1; - - return div; -} - -static int bsc_init_endps_if_needed(struct bsc_connection *con) -{ - int multiplexes; - - /* we have done that */ - if (con->_endpoint_status) - return 0; - - /* we have no config... */ - if (!con->cfg) - return -1; - - multiplexes = bsc_mgcp_nr_multiplexes(con->cfg->max_endpoints); - con->number_multiplexes = multiplexes; - con->max_endpoints = con->cfg->max_endpoints; - con->_endpoint_status = talloc_zero_array(con, char, 32 * multiplexes + 1); - return con->_endpoint_status == NULL; -} - -static int bsc_assign_endpoint(struct bsc_connection *bsc, struct nat_sccp_connection *con) -{ - int multiplex; - int timeslot; - const int number_endpoints = bsc->max_endpoints; - int i; - - mgcp_endpoint_to_timeslot(bsc->last_endpoint, &multiplex, ×lot); - timeslot += 1; - - for (i = 0; i < number_endpoints; ++i) { - int endpoint; - - /* Wrap around timeslots */ - if (timeslot == 0) - timeslot = 1; - - if (timeslot == 0x1f) { - timeslot = 1; - multiplex += 1; - } - - /* Wrap around the multiplex */ - if (multiplex >= bsc->number_multiplexes) - multiplex = 0; - - endpoint = mgcp_timeslot_to_endpoint(multiplex, timeslot); - - /* Now check if we are allowed to assign this one */ - if (endpoint >= bsc->max_endpoints) { - multiplex = 0; - timeslot = 1; - endpoint = mgcp_timeslot_to_endpoint(multiplex, timeslot); - } - - - if (bsc->_endpoint_status[endpoint] == 0) { - bsc->_endpoint_status[endpoint] = 1; - con->bsc_endp = endpoint; - bsc->last_endpoint = endpoint; - return 0; - } - - timeslot += 1; - } - - return -1; -} - -static uint16_t create_cic(int endpoint) -{ - int timeslot, multiplex; - - mgcp_endpoint_to_timeslot(endpoint, &multiplex, ×lot); - return (multiplex << 5) | (timeslot & 0x1f); -} - -int bsc_mgcp_assign_patch(struct nat_sccp_connection *con, struct msgb *msg) -{ - struct nat_sccp_connection *mcon; - struct tlv_parsed tp; - uint16_t cic; - uint8_t timeslot; - uint8_t multiplex; - unsigned int endp; - - if (!msg->l3h) { - LOGP(DNAT, LOGL_ERROR, "Assignment message should have l3h pointer.\n"); - return -1; - } - - if (msgb_l3len(msg) < 3) { - LOGP(DNAT, LOGL_ERROR, "Assignment message has not enough space for GSM0808.\n"); - return -1; - } - - tlv_parse(&tp, gsm0808_att_tlvdef(), msg->l3h + 3, msgb_l3len(msg) - 3, 0, 0); - if (!TLVP_PRESENT(&tp, GSM0808_IE_CIRCUIT_IDENTITY_CODE)) { - LOGP(DNAT, LOGL_ERROR, "Circuit identity code not found in assignment message.\n"); - return -1; - } - - cic = ntohs(tlvp_val16_unal(&tp, GSM0808_IE_CIRCUIT_IDENTITY_CODE)); - timeslot = cic & 0x1f; - multiplex = (cic & ~0x1f) >> 5; - - - endp = mgcp_timeslot_to_endpoint(multiplex, timeslot); - - if (endp >= con->bsc->nat->mgcp_cfg->trunk.number_endpoints) { - LOGP(DNAT, LOGL_ERROR, - "MSC attempted to assign bad endpoint 0x%x\n", - endp); - return -1; - } - - /* find stale connections using that endpoint */ - llist_for_each_entry(mcon, &con->bsc->nat->sccp_connections, list_entry) { - if (mcon->msc_endp == endp) { - LOGP(DNAT, LOGL_ERROR, - "Endpoint %d was assigned to 0x%x and now 0x%x\n", - endp, - sccp_src_ref_to_int(&mcon->patched_ref), - sccp_src_ref_to_int(&con->patched_ref)); - bsc_mgcp_dlcx(mcon); - } - } - - con->msc_endp = endp; - if (bsc_init_endps_if_needed(con->bsc) != 0) - return -1; - if (bsc_assign_endpoint(con->bsc, con) != 0) - return -1; - - /* - * now patch the message for the new CIC... - * still assumed to be one multiplex only - */ - cic = htons(create_cic(con->bsc_endp)); - memcpy((uint8_t *) TLVP_VAL(&tp, GSM0808_IE_CIRCUIT_IDENTITY_CODE), - &cic, sizeof(cic)); - - return 0; -} - -static void bsc_mgcp_free_endpoint(struct bsc_nat *nat, int i) -{ - if (nat->bsc_endpoints[i].transaction_id) { - talloc_free(nat->bsc_endpoints[i].transaction_id); - nat->bsc_endpoints[i].transaction_id = NULL; - } - - nat->bsc_endpoints[i].transaction_state = 0; - nat->bsc_endpoints[i].bsc = NULL; -} - -void bsc_mgcp_free_endpoints(struct bsc_nat *nat) -{ - int i; - - for (i = 1; i < nat->mgcp_cfg->trunk.number_endpoints; ++i){ - bsc_mgcp_free_endpoint(nat, i); - mgcp_release_endp(&nat->mgcp_cfg->trunk.endpoints[i]); - } -} - -/* send a MDCX where we do not want a response */ -static void bsc_mgcp_send_mdcx(struct bsc_connection *bsc, int port, struct mgcp_endpoint *endp) -{ - char buf[2096]; - int len; - - len = snprintf(buf, sizeof(buf), - "MDCX 23 %x@mgw MGCP 1.0\r\n" - "Z: noanswer\r\n" - "\r\n" - "c=IN IP4 %s\r\n" - "m=audio %d RTP/AVP 255\r\n", - port, mgcp_bts_src_addr(endp), - endp->bts_end.local_port); - if (len < 0) { - LOGP(DMGCP, LOGL_ERROR, "snprintf for MDCX failed.\n"); - return; - } - - bsc_write_mgcp(bsc, (uint8_t *) buf, len); -} - -static void bsc_mgcp_send_dlcx(struct bsc_connection *bsc, int endpoint, int trans) -{ - char buf[2096]; - int len; - - /* - * The following is a bit of a spec violation. According to the - * MGCP grammar the transaction id is are upto 9 digits but we - * prefix it with an alpha numeric value so we can easily recognize - * it as a response. - */ - len = snprintf(buf, sizeof(buf), - "DLCX nat-%u %x@mgw MGCP 1.0\r\n", - trans, endpoint); - if (len < 0) { - LOGP(DMGCP, LOGL_ERROR, "snprintf for DLCX failed.\n"); - return; - } - - bsc_write_mgcp(bsc, (uint8_t *) buf, len); -} - -void bsc_mgcp_init(struct nat_sccp_connection *con) -{ - con->msc_endp = -1; - con->bsc_endp = -1; -} - -/** - * This code will remember the network side of the audio statistics and - * once the internal DLCX response arrives this can be combined with the - * the BSC side and forwarded as a trap. - */ -static void remember_pending_dlcx(struct nat_sccp_connection *con, uint32_t transaction) -{ - struct bsc_nat_call_stats *stats; - struct bsc_connection *bsc = con->bsc; - struct mgcp_endpoint *endp; - - stats = talloc_zero(bsc, struct bsc_nat_call_stats); - if (!stats) { - LOGP(DNAT, LOGL_NOTICE, - "Failed to allocate statistics for endpoint 0x%x\n", - con->msc_endp); - return; - } - - /* take the endpoint here */ - endp = &bsc->nat->mgcp_cfg->trunk.endpoints[con->msc_endp]; - - stats->remote_ref = con->remote_ref; - stats->src_ref = con->patched_ref; - - stats->ci = endp->ci; - stats->bts_rtp_port = endp->bts_end.rtp_port; - stats->bts_addr = endp->bts_end.addr; - stats->net_rtp_port = endp->net_end.rtp_port; - stats->net_addr = endp->net_end.addr; - - stats->net_ps = endp->net_end.packets; - stats->net_os = endp->net_end.octets; - stats->bts_pr = endp->bts_end.packets; - stats->bts_or = endp->bts_end.octets; - mgcp_state_calc_loss(&endp->bts_state, &endp->bts_end, - &stats->bts_expected, &stats->bts_loss); - stats->bts_jitter = mgcp_state_calc_jitter(&endp->bts_state); - - stats->trans_id = transaction; - stats->msc_endpoint = con->msc_endp; - - /* - * Too many pending requests.. let's remove the first two items. - */ - if (!llist_empty(&bsc->pending_dlcx) && - bsc->pending_dlcx_count >= bsc->cfg->max_endpoints * 3) { - struct bsc_nat_call_stats *tmp; - LOGP(DNAT, LOGL_ERROR, - "Too many(%d) pending DLCX responses on BSC: %d\n", - bsc->pending_dlcx_count, bsc->cfg->nr); - bsc->pending_dlcx_count -= 1; - tmp = (struct bsc_nat_call_stats *) bsc->pending_dlcx.next; - llist_del(&tmp->entry); - talloc_free(tmp); - } - - bsc->pending_dlcx_count += 1; - llist_add_tail(&stats->entry, &bsc->pending_dlcx); -} - -void bsc_mgcp_dlcx(struct nat_sccp_connection *con) -{ - /* send a DLCX down the stream */ - if (con->bsc_endp != -1 && con->bsc->_endpoint_status) { - LOGP(DNAT, LOGL_NOTICE, - "Endpoint 0x%x was allocated for bsc: %d. Freeing it.\n", - con->bsc_endp, con->bsc->cfg->nr); - if (con->bsc->_endpoint_status[con->bsc_endp] != 1) - LOGP(DNAT, LOGL_ERROR, "Endpoint 0x%x was not in use\n", con->bsc_endp); - remember_pending_dlcx(con, con->bsc->next_transaction); - con->bsc->_endpoint_status[con->bsc_endp] = 0; - bsc_mgcp_send_dlcx(con->bsc, con->bsc_endp, con->bsc->next_transaction++); - bsc_mgcp_free_endpoint(con->bsc->nat, con->msc_endp); - } - - bsc_mgcp_init(con); - -} - -/* - * Search for the pending request - */ -static void handle_dlcx_response(struct bsc_connection *bsc, struct msgb *msg, - int code, const char *transaction) -{ - uint32_t trans_id = UINT32_MAX; - uint32_t b_ps, b_os, n_pr, n_or, jitter; - int loss; - struct bsc_nat_call_stats *tmp, *stat = NULL; - struct ctrl_cmd *cmd; - - /* parse the transaction identifier */ - int rc = sscanf(transaction, "nat-%u", &trans_id); - if (rc != 1) { - LOGP(DNAT, LOGL_ERROR, "Can not parse transaction id: '%s'\n", - transaction); - return; - } - - /* find the answer for the request we made */ - llist_for_each_entry(tmp, &bsc->pending_dlcx, entry) { - if (trans_id != tmp->trans_id) - continue; - - stat = tmp; - break; - } - - if (!stat) { - LOGP(DNAT, LOGL_ERROR, - "Can not find transaction for: %u\n", trans_id); - return; - } - - /* attempt to parse the data now */ - rc = mgcp_parse_stats(msg, &b_ps, &b_os, &n_pr, &n_or, &loss, &jitter); - if (rc != 0) - LOGP(DNAT, LOGL_ERROR, - "Can not parse connection statistics: %d\n", rc); - - /* send a trap now */ - cmd = ctrl_cmd_create(bsc, CTRL_TYPE_TRAP); - if (!cmd) { - LOGP(DNAT, LOGL_ERROR, - "Creating a ctrl cmd failed.\n"); - goto free_stat; - } - - cmd->id = "0"; - cmd->variable = talloc_asprintf(cmd, "net.0.bsc.%d.call_stats.v2", - bsc->cfg->nr); - cmd->reply = talloc_asprintf(cmd, - "mg_ip_addr=%s,mg_port=%d,", - inet_ntoa(stat->net_addr), - stat->net_rtp_port); - cmd->reply = talloc_asprintf_append(cmd->reply, - "endpoint_ip_addr=%s,endpoint_port=%d,", - inet_ntoa(stat->bts_addr), - stat->bts_rtp_port); - cmd->reply = talloc_asprintf_append(cmd->reply, - "nat_pkt_in=%u,nat_pkt_out=%u," - "nat_bytes_in=%u,nat_bytes_out=%u," - "nat_jitter=%u,nat_pkt_lost=%d,", - stat->bts_pr, stat->net_ps, - stat->bts_or, stat->net_os, - stat->bts_jitter, stat->bts_loss); - cmd->reply = talloc_asprintf_append(cmd->reply, - "bsc_pkt_in=%u,bsc_pkt_out=%u," - "bsc_bytes_in=%u,bsc_bytes_out=%u," - "bsc_jitter=%u,bsc_pkt_lost=%d,", - n_pr, b_ps, - n_or, b_os, - jitter, loss); - cmd->reply = talloc_asprintf_append(cmd->reply, - "sccp_src_ref=%u,sccp_dst_ref=%u", - sccp_src_ref_to_int(&stat->src_ref), - sccp_src_ref_to_int(&stat->remote_ref)); - - /* send it and be done */ - ctrl_cmd_send_to_all(bsc->nat->ctrl, cmd); - talloc_free(cmd); - -free_stat: - bsc->pending_dlcx_count -= 1; - llist_del(&stat->entry); - talloc_free(stat); -} - - -struct nat_sccp_connection *bsc_mgcp_find_con(struct bsc_nat *nat, int endpoint) -{ - struct nat_sccp_connection *con = NULL; - struct nat_sccp_connection *sccp; - - llist_for_each_entry(sccp, &nat->sccp_connections, list_entry) { - if (sccp->msc_endp == -1) - continue; - if (sccp->msc_endp != endpoint) - continue; - - con = sccp; - } - - if (con) - return con; - - LOGP(DMGCP, LOGL_ERROR, - "Failed to find the connection for endpoint: 0x%x\n", endpoint); - return NULL; -} - -static int nat_osmux_only(struct mgcp_config *mgcp_cfg, struct bsc_config *bsc_cfg) -{ - if (mgcp_cfg->osmux == OSMUX_USAGE_ONLY) - return 1; - if (bsc_cfg->osmux == OSMUX_USAGE_ONLY) - return 1; - return 0; -} - -static int bsc_mgcp_policy_cb(struct mgcp_trunk_config *tcfg, int endpoint, int state, const char *transaction_id) -{ - struct bsc_nat *nat; - struct bsc_endpoint *bsc_endp; - struct nat_sccp_connection *sccp; - struct mgcp_endpoint *mgcp_endp; - struct msgb *bsc_msg; - - nat = tcfg->cfg->data; - bsc_endp = &nat->bsc_endpoints[endpoint]; - mgcp_endp = &nat->mgcp_cfg->trunk.endpoints[endpoint]; - - if (bsc_endp->transaction_id) { - LOGP(DMGCP, LOGL_ERROR, "Endpoint 0x%x had pending transaction: '%s'\n", - endpoint, bsc_endp->transaction_id); - talloc_free(bsc_endp->transaction_id); - bsc_endp->transaction_id = NULL; - bsc_endp->transaction_state = 0; - } - bsc_endp->bsc = NULL; - - sccp = bsc_mgcp_find_con(nat, endpoint); - - if (!sccp) { - LOGP(DMGCP, LOGL_ERROR, "Did not find BSC for change on endpoint: 0x%x state: %d\n", endpoint, state); - - switch (state) { - case MGCP_ENDP_CRCX: - return MGCP_POLICY_REJECT; - break; - case MGCP_ENDP_DLCX: - return MGCP_POLICY_CONT; - break; - case MGCP_ENDP_MDCX: - return MGCP_POLICY_CONT; - break; - default: - LOGP(DMGCP, LOGL_FATAL, "Unhandled state: %d\n", state); - return MGCP_POLICY_CONT; - break; - } - } - - /* Allocate a Osmux circuit ID */ - if (state == MGCP_ENDP_CRCX) { - if (nat->mgcp_cfg->osmux && sccp->bsc->cfg->osmux) { - osmux_allocate_cid(mgcp_endp); - if (mgcp_endp->osmux.allocated_cid < 0 && - nat_osmux_only(nat->mgcp_cfg, sccp->bsc->cfg)) { - LOGP(DMGCP, LOGL_ERROR, - "Rejecting usage of endpoint\n"); - return MGCP_POLICY_REJECT; - } - } - } - - /* we need to generate a new and patched message */ - bsc_msg = bsc_mgcp_rewrite((char *) nat->mgcp_msg, nat->mgcp_length, - sccp->bsc_endp, mgcp_bts_src_addr(mgcp_endp), - mgcp_endp->bts_end.local_port, - mgcp_endp->osmux.allocated_cid, - &mgcp_endp->net_end.codec.payload_type, - nat->sdp_ensure_amr_mode_set); - if (!bsc_msg) { - LOGP(DMGCP, LOGL_ERROR, "Failed to patch the msg.\n"); - return MGCP_POLICY_CONT; - } - - - bsc_endp->transaction_id = talloc_strdup(nat, transaction_id); - bsc_endp->transaction_state = state; - bsc_endp->bsc = sccp->bsc; - - /* we need to update some bits */ - if (state == MGCP_ENDP_CRCX) { - struct sockaddr_in sock; - - /* Annotate the allocated Osmux CID until the bsc confirms that - * it agrees to use Osmux for this voice flow. - */ - if (mgcp_endp->osmux.allocated_cid >= 0 && - mgcp_endp->osmux.state != OSMUX_STATE_ENABLED) { - mgcp_endp->osmux.state = OSMUX_STATE_NEGOTIATING; - mgcp_endp->osmux.cid = mgcp_endp->osmux.allocated_cid; - } - - socklen_t len = sizeof(sock); - if (getpeername(sccp->bsc->write_queue.bfd.fd, (struct sockaddr *) &sock, &len) != 0) { - LOGP(DMGCP, LOGL_ERROR, "Can not get the peername...%d/%s\n", - errno, strerror(errno)); - } else { - mgcp_endp->bts_end.addr = sock.sin_addr; - } - - /* send the message and a fake MDCX to force sending of a dummy packet */ - bsc_write(sccp->bsc, bsc_msg, IPAC_PROTO_MGCP_OLD); - bsc_mgcp_send_mdcx(sccp->bsc, sccp->bsc_endp, mgcp_endp); - return MGCP_POLICY_DEFER; - } else if (state == MGCP_ENDP_DLCX) { - /* we will free the endpoint now and send a DLCX to the BSC */ - msgb_free(bsc_msg); - bsc_mgcp_dlcx(sccp); - - /* libmgcp clears the MGCP endpoint for us */ - if (mgcp_endp->osmux.state == OSMUX_STATE_ENABLED) - osmux_release_cid(mgcp_endp); - - return MGCP_POLICY_CONT; - } else { - bsc_write(sccp->bsc, bsc_msg, IPAC_PROTO_MGCP_OLD); - return MGCP_POLICY_DEFER; - } -} - -/* - * We do have a failure, free data downstream.. - */ -static void free_chan_downstream(struct mgcp_endpoint *endp, struct bsc_endpoint *bsc_endp, - struct bsc_connection *bsc) -{ - LOGP(DMGCP, LOGL_ERROR, "No CI, freeing endpoint 0x%x in state %d\n", - ENDPOINT_NUMBER(endp), bsc_endp->transaction_state); - - /* if a CRCX failed... send a DLCX down the stream */ - if (bsc_endp->transaction_state == MGCP_ENDP_CRCX) { - struct nat_sccp_connection *con; - con = bsc_mgcp_find_con(bsc->nat, ENDPOINT_NUMBER(endp)); - if (!con) { - LOGP(DMGCP, LOGL_ERROR, - "No SCCP connection for endp 0x%x\n", - ENDPOINT_NUMBER(endp)); - } else { - if (con->bsc == bsc) { - bsc_mgcp_send_dlcx(bsc, con->bsc_endp, con->bsc->next_transaction++); - } else { - LOGP(DMGCP, LOGL_ERROR, - "Endpoint belongs to a different BSC\n"); - } - } - } - - bsc_mgcp_free_endpoint(bsc->nat, ENDPOINT_NUMBER(endp)); - mgcp_release_endp(endp); -} - -static void bsc_mgcp_osmux_confirm(struct mgcp_endpoint *endp, const char *str) -{ - unsigned int osmux_cid; - char *res; - - res = strstr(str, "X-Osmux: "); - if (!res) { - LOGP(DMGCP, LOGL_INFO, - "BSC doesn't want to use Osmux, failing back to RTP\n"); - goto err; - } - - if (sscanf(res, "X-Osmux: %u", &osmux_cid) != 1) { - LOGP(DMGCP, LOGL_ERROR, "Failed to parse Osmux CID '%s'\n", - str); - goto err; - } - - if (endp->osmux.cid != osmux_cid) { - LOGP(DMGCP, LOGL_ERROR, - "BSC sent us wrong CID %u, we expected %u", - osmux_cid, endp->osmux.cid); - goto err; - } - - LOGP(DMGCP, LOGL_NOTICE, "bsc accepted to use Osmux (cid=%u)\n", - osmux_cid); - endp->osmux.state = OSMUX_STATE_ACTIVATING; - return; -err: - osmux_release_cid(endp); - endp->osmux.state = OSMUX_STATE_DISABLED; -} - -/* - * We have received a msg from the BSC. We will see if we know - * this transaction and if it belongs to the BSC. Then we will - * need to patch the content to point to the local network and we - * need to update the I: that was assigned by the BSS. - * - * Only responses to CRCX and DLCX should arrive here. The DLCX - * needs to be handled specially to combine the two statistics. - */ -void bsc_mgcp_forward(struct bsc_connection *bsc, struct msgb *msg) -{ - struct msgb *output; - struct bsc_endpoint *bsc_endp = NULL; - struct mgcp_endpoint *endp = NULL; - int i, code; - char transaction_id[60]; - - /* Some assumption that our buffer is big enough.. and null terminate */ - if (msgb_l2len(msg) > 2000) { - LOGP(DMGCP, LOGL_ERROR, "MGCP message too long.\n"); - return; - } - - msg->l2h[msgb_l2len(msg)] = '\0'; - - if (bsc_mgcp_parse_response((const char *) msg->l2h, &code, transaction_id) != 0) { - LOGP(DMGCP, LOGL_ERROR, "Failed to parse response code.\n"); - return; - } - - for (i = 1; i < bsc->nat->mgcp_cfg->trunk.number_endpoints; ++i) { - if (bsc->nat->bsc_endpoints[i].bsc != bsc) - continue; - /* no one listening? a bug? */ - if (!bsc->nat->bsc_endpoints[i].transaction_id) - continue; - if (strcmp(transaction_id, bsc->nat->bsc_endpoints[i].transaction_id) != 0) - continue; - - endp = &bsc->nat->mgcp_cfg->trunk.endpoints[i]; - bsc_endp = &bsc->nat->bsc_endpoints[i]; - break; - } - - if (!bsc_endp && strncmp("nat-", transaction_id, 4) == 0) { - handle_dlcx_response(bsc, msg, code, transaction_id); - return; - } - - if (!bsc_endp) { - LOGP(DMGCP, LOGL_ERROR, "Could not find active endpoint: %s for msg: '%s'\n", - transaction_id, (const char *) msg->l2h); - return; - } - - endp->ci = bsc_mgcp_extract_ci((const char *) msg->l2h); - if (endp->ci == CI_UNUSED) { - free_chan_downstream(endp, bsc_endp, bsc); - return; - } - - if (endp->osmux.state == OSMUX_STATE_NEGOTIATING) - bsc_mgcp_osmux_confirm(endp, (const char *) msg->l2h); - - /* If we require osmux and it is disabled.. fail */ - if (nat_osmux_only(bsc->nat->mgcp_cfg, bsc->cfg) && - endp->osmux.state == OSMUX_STATE_DISABLED) { - LOGP(DMGCP, LOGL_ERROR, - "Failed to activate osmux endpoint 0x%x\n", - ENDPOINT_NUMBER(endp)); - free_chan_downstream(endp, bsc_endp, bsc); - return; - } - - /* free some stuff */ - talloc_free(bsc_endp->transaction_id); - bsc_endp->transaction_id = NULL; - bsc_endp->transaction_state = 0; - - /* - * rewrite the information. In case the endpoint was deleted - * there should be nothing for us to rewrite so putting endp->rtp_port - * with the value of 0 should be no problem. - */ - output = bsc_mgcp_rewrite((char * ) msg->l2h, msgb_l2len(msg), -1, - mgcp_net_src_addr(endp), - endp->net_end.local_port, -1, - &endp->bts_end.codec.payload_type, - bsc->nat->sdp_ensure_amr_mode_set); - if (!output) { - LOGP(DMGCP, LOGL_ERROR, "Failed to rewrite MGCP msg.\n"); - return; - } - - mgcp_queue_for_call_agent(bsc->nat, output); -} - -int bsc_mgcp_parse_response(const char *str, int *code, char transaction[60]) -{ - int rc; - /* we want to parse two strings */ - rc = sscanf(str, "%3d %59s\n", code, transaction) != 2; - transaction[59] = '\0'; - return rc; -} - -uint32_t bsc_mgcp_extract_ci(const char *str) -{ - unsigned int ci; - char *res = strstr(str, "I: "); - if (!res) { - LOGP(DMGCP, LOGL_ERROR, "No CI in msg '%s'\n", str); - return CI_UNUSED; - } - - if (sscanf(res, "I: %u", &ci) != 1) { - LOGP(DMGCP, LOGL_ERROR, "Failed to parse CI in msg '%s'\n", str); - return CI_UNUSED; - } - - return ci; -} - -/** - * Create a new MGCPCommand based on the input and endpoint from a message - */ -static void patch_mgcp(struct msgb *output, const char *op, const char *tok, - int endp, int len, int cr, int osmux_cid) -{ - int slen; - int ret; - char buf[40]; - char osmux_extension[strlen("\nX-Osmux: 255") + 1]; - - buf[0] = buf[39] = '\0'; - ret = sscanf(tok, "%*s %s", buf); - if (ret != 1) { - LOGP(DMGCP, LOGL_ERROR, - "Failed to find Endpoint in: %s\n", tok); - return; - } - - if (osmux_cid >= 0) - sprintf(osmux_extension, "\nX-Osmux: %u", osmux_cid & 0xff); - else - osmux_extension[0] = '\0'; - - slen = sprintf((char *) output->l3h, "%s %s %x@mgw MGCP 1.0%s%s", - op, buf, endp, osmux_extension, cr ? "\r\n" : "\n"); - output->l3h = msgb_put(output, slen); -} - -/* we need to replace some strings... */ -struct msgb *bsc_mgcp_rewrite(char *input, int length, int endpoint, - const char *ip, int port, int osmux_cid, - int *first_payload_type, int ensure_mode_set) -{ - static const char crcx_str[] = "CRCX "; - static const char dlcx_str[] = "DLCX "; - static const char mdcx_str[] = "MDCX "; - - static const char ip_str[] = "c=IN IP4 "; - static const char aud_str[] = "m=audio "; - static const char fmt_str[] = "a=fmtp:"; - - char buf[128]; - char *running, *token; - struct msgb *output; - - /* keep state to add the a=fmtp line */ - int found_fmtp = 0; - int payload = -1; - int cr = 1; - - if (length > 4096 - 256) { - LOGP(DMGCP, LOGL_ERROR, "Input is too long.\n"); - return NULL; - } - - output = msgb_alloc_headroom(4096, 128, "MGCP rewritten"); - if (!output) { - LOGP(DMGCP, LOGL_ERROR, "Failed to allocate new MGCP msg.\n"); - return NULL; - } - - running = input; - output->l2h = output->data; - output->l3h = output->l2h; - for (token = strsep(&running, "\n"); running; token = strsep(&running, "\n")) { - int len = strlen(token); - cr = len > 0 && token[len - 1] == '\r'; - - if (strncmp(crcx_str, token, (sizeof crcx_str) - 1) == 0) { - patch_mgcp(output, "CRCX", token, endpoint, len, cr, osmux_cid); - } else if (strncmp(dlcx_str, token, (sizeof dlcx_str) - 1) == 0) { - patch_mgcp(output, "DLCX", token, endpoint, len, cr, -1); - } else if (strncmp(mdcx_str, token, (sizeof mdcx_str) - 1) == 0) { - patch_mgcp(output, "MDCX", token, endpoint, len, cr, -1); - } else if (strncmp(ip_str, token, (sizeof ip_str) - 1) == 0) { - output->l3h = msgb_put(output, strlen(ip_str)); - memcpy(output->l3h, ip_str, strlen(ip_str)); - output->l3h = msgb_put(output, strlen(ip)); - memcpy(output->l3h, ip, strlen(ip)); - - if (cr) { - output->l3h = msgb_put(output, 2); - output->l3h[0] = '\r'; - output->l3h[1] = '\n'; - } else { - output->l3h = msgb_put(output, 1); - output->l3h[0] = '\n'; - } - } else if (strncmp(aud_str, token, (sizeof aud_str) - 1) == 0) { - int offset; - if (sscanf(token, "m=audio %*d RTP/AVP %n%d", &offset, &payload) != 1) { - LOGP(DMGCP, LOGL_ERROR, "Could not parsed audio line.\n"); - msgb_free(output); - return NULL; - } - - snprintf(buf, sizeof(buf)-1, "m=audio %d RTP/AVP %s\n", - port, &token[offset]); - buf[sizeof(buf)-1] = '\0'; - - output->l3h = msgb_put(output, strlen(buf)); - memcpy(output->l3h, buf, strlen(buf)); - } else if (strncmp(fmt_str, token, (sizeof fmt_str) - 1) == 0) { - found_fmtp = 1; - goto copy; - } else { -copy: - output->l3h = msgb_put(output, len + 1); - memcpy(output->l3h, token, len); - output->l3h[len] = '\n'; - } - } - - /* - * the above code made sure that we have 128 bytes lefts. So we can - * safely append another line. - */ - if (ensure_mode_set && !found_fmtp && payload != -1) { - snprintf(buf, sizeof(buf) - 1, "a=fmtp:%d mode-set=2%s", - payload, cr ? "\r\n" : "\n"); - buf[sizeof(buf) - 1] = '\0'; - output->l3h = msgb_put(output, strlen(buf)); - memcpy(output->l3h, buf, strlen(buf)); - } - - if (payload != -1 && first_payload_type) - *first_payload_type = payload; - - return output; -} - -/* - * This comes from the MSC and we will now parse it. The caller needs - * to free the msgb. - */ -void bsc_nat_handle_mgcp(struct bsc_nat *nat, struct msgb *msg) -{ - struct msgb *resp; - - if (!nat->mgcp_ipa) { - LOGP(DMGCP, LOGL_ERROR, "MGCP message not allowed on IPA.\n"); - return; - } - - if (msgb_l2len(msg) > sizeof(nat->mgcp_msg) - 1) { - LOGP(DMGCP, LOGL_ERROR, "MGCP msg too big for handling.\n"); - return; - } - - memcpy(nat->mgcp_msg, msg->l2h, msgb_l2len(msg)); - nat->mgcp_length = msgb_l2len(msg); - nat->mgcp_msg[nat->mgcp_length] = '\0'; - - /* now handle the message */ - resp = mgcp_handle_message(nat->mgcp_cfg, msg); - - /* we do have a direct answer... e.g. AUEP */ - if (resp) - mgcp_queue_for_call_agent(nat, resp); - - return; -} - -static int mgcp_do_read(struct osmo_fd *fd) -{ - struct bsc_nat *nat; - struct msgb *msg, *resp; - int rc; - - nat = fd->data; - - rc = read(fd->fd, nat->mgcp_msg, sizeof(nat->mgcp_msg) - 1); - if (rc <= 0) { - LOGP(DMGCP, LOGL_ERROR, "Failed to read errno: %d\n", errno); - return -1; - } - - nat->mgcp_msg[rc] = '\0'; - nat->mgcp_length = rc; - - msg = msgb_alloc(sizeof(nat->mgcp_msg), "MGCP GW Read"); - if (!msg) { - LOGP(DMGCP, LOGL_ERROR, "Failed to create buffer.\n"); - return -1; - } - - msg->l2h = msgb_put(msg, rc); - memcpy(msg->l2h, nat->mgcp_msg, msgb_l2len(msg)); - resp = mgcp_handle_message(nat->mgcp_cfg, msg); - msgb_free(msg); - - /* we do have a direct answer... e.g. AUEP */ - if (resp) - mgcp_queue_for_call_agent(nat, resp); - - return 0; -} - -static int mgcp_do_write(struct osmo_fd *bfd, struct msgb *msg) -{ - int rc; - - rc = write(bfd->fd, msg->data, msg->len); - - if (rc != msg->len) { - LOGP(DMGCP, LOGL_ERROR, "Failed to write msg to MGCP CallAgent.\n"); - return -1; - } - - return rc; -} - -static int init_mgcp_socket(struct bsc_nat *nat, struct mgcp_config *cfg) -{ - struct sockaddr_in addr; - int on; - - cfg->gw_fd.bfd.fd = socket(AF_INET, SOCK_DGRAM, 0); - if (cfg->gw_fd.bfd.fd < 0) { - LOGP(DMGCP, LOGL_ERROR, "Failed to create MGCP socket. errno: %d\n", errno); - return -1; - } - - on = 1; - setsockopt(cfg->gw_fd.bfd.fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)); - - memset(&addr, 0, sizeof(addr)); - addr.sin_family = AF_INET; - addr.sin_port = htons(cfg->source_port); - inet_aton(cfg->source_addr, &addr.sin_addr); - - if (bind(cfg->gw_fd.bfd.fd, (struct sockaddr *) &addr, sizeof(addr)) < 0) { - LOGP(DMGCP, LOGL_ERROR, "Failed to bind on %s:%d errno: %d\n", - cfg->source_addr, cfg->source_port, errno); - close(cfg->gw_fd.bfd.fd); - cfg->gw_fd.bfd.fd = -1; - return -1; - } - - addr.sin_port = htons(2727); - inet_aton(cfg->call_agent_addr, &addr.sin_addr); - if (connect(cfg->gw_fd.bfd.fd, (struct sockaddr *) &addr, sizeof(addr)) < 0) { - LOGP(DMGCP, LOGL_ERROR, "Failed to connect to: '%s'. errno: %d\n", - cfg->call_agent_addr, errno); - close(cfg->gw_fd.bfd.fd); - cfg->gw_fd.bfd.fd = -1; - return -1; - } - - osmo_wqueue_init(&cfg->gw_fd, 10); - cfg->gw_fd.bfd.when = BSC_FD_READ; - cfg->gw_fd.bfd.data = nat; - cfg->gw_fd.read_cb = mgcp_do_read; - cfg->gw_fd.write_cb = mgcp_do_write; - - if (osmo_fd_register(&cfg->gw_fd.bfd) != 0) { - LOGP(DMGCP, LOGL_ERROR, "Failed to register MGCP fd.\n"); - close(cfg->gw_fd.bfd.fd); - cfg->gw_fd.bfd.fd = -1; - return -1; - } - - return 0; -} - -int bsc_mgcp_nat_init(struct bsc_nat *nat) -{ - struct mgcp_config *cfg = nat->mgcp_cfg; - - if (!cfg->call_agent_addr) { - LOGP(DMGCP, LOGL_ERROR, "The BSC nat requires the call agent ip to be set.\n"); - return -1; - } - - if (cfg->bts_ip) { - LOGP(DMGCP, LOGL_ERROR, "Do not set the BTS ip for the nat.\n"); - return -1; - } - - /* initialize the MGCP socket */ - if (!nat->mgcp_ipa) { - int rc = init_mgcp_socket(nat, cfg); - if (rc != 0) - return rc; - } - - - /* some more MGCP config handling */ - cfg->data = nat; - cfg->policy_cb = bsc_mgcp_policy_cb; - cfg->trunk.force_realloc = 1; - - if (cfg->bts_ip) - talloc_free(cfg->bts_ip); - cfg->bts_ip = ""; - - nat->bsc_endpoints = talloc_zero_array(nat, - struct bsc_endpoint, - cfg->trunk.number_endpoints + 1); - if (!nat->bsc_endpoints) { - LOGP(DMGCP, LOGL_ERROR, "Failed to allocate nat endpoints\n"); - close(cfg->gw_fd.bfd.fd); - cfg->gw_fd.bfd.fd = -1; - return -1; - } - - if (mgcp_reset_transcoder(cfg) < 0) { - LOGP(DMGCP, LOGL_ERROR, "Failed to send packet to the transcoder.\n"); - talloc_free(nat->bsc_endpoints); - nat->bsc_endpoints = NULL; - close(cfg->gw_fd.bfd.fd); - cfg->gw_fd.bfd.fd = -1; - return -1; - } - - return 0; -} - -void bsc_mgcp_clear_endpoints_for(struct bsc_connection *bsc) -{ - struct rate_ctr *ctr = NULL; - int i; - - if (bsc->cfg) - ctr = &bsc->cfg->stats.ctrg->ctr[BCFG_CTR_DROPPED_CALLS]; - - for (i = 1; i < bsc->nat->mgcp_cfg->trunk.number_endpoints; ++i) { - struct bsc_endpoint *bsc_endp = &bsc->nat->bsc_endpoints[i]; - - if (bsc_endp->bsc != bsc) - continue; - - if (ctr) - rate_ctr_inc(ctr); - - bsc_mgcp_free_endpoint(bsc->nat, i); - mgcp_release_endp(&bsc->nat->mgcp_cfg->trunk.endpoints[i]); - } -} diff --git a/openbsc/src/osmo-bsc_nat/bsc_nat.c b/openbsc/src/osmo-bsc_nat/bsc_nat.c deleted file mode 100644 index daa066d0..00000000 --- a/openbsc/src/osmo-bsc_nat/bsc_nat.c +++ /dev/null @@ -1,1736 +0,0 @@ -/* BSC Multiplexer/NAT */ - -/* - * (C) 2010-2013 by Holger Hans Peter Freyther - * (C) 2010-2013 by On-Waves - * (C) 2009 by Harald Welte - * All Rights Reserved - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation; either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - * - */ -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include - -#define _GNU_SOURCE -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include - -#include - -#include -#include -#include - -#include -#include -#include - -#include -#include -#include -#include -#include - -#include - -#include - -#include - -#include "../../bscconfig.h" - -#define SCCP_CLOSE_TIME 20 -#define SCCP_CLOSE_TIME_TIMEOUT 19 - -static const char *config_file = "bsc-nat.cfg"; -static struct in_addr local_addr; -static struct osmo_fd bsc_listen; -static const char *msc_ip = NULL; -static struct osmo_timer_list sccp_close; -static int daemonize = 0; - -const char *openbsc_copyright = - "Copyright (C) 2010 Holger Hans Peter Freyther and On-Waves\r\n" - "License AGPLv3+: GNU AGPL version 3 or later \r\n" - "This is free software: you are free to change and redistribute it.\r\n" - "There is NO WARRANTY, to the extent permitted by law.\r\n"; - -static struct bsc_nat *nat; -static void bsc_send_data(struct bsc_connection *bsc, const uint8_t *data, unsigned int length, int); -static void msc_send_reset(struct bsc_msc_connection *con); -static void bsc_stat_reject(int filter, struct bsc_connection *bsc, int normal); - -struct bsc_config *bsc_config_num(struct bsc_nat *nat, int num) -{ - struct bsc_config *conf; - - llist_for_each_entry(conf, &nat->bsc_configs, entry) - if (conf->nr == num) - return conf; - - return NULL; -} - -static void queue_for_msc(struct bsc_msc_connection *con, struct msgb *msg) -{ - if (!con) { - LOGP(DLINP, LOGL_ERROR, "No MSC Connection assigned. Check your code.\n"); - msgb_free(msg); - return; - } - - - if (osmo_wqueue_enqueue(&con->write_queue, msg) != 0) { - LOGP(DLINP, LOGL_ERROR, "Failed to enqueue the write.\n"); - msgb_free(msg); - } -} - -static void send_reset_ack(struct bsc_connection *bsc) -{ - static const uint8_t gsm_reset_ack[] = { - 0x09, 0x00, 0x03, 0x07, 0x0b, 0x04, 0x43, 0x01, - 0x00, 0xfe, 0x04, 0x43, 0x5c, 0x00, 0xfe, 0x03, - 0x00, 0x01, 0x31, - }; - - bsc_send_data(bsc, gsm_reset_ack, sizeof(gsm_reset_ack), IPAC_PROTO_SCCP); -} - -static void send_ping(struct bsc_connection *bsc) -{ - static const uint8_t id_ping[] = { - IPAC_MSGT_PING, - }; - - bsc_send_data(bsc, id_ping, sizeof(id_ping), IPAC_PROTO_IPACCESS); -} - -static void send_pong(struct bsc_connection *bsc) -{ - static const uint8_t id_pong[] = { - IPAC_MSGT_PONG, - }; - - bsc_send_data(bsc, id_pong, sizeof(id_pong), IPAC_PROTO_IPACCESS); -} - -static void bsc_pong_timeout(void *_bsc) -{ - struct bsc_connection *bsc = _bsc; - - LOGP(DNAT, LOGL_ERROR, "BSC Nr: %d PONG timeout.\n", bsc->cfg->nr); - bsc_close_connection(bsc); -} - -static void bsc_ping_timeout(void *_bsc) -{ - struct bsc_connection *bsc = _bsc; - - if (bsc->nat->ping_timeout < 0) - return; - - send_ping(bsc); - - /* send another ping in 20 seconds */ - osmo_timer_schedule(&bsc->ping_timeout, bsc->nat->ping_timeout, 0); - - /* also start a pong timer */ - osmo_timer_schedule(&bsc->pong_timeout, bsc->nat->pong_timeout, 0); -} - -static void start_ping_pong(struct bsc_connection *bsc) -{ - osmo_timer_setup(&bsc->pong_timeout, bsc_pong_timeout, bsc); - osmo_timer_setup(&bsc->ping_timeout, bsc_ping_timeout, bsc); - - bsc_ping_timeout(bsc); -} - -static void send_id_ack(struct bsc_connection *bsc) -{ - static const uint8_t id_ack[] = { - IPAC_MSGT_ID_ACK - }; - - bsc_send_data(bsc, id_ack, sizeof(id_ack), IPAC_PROTO_IPACCESS); -} - -static void send_id_req(struct bsc_nat *nat, struct bsc_connection *bsc) -{ - static const uint8_t s_id_req[] = { - IPAC_MSGT_ID_GET, - 0x01, IPAC_IDTAG_UNIT, - 0x01, IPAC_IDTAG_MACADDR, - 0x01, IPAC_IDTAG_LOCATION1, - 0x01, IPAC_IDTAG_LOCATION2, - 0x01, IPAC_IDTAG_EQUIPVERS, - 0x01, IPAC_IDTAG_SWVERSION, - 0x01, IPAC_IDTAG_UNITNAME, - 0x01, IPAC_IDTAG_SERNR, - }; - - uint8_t *mrand; - uint8_t id_req[sizeof(s_id_req) + (2+16)]; - uint8_t *buf = &id_req[sizeof(s_id_req)]; - - /* copy the static data */ - memcpy(id_req, s_id_req, sizeof(s_id_req)); - - /* put the RAND with length, tag, value */ - buf = v_put(buf, 0x11); - buf = v_put(buf, 0x23); - mrand = bsc->last_rand; - - if (RAND_bytes(mrand, 16) != 1) - goto failed_random; - - memcpy(buf, mrand, 16); - buf += 16; - - bsc_send_data(bsc, id_req, sizeof(id_req), IPAC_PROTO_IPACCESS); - return; - -failed_random: - /* the timeout will trigger and close this connection */ - LOGP(DNAT, LOGL_ERROR, "Failed to read from urandom.\n"); - return; -} - -static struct msgb *nat_create_rlsd(struct nat_sccp_connection *conn) -{ - struct sccp_connection_released *rel; - struct msgb *msg; - - msg = msgb_alloc_headroom(4096, 128, "rlsd"); - if (!msg) { - LOGP(DNAT, LOGL_ERROR, "Failed to allocate released.\n"); - return NULL; - } - - msg->l2h = msgb_put(msg, sizeof(*rel)); - rel = (struct sccp_connection_released *) msg->l2h; - rel->type = SCCP_MSG_TYPE_RLSD; - rel->release_cause = SCCP_RELEASE_CAUSE_SCCP_FAILURE; - rel->destination_local_reference = conn->remote_ref; - rel->source_local_reference = conn->patched_ref; - - return msg; -} - -static void nat_send_rlsd_ussd(struct bsc_nat *nat, struct nat_sccp_connection *conn) -{ - struct msgb *msg; - - if (!nat->ussd_con) - return; - - msg = nat_create_rlsd(conn); - if (!msg) - return; - - bsc_do_write(&nat->ussd_con->queue, msg, IPAC_PROTO_SCCP); -} - -static void nat_send_rlsd_msc(struct nat_sccp_connection *conn) -{ - struct msgb *msg; - - msg = nat_create_rlsd(conn); - if (!msg) - return; - - ipa_prepend_header(msg, IPAC_PROTO_SCCP); - queue_for_msc(conn->msc_con, msg); -} - -static void nat_send_rlsd_bsc(struct nat_sccp_connection *conn) -{ - struct msgb *msg; - struct sccp_connection_released *rel; - - msg = msgb_alloc_headroom(4096, 128, "rlsd"); - if (!msg) { - LOGP(DNAT, LOGL_ERROR, "Failed to allocate clear command.\n"); - return; - } - - msg->l2h = msgb_put(msg, sizeof(*rel)); - rel = (struct sccp_connection_released *) msg->l2h; - rel->type = SCCP_MSG_TYPE_RLSD; - rel->release_cause = SCCP_RELEASE_CAUSE_SCCP_FAILURE; - rel->destination_local_reference = conn->real_ref; - rel->source_local_reference = conn->remote_ref; - - bsc_write(conn->bsc, msg, IPAC_PROTO_SCCP); -} - -static struct msgb *nat_creat_clrc(struct nat_sccp_connection *conn, uint8_t cause) -{ - struct msgb *msg; - struct msgb *sccp; - - msg = gsm0808_create_clear_command(cause); - if (!msg) { - LOGP(DNAT, LOGL_ERROR, "Failed to allocate clear command.\n"); - return NULL; - } - - sccp = sccp_create_dt1(&conn->real_ref, msg->data, msg->len); - if (!sccp) { - LOGP(DNAT, LOGL_ERROR, "Failed to allocate SCCP msg.\n"); - msgb_free(msg); - return NULL; - } - - msgb_free(msg); - return sccp; -} - -static int nat_send_clrc_bsc(struct nat_sccp_connection *conn) -{ - struct msgb *sccp; - - sccp = nat_creat_clrc(conn, 0x20); - if (!sccp) - return -1; - return bsc_write(conn->bsc, sccp, IPAC_PROTO_SCCP); -} - -static void nat_send_rlc(struct bsc_msc_connection *msc_con, - struct sccp_source_reference *src, - struct sccp_source_reference *dst) -{ - struct sccp_connection_release_complete *rlc; - struct msgb *msg; - - msg = msgb_alloc_headroom(4096, 128, "rlc"); - if (!msg) { - LOGP(DNAT, LOGL_ERROR, "Failed to sccp rlc.\n"); - return; - } - - msg->l2h = msgb_put(msg, sizeof(*rlc)); - rlc = (struct sccp_connection_release_complete *) msg->l2h; - rlc->type = SCCP_MSG_TYPE_RLC; - rlc->destination_local_reference = *dst; - rlc->source_local_reference = *src; - - ipa_prepend_header(msg, IPAC_PROTO_SCCP); - - queue_for_msc(msc_con, msg); -} - -static void send_mgcp_reset(struct bsc_connection *bsc) -{ - static const uint8_t mgcp_reset[] = { - "RSIP 1 13@mgw MGCP 1.0\r\n" - }; - - bsc_write_mgcp(bsc, mgcp_reset, sizeof mgcp_reset - 1); -} - -void bsc_nat_send_mgcp_to_msc(struct bsc_nat *nat, struct msgb *msg) -{ - ipa_prepend_header(msg, IPAC_PROTO_MGCP_OLD); - queue_for_msc(nat->msc_con, msg); -} - -/* - * Below is the handling of messages coming - * from the MSC and need to be forwarded to - * a real BSC. - */ -static void initialize_msc_if_needed(struct bsc_msc_connection *msc_con) -{ - if (msc_con->first_contact) - return; - - msc_con->first_contact = 1; - msc_send_reset(msc_con); -} - -static void send_id_get_response(struct bsc_msc_connection *msc_con) -{ - struct msgb *msg = bsc_msc_id_get_resp(0, nat->token, NULL, 0); - if (!msg) - return; - - ipa_prepend_header(msg, IPAC_PROTO_IPACCESS); - queue_for_msc(msc_con, msg); -} - -/* - * Currently we are lacking refcounting so we need to copy each message. - */ -static void bsc_send_data(struct bsc_connection *bsc, const uint8_t *data, unsigned int length, int proto) -{ - struct msgb *msg; - - if (length > 4096 - 128) { - LOGP(DLINP, LOGL_ERROR, "Can not send message of that size.\n"); - return; - } - - msg = msgb_alloc_headroom(4096, 128, "to-bsc"); - if (!msg) { - LOGP(DLINP, LOGL_ERROR, "Failed to allocate memory for BSC msg.\n"); - return; - } - - msg->l2h = msgb_put(msg, length); - memcpy(msg->data, data, length); - - bsc_write(bsc, msg, proto); -} - -/* - * Update the release statistics - */ -static void bsc_stat_reject(int filter, struct bsc_connection *bsc, int normal) -{ - if (!bsc->cfg) { - LOGP(DNAT, LOGL_ERROR, "BSC is not authenticated."); - return; - } - - if (filter >= 0) { - LOGP(DNAT, LOGL_ERROR, "Connection was not rejected"); - return; - } - - if (filter == -1) - rate_ctr_inc(&bsc->cfg->stats.ctrg->ctr[BCFG_CTR_ILL_PACKET]); - else if (normal) - rate_ctr_inc(&bsc->cfg->stats.ctrg->ctr[BCFG_CTR_REJECTED_MSG]); - else - rate_ctr_inc(&bsc->cfg->stats.ctrg->ctr[BCFG_CTR_REJECTED_CR]); -} - -/* - * Release an established connection. We will have to release it to the BSC - * and to the network and we do it the following way. - * 1.) Give up on the MSC side - * 1.1) Send a RLSD message, it is a bit non standard but should work, we - * ignore the RLC... we might complain about it. Other options would - * be to send a Release Request, handle the Release Complete.. - * 1.2) Mark the data structure to be con_local and wait for 2nd - * - * 2.) Give up on the BSC side - * 2.1) Depending on the con type reject the service, or just close it - */ -static void bsc_send_con_release(struct bsc_connection *bsc, - struct nat_sccp_connection *con, - struct bsc_filter_reject_cause *cause) -{ - struct msgb *rlsd; - /* 1. release the network */ - rlsd = sccp_create_rlsd(&con->patched_ref, &con->remote_ref, - SCCP_RELEASE_CAUSE_END_USER_ORIGINATED); - if (!rlsd) - LOGP(DNAT, LOGL_ERROR, "Failed to create RLSD message.\n"); - else { - ipa_prepend_header(rlsd, IPAC_PROTO_SCCP); - queue_for_msc(con->msc_con, rlsd); - } - con->con_local = NAT_CON_END_LOCAL; - con->msc_con = NULL; - - /* 2. release the BSC side */ - if (con->filter_state.con_type == FLT_CON_TYPE_LU) { - struct msgb *payload, *udt; - payload = gsm48_create_loc_upd_rej(cause->lu_reject_cause); - - if (payload) { - gsm0808_prepend_dtap_header(payload, 0); - udt = sccp_create_dt1(&con->real_ref, payload->data, payload->len); - if (udt) - bsc_write(bsc, udt, IPAC_PROTO_SCCP); - else - LOGP(DNAT, LOGL_ERROR, "Failed to create DT1\n"); - - msgb_free(payload); - } else { - LOGP(DNAT, LOGL_ERROR, "Failed to allocate LU Reject.\n"); - } - } - - nat_send_clrc_bsc(con); - - rlsd = sccp_create_rlsd(&con->remote_ref, &con->real_ref, - SCCP_RELEASE_CAUSE_END_USER_ORIGINATED); - if (!rlsd) { - LOGP(DNAT, LOGL_ERROR, "Failed to allocate RLSD for the BSC.\n"); - sccp_connection_destroy(con); - return; - } - - con->filter_state.con_type = FLT_CON_TYPE_LOCAL_REJECT; - bsc_write(bsc, rlsd, IPAC_PROTO_SCCP); -} - -static void bsc_send_con_refuse(struct bsc_connection *bsc, - struct bsc_nat_parsed *parsed, int con_type, - struct bsc_filter_reject_cause *cause) -{ - struct msgb *payload; - struct msgb *refuse; - - if (con_type == FLT_CON_TYPE_LU) - payload = gsm48_create_loc_upd_rej(cause->lu_reject_cause); - else if (con_type == FLT_CON_TYPE_CM_SERV_REQ || con_type == FLT_CON_TYPE_SSA) - payload = gsm48_create_mm_serv_rej(cause->cm_reject_cause); - else { - LOGP(DNAT, LOGL_ERROR, "Unknown connection type: %d\n", con_type); - payload = NULL; - } - - /* - * Some BSCs do not handle the payload inside a SCCP CREF msg - * so we will need to: - * 1.) Allocate a local connection and mark it as local.. - * 2.) queue data for downstream.. and the RLC should delete everything - */ - if (payload) { - struct msgb *cc, *udt, *clear, *rlsd; - struct nat_sccp_connection *con; - con = create_sccp_src_ref(bsc, parsed); - if (!con) - goto send_refuse; - - /* declare it local and assign a unique remote_ref */ - con->filter_state.con_type = FLT_CON_TYPE_LOCAL_REJECT; - con->con_local = NAT_CON_END_LOCAL; - con->has_remote_ref = 1; - con->remote_ref = con->patched_ref; - - /* 1. create a confirmation */ - cc = sccp_create_cc(&con->remote_ref, &con->real_ref); - if (!cc) - goto send_refuse; - - /* 2. create the DT1 */ - gsm0808_prepend_dtap_header(payload, 0); - udt = sccp_create_dt1(&con->real_ref, payload->data, payload->len); - if (!udt) { - msgb_free(cc); - goto send_refuse; - } - - /* 3. send a Clear Command */ - clear = nat_creat_clrc(con, 0x20); - if (!clear) { - msgb_free(cc); - msgb_free(udt); - goto send_refuse; - } - - /* 4. send a RLSD */ - rlsd = sccp_create_rlsd(&con->remote_ref, &con->real_ref, - SCCP_RELEASE_CAUSE_END_USER_ORIGINATED); - if (!rlsd) { - msgb_free(cc); - msgb_free(udt); - msgb_free(clear); - goto send_refuse; - } - - bsc_write(bsc, cc, IPAC_PROTO_SCCP); - bsc_write(bsc, udt, IPAC_PROTO_SCCP); - bsc_write(bsc, clear, IPAC_PROTO_SCCP); - bsc_write(bsc, rlsd, IPAC_PROTO_SCCP); - msgb_free(payload); - return; - } - - -send_refuse: - if (payload) - msgb_free(payload); - - refuse = sccp_create_refuse(parsed->src_local_ref, - SCCP_REFUSAL_SCCP_FAILURE, NULL, 0); - if (!refuse) { - LOGP(DNAT, LOGL_ERROR, - "Creating refuse msg failed for SCCP 0x%x on BSC Nr: %d.\n", - sccp_src_ref_to_int(parsed->src_local_ref), bsc->cfg->nr); - return; - } - - bsc_write(bsc, refuse, IPAC_PROTO_SCCP); -} - -static void bsc_nat_send_paging(struct bsc_connection *bsc, struct msgb *msg) -{ - if (bsc->cfg->forbid_paging) { - LOGP(DNAT, LOGL_DEBUG, "Paging forbidden for BTS: %d\n", bsc->cfg->nr); - return; - } - - bsc_send_data(bsc, msg->l2h, msgb_l2len(msg), IPAC_PROTO_SCCP); -} - -static void bsc_nat_handle_paging(struct bsc_nat *nat, struct msgb *msg) -{ - struct bsc_connection *bsc; - const uint8_t *paging_start; - int paging_length, i, ret; - - ret = bsc_nat_find_paging(msg, &paging_start, &paging_length); - if (ret != 0) { - LOGP(DNAT, LOGL_ERROR, "Could not parse paging message: %d\n", ret); - return; - } - - /* This is quite expensive now */ - for (i = 0; i < paging_length; i += 2) { - unsigned int _lac = ntohs(*(unsigned int *) &paging_start[i]); - unsigned int paged = 0; - llist_for_each_entry(bsc, &nat->bsc_connections, list_entry) { - if (!bsc->cfg) - continue; - if (!bsc->authenticated) - continue; - if (!bsc_config_handles_lac(bsc->cfg, _lac)) - continue; - bsc_nat_send_paging(bsc, msg); - paged += 1; - } - - /* highlight a possible config issue */ - if (paged == 0) - LOGP(DNAT, LOGL_ERROR, "No BSC for LAC %d/0x%d\n", _lac, _lac); - - } -} - - -/* - * Update the auth status. This can be either a CIPHER MODE COMMAND or - * a CM Serivce Accept. Maybe also LU Accept or such in the future. - */ -static void update_con_authorize(struct nat_sccp_connection *con, - struct bsc_nat_parsed *parsed, - struct msgb *msg) -{ - if (!con) - return; - if (con->authorized) - return; - - if (parsed->bssap == BSSAP_MSG_BSS_MANAGEMENT && - parsed->gsm_type == BSS_MAP_MSG_CIPHER_MODE_CMD) { - con->authorized = 1; - } else if (parsed->bssap == BSSAP_MSG_DTAP) { - uint8_t msg_type, proto; - uint32_t len; - struct gsm48_hdr *hdr48; - hdr48 = bsc_unpack_dtap(parsed, msg, &len); - if (!hdr48) - return; - - proto = gsm48_hdr_pdisc(hdr48); - msg_type = gsm48_hdr_msg_type(hdr48); - if (proto == GSM48_PDISC_MM && - msg_type == GSM48_MT_MM_CM_SERV_ACC) - con->authorized = 1; - } -} - -static int forward_sccp_to_bts(struct bsc_msc_connection *msc_con, struct msgb *msg) -{ - struct nat_sccp_connection *con = NULL; - struct bsc_connection *bsc; - struct bsc_nat_parsed *parsed; - int proto; - - /* filter, drop, patch the message? */ - parsed = bsc_nat_parse(msg); - if (!parsed) { - LOGP(DNAT, LOGL_ERROR, "Can not parse msg from BSC.\n"); - return -1; - } - - if (bsc_nat_filter_ipa(DIR_BSC, msg, parsed)) - goto exit; - - proto = parsed->ipa_proto; - - /* Route and modify the SCCP packet */ - if (proto == IPAC_PROTO_SCCP) { - switch (parsed->sccp_type) { - case SCCP_MSG_TYPE_UDT: - /* forward UDT messages to every BSC */ - goto send_to_all; - break; - case SCCP_MSG_TYPE_RLSD: - case SCCP_MSG_TYPE_CREF: - case SCCP_MSG_TYPE_DT1: - case SCCP_MSG_TYPE_IT: - con = patch_sccp_src_ref_to_bsc(msg, parsed, nat); - if (parsed->gsm_type == BSS_MAP_MSG_ASSIGMENT_RQST) { - osmo_counter_inc(nat->stats.sccp.calls); - - if (con) { - struct rate_ctr_group *ctrg; - ctrg = con->bsc->cfg->stats.ctrg; - rate_ctr_inc(&ctrg->ctr[BCFG_CTR_SCCP_CALLS]); - if (bsc_mgcp_assign_patch(con, msg) != 0) - LOGP(DNAT, LOGL_ERROR, "Failed to assign...\n"); - } else - LOGP(DNAT, LOGL_ERROR, "Assignment command but no BSC.\n"); - } else if (con && con->con_local == NAT_CON_END_USSD && - parsed->gsm_type == BSS_MAP_MSG_CLEAR_CMD) { - LOGP(DNAT, LOGL_NOTICE, "Clear Command for USSD Connection. Ignoring.\n"); - con = NULL; - } - break; - case SCCP_MSG_TYPE_CC: - con = patch_sccp_src_ref_to_bsc(msg, parsed, nat); - if (!con || update_sccp_src_ref(con, parsed) != 0) - goto exit; - break; - case SCCP_MSG_TYPE_RLC: - LOGP(DNAT, LOGL_ERROR, "Unexpected release complete from MSC.\n"); - goto exit; - break; - case SCCP_MSG_TYPE_CR: - /* MSC never opens a SCCP connection, fall through */ - default: - goto exit; - } - - if (!con && parsed->sccp_type == SCCP_MSG_TYPE_RLSD) { - LOGP(DNAT, LOGL_NOTICE, "Sending fake RLC on RLSD message to network.\n"); - /* Exchange src/dest for the reply */ - nat_send_rlc(msc_con, &parsed->original_dest_ref, - parsed->src_local_ref); - } else if (!con) - LOGP(DNAT, LOGL_ERROR, "Unknown connection for msg type: 0x%x from the MSC.\n", parsed->sccp_type); - } - - if (!con) { - talloc_free(parsed); - return -1; - } - if (!con->bsc->authenticated) { - talloc_free(parsed); - LOGP(DNAT, LOGL_ERROR, "Selected BSC not authenticated.\n"); - return -1; - } - - update_con_authorize(con, parsed, msg); - talloc_free(parsed); - - bsc_send_data(con->bsc, msg->l2h, msgb_l2len(msg), proto); - return 0; - -send_to_all: - /* - * Filter Paging from the network. We do not want to send a PAGING - * Command to every BSC in our network. We will analys the PAGING - * message and then send it to the authenticated messages... - */ - if (parsed->ipa_proto == IPAC_PROTO_SCCP && parsed->gsm_type == BSS_MAP_MSG_PAGING) { - bsc_nat_handle_paging(nat, msg); - goto exit; - } - /* currently send this to every BSC connected */ - llist_for_each_entry(bsc, &nat->bsc_connections, list_entry) { - if (!bsc->authenticated) - continue; - - bsc_send_data(bsc, msg->l2h, msgb_l2len(msg), parsed->ipa_proto); - } - -exit: - talloc_free(parsed); - return 0; -} - -static void msc_connection_was_lost(struct bsc_msc_connection *con) -{ - struct bsc_connection *bsc, *tmp; - - LOGP(DMSC, LOGL_ERROR, "Closing all connections downstream.\n"); - llist_for_each_entry_safe(bsc, tmp, &nat->bsc_connections, list_entry) - bsc_close_connection(bsc); - - bsc_mgcp_free_endpoints(nat); - bsc_msc_schedule_connect(con); -} - -static void msc_connection_connected(struct bsc_msc_connection *con) -{ - osmo_counter_inc(nat->stats.msc.reconn); -} - -static void msc_send_reset(struct bsc_msc_connection *msc_con) -{ - static const uint8_t reset[] = { - 0x00, 0x12, 0xfd, - 0x09, 0x00, 0x03, 0x05, 0x07, 0x02, 0x42, 0xfe, - 0x02, 0x42, 0xfe, 0x06, 0x00, 0x04, 0x30, 0x04, - 0x01, 0x20 - }; - - struct msgb *msg; - - msg = msgb_alloc_headroom(4096, 128, "08.08 reset"); - if (!msg) { - LOGP(DMSC, LOGL_ERROR, "Failed to allocate reset msg.\n"); - return; - } - - msg->l2h = msgb_put(msg, sizeof(reset)); - memcpy(msg->l2h, reset, msgb_l2len(msg)); - - queue_for_msc(msc_con, msg); - - LOGP(DMSC, LOGL_NOTICE, "Scheduled GSM0808 reset msg for the MSC.\n"); -} - -static int ipaccess_msc_read_cb(struct osmo_fd *bfd) -{ - struct bsc_msc_connection *msc_con; - struct msgb *msg = NULL; - struct ipaccess_head *hh; - int ret; - - msc_con = (struct bsc_msc_connection *) bfd->data; - - ret = ipa_msg_recv_buffered(bfd->fd, &msg, &msc_con->pending_msg); - if (ret <= 0) { - if (ret == -EAGAIN) - return 0; - if (ret == 0) - LOGP(DNAT, LOGL_FATAL, - "The connection the MSC(%s) was lost, exiting\n", - msc_con->name); - else - LOGP(DNAT, LOGL_ERROR, - "Failed to parse ip access message on %s: %d\n", - msc_con->name, ret); - - bsc_msc_lost(msc_con); - return -1; - } - - LOGP(DNAT, LOGL_DEBUG, - "MSG from MSC(%s): %s proto: %d\n", msc_con->name, - osmo_hexdump(msg->data, msg->len), msg->l2h[0]); - - /* handle base message handling */ - hh = (struct ipaccess_head *) msg->data; - - /* initialize the networking. This includes sending a GSM08.08 message */ - if (hh->proto == IPAC_PROTO_IPACCESS) { - ipa_ccm_rcvmsg_base(msg, bfd); - if (msg->l2h[0] == IPAC_MSGT_ID_ACK) - initialize_msc_if_needed(msc_con); - else if (msg->l2h[0] == IPAC_MSGT_ID_GET) - send_id_get_response(msc_con); - } else if (hh->proto == IPAC_PROTO_SCCP) { - forward_sccp_to_bts(msc_con, msg); - } else if (hh->proto == IPAC_PROTO_MGCP_OLD) { - bsc_nat_handle_mgcp(nat, msg); - } - - msgb_free(msg); - return 0; -} - -static int ipaccess_msc_write_cb(struct osmo_fd *bfd, struct msgb *msg) -{ - int rc; - rc = write(bfd->fd, msg->data, msg->len); - - if (rc != msg->len) { - LOGP(DNAT, LOGL_ERROR, "Failed to write MSG to MSC.\n"); - return -1; - } - - return rc; -} - -/* - * Below is the handling of messages coming - * from the BSC and need to be forwarded to - * a real BSC. - */ - -/* - * Remove the connection from the connections list, - * remove it from the patching of SCCP header lists - * as well. Maybe in the future even close connection.. - */ -void bsc_close_connection(struct bsc_connection *connection) -{ - struct nat_sccp_connection *sccp_patch, *tmp; - struct bsc_cmd_list *cmd_entry, *cmd_tmp; - struct rate_ctr *ctr = NULL; - - /* stop the timeout timer */ - osmo_timer_del(&connection->id_timeout); - osmo_timer_del(&connection->ping_timeout); - osmo_timer_del(&connection->pong_timeout); - - if (connection->cfg) - ctr = &connection->cfg->stats.ctrg->ctr[BCFG_CTR_DROPPED_SCCP]; - - /* remove all SCCP connections */ - llist_for_each_entry_safe(sccp_patch, tmp, &nat->sccp_connections, list_entry) { - if (sccp_patch->bsc != connection) - continue; - - if (ctr) - rate_ctr_inc(ctr); - if (sccp_patch->has_remote_ref) { - if (sccp_patch->con_local == NAT_CON_END_MSC) - nat_send_rlsd_msc(sccp_patch); - else if (sccp_patch->con_local == NAT_CON_END_USSD) - nat_send_rlsd_ussd(nat, sccp_patch); - } - - sccp_connection_destroy(sccp_patch); - } - - /* Reply to all outstanding commands */ - llist_for_each_entry_safe(cmd_entry, cmd_tmp, &connection->cmd_pending, list_entry) { - cmd_entry->cmd->type = CTRL_TYPE_ERROR; - cmd_entry->cmd->reply = "BSC closed the connection"; - ctrl_cmd_send(&cmd_entry->ccon->write_queue, cmd_entry->cmd); - bsc_nat_ctrl_del_pending(cmd_entry); - } - - /* close endpoints allocated by this BSC */ - bsc_mgcp_clear_endpoints_for(connection); - - osmo_fd_unregister(&connection->write_queue.bfd); - close(connection->write_queue.bfd.fd); - osmo_wqueue_clear(&connection->write_queue); - llist_del(&connection->list_entry); - - if (connection->pending_msg) { - LOGP(DNAT, LOGL_ERROR, "Dropping partial message on connection %d.\n", - connection->cfg ? connection->cfg->nr : -1); - msgb_free(connection->pending_msg); - connection->pending_msg = NULL; - } - - talloc_free(connection); -} - -static void bsc_maybe_close(struct bsc_connection *bsc) -{ - struct nat_sccp_connection *sccp; - if (!bsc->nat->blocked) - return; - - /* are there any connections left */ - llist_for_each_entry(sccp, &bsc->nat->sccp_connections, list_entry) - if (sccp->bsc == bsc) - return; - - /* nothing left, close the BSC */ - LOGP(DNAT, LOGL_NOTICE, "Cleaning up BSC %d in blocking mode.\n", - bsc->cfg ? bsc->cfg->nr : -1); - bsc_close_connection(bsc); -} - -static void ipaccess_close_bsc(void *data) -{ - struct sockaddr_in sock; - socklen_t len = sizeof(sock); - struct bsc_connection *conn = data; - - - getpeername(conn->write_queue.bfd.fd, (struct sockaddr *) &sock, &len); - LOGP(DNAT, LOGL_ERROR, "BSC on %s didn't respond to identity request. Closing.\n", - inet_ntoa(sock.sin_addr)); - bsc_close_connection(conn); -} - -static int verify_key(struct bsc_connection *conn, struct bsc_config *conf, const uint8_t *key, const int keylen) -{ - struct osmo_auth_vector vec; - - struct osmo_sub_auth_data auth = { - .type = OSMO_AUTH_TYPE_GSM, - .algo = OSMO_AUTH_ALG_MILENAGE, - }; - - /* expect a specific keylen */ - if (keylen != 8) { - LOGP(DNAT, LOGL_ERROR, "Key length is wrong: %d for bsc nr %d\n", - keylen, conf->nr); - return 0; - } - - memcpy(auth.u.umts.opc, conf->key, 16); - memcpy(auth.u.umts.k, conf->key, 16); - memset(auth.u.umts.amf, 0, 2); - auth.u.umts.sqn = 0; - - memset(&vec, 0, sizeof(vec)); - osmo_auth_gen_vec(&vec, &auth, conn->last_rand); - - if (vec.res_len != 8) { - LOGP(DNAT, LOGL_ERROR, "Res length is wrong: %d for bsc nr %d\n", - vec.res_len, conf->nr); - return 0; - } - - return osmo_constant_time_cmp(vec.res, key, 8) == 0; -} - -static void ipaccess_auth_bsc(struct tlv_parsed *tvp, struct bsc_connection *bsc) -{ - struct bsc_config *conf; - const char *token = (const char *) TLVP_VAL(tvp, IPAC_IDTAG_UNITNAME); - int len = TLVP_LEN(tvp, IPAC_IDTAG_UNITNAME); - const uint8_t *xres = TLVP_VAL(tvp, 0x24); - const int xlen = TLVP_LEN(tvp, 0x24); - - if (bsc->cfg) { - LOGP(DNAT, LOGL_ERROR, "Reauth on fd %d bsc nr %d\n", - bsc->write_queue.bfd.fd, bsc->cfg->nr); - return; - } - - if (len <= 0) { - LOGP(DNAT, LOGL_ERROR, "Token with length zero on fd: %d\n", - bsc->write_queue.bfd.fd); - return; - } - - if (token[len - 1] != '\0') { - LOGP(DNAT, LOGL_ERROR, "Token not null terminated on fd: %d\n", - bsc->write_queue.bfd.fd); - return; - } - - /* - * New systems have fixed the structure of the message but - * we need to support old ones too. - */ - if (len >= 2 && token[len - 2] == '\0') - len -= 1; - - conf = bsc_config_by_token(bsc->nat, token, len); - if (!conf) { - LOGP(DNAT, LOGL_ERROR, - "No bsc found for token '%s' len %d on fd: %d.\n", token, - bsc->write_queue.bfd.fd, len); - bsc_close_connection(bsc); - return; - } - - /* We have set a key and expect it to be present */ - if (conf->key_present && !verify_key(bsc, conf, xres, xlen - 1)) { - LOGP(DNAT, LOGL_ERROR, - "Wrong key for bsc nr %d fd: %d.\n", conf->nr, - bsc->write_queue.bfd.fd); - bsc_close_connection(bsc); - return; - } - - rate_ctr_inc(&conf->stats.ctrg->ctr[BCFG_CTR_NET_RECONN]); - bsc->authenticated = 1; - bsc->cfg = conf; - osmo_timer_del(&bsc->id_timeout); - LOGP(DNAT, LOGL_NOTICE, "Authenticated bsc nr: %d on fd %d\n", - conf->nr, bsc->write_queue.bfd.fd); - start_ping_pong(bsc); -} - -static void handle_con_stats(struct nat_sccp_connection *con) -{ - struct rate_ctr_group *ctrg; - int id = bsc_conn_type_to_ctr(con); - - if (id == -1) - return; - - if (!con->bsc || !con->bsc->cfg) - return; - - ctrg = con->bsc->cfg->stats.ctrg; - rate_ctr_inc(&ctrg->ctr[id]); -} - -static int forward_sccp_to_msc(struct bsc_connection *bsc, struct msgb *msg) -{ - int con_filter = 0; - char *imsi = NULL; - struct bsc_msc_connection *con_msc = NULL; - struct bsc_connection *con_bsc = NULL; - int con_type; - struct bsc_nat_parsed *parsed; - struct bsc_filter_reject_cause cause; - - /* Parse and filter messages */ - parsed = bsc_nat_parse(msg); - if (!parsed) { - LOGP(DNAT, LOGL_ERROR, "Can not parse msg from BSC.\n"); - msgb_free(msg); - return -1; - } - - if (bsc_nat_filter_ipa(DIR_MSC, msg, parsed)) - goto exit; - - /* - * check authentication after filtering to not reject auth - * responses coming from the BSC. We have to make sure that - * nothing from the exit path will forward things to the MSC - */ - if (!bsc->authenticated) { - LOGP(DNAT, LOGL_ERROR, "BSC is not authenticated.\n"); - msgb_free(msg); - return -1; - } - - - /* modify the SCCP entries */ - if (parsed->ipa_proto == IPAC_PROTO_SCCP) { - int filter; - struct nat_sccp_connection *con; - switch (parsed->sccp_type) { - case SCCP_MSG_TYPE_CR: - memset(&cause, 0, sizeof(cause)); - filter = bsc_nat_filter_sccp_cr(bsc, msg, parsed, - &con_type, &imsi, &cause); - if (filter < 0) { - if (imsi) - bsc_nat_inform_reject(bsc, imsi); - bsc_stat_reject(filter, bsc, 0); - goto exit3; - } - - if (!create_sccp_src_ref(bsc, parsed)) - goto exit2; - con = patch_sccp_src_ref_to_msc(msg, parsed, bsc); - OSMO_ASSERT(con); - con->msc_con = bsc->nat->msc_con; - con_msc = con->msc_con; - con->filter_state.con_type = con_type; - con->filter_state.imsi_checked = filter; - bsc_nat_extract_lac(bsc, con, parsed, msg); - if (imsi) - con->filter_state.imsi = talloc_steal(con, imsi); - imsi = NULL; - con_bsc = con->bsc; - handle_con_stats(con); - break; - case SCCP_MSG_TYPE_RLSD: - case SCCP_MSG_TYPE_CREF: - case SCCP_MSG_TYPE_DT1: - case SCCP_MSG_TYPE_CC: - case SCCP_MSG_TYPE_IT: - con = patch_sccp_src_ref_to_msc(msg, parsed, bsc); - if (con) { - /* only filter non local connections */ - if (!con->con_local) { - memset(&cause, 0, sizeof(cause)); - filter = bsc_nat_filter_dt(bsc, msg, - con, parsed, &cause); - if (filter < 0) { - if (con->filter_state.imsi) - bsc_nat_inform_reject(bsc, - con->filter_state.imsi); - bsc_stat_reject(filter, bsc, 1); - bsc_send_con_release(bsc, con, &cause); - con = NULL; - goto exit2; - } - - /* hand data to a side channel */ - if (bsc_ussd_check(con, parsed, msg) == 1) - con->con_local = NAT_CON_END_USSD; - - /* - * Optionally rewrite setup message. This can - * replace the msg and the parsed structure becomes - * invalid. - */ - msg = bsc_nat_rewrite_msg(bsc->nat, msg, parsed, - con->filter_state.imsi); - talloc_free(parsed); - parsed = NULL; - } else if (con->con_local == NAT_CON_END_USSD) { - bsc_ussd_check(con, parsed, msg); - } - - con_bsc = con->bsc; - con_msc = con->msc_con; - con_filter = con->con_local; - } - - break; - case SCCP_MSG_TYPE_RLC: - con = patch_sccp_src_ref_to_msc(msg, parsed, bsc); - if (con) { - con_bsc = con->bsc; - con_msc = con->msc_con; - con_filter = con->con_local; - } - remove_sccp_src_ref(bsc, msg, parsed); - bsc_maybe_close(bsc); - break; - case SCCP_MSG_TYPE_UDT: - /* simply forward everything */ - con = NULL; - break; - default: - LOGP(DNAT, LOGL_ERROR, "Not forwarding to msc sccp type: 0x%x\n", parsed->sccp_type); - con = NULL; - goto exit2; - break; - } - } else if (parsed->ipa_proto == IPAC_PROTO_MGCP_OLD) { - bsc_mgcp_forward(bsc, msg); - goto exit2; - } else { - LOGP(DNAT, LOGL_ERROR, "Not forwarding unknown stream id: 0x%x\n", parsed->ipa_proto); - goto exit2; - } - - if (con_msc && con_bsc != bsc) { - LOGP(DNAT, LOGL_ERROR, "The connection belongs to a different BTS: input: %d con: %d\n", - bsc->cfg->nr, con_bsc->cfg->nr); - goto exit2; - } - - /* do not forward messages to the MSC */ - if (con_filter) - goto exit2; - - if (!con_msc) { - LOGP(DNAT, LOGL_ERROR, "Not forwarding data bsc_nr: %d ipa: %d type: 0x%x\n", - bsc->cfg->nr, - parsed ? parsed->ipa_proto : -1, - parsed ? parsed->sccp_type : -1); - goto exit2; - } - - /* send the non-filtered but maybe modified msg */ - queue_for_msc(con_msc, msg); - if (parsed) - talloc_free(parsed); - return 0; - -exit: - /* if we filter out the reset send an ack to the BSC */ - if (parsed->bssap == 0 && parsed->gsm_type == BSS_MAP_MSG_RESET) { - send_reset_ack(bsc); - send_reset_ack(bsc); - } else if (parsed->ipa_proto == IPAC_PROTO_IPACCESS) { - /* do we know who is handling this? */ - if (msg->l2h[0] == IPAC_MSGT_ID_RESP && msgb_l2len(msg) > 2) { - struct tlv_parsed tvp; - int ret; - ret = ipa_ccm_idtag_parse_off(&tvp, - (unsigned char *) msg->l2h + 2, - msgb_l2len(msg) - 2, 0); - if (ret < 0) { - LOGP(DNAT, LOGL_ERROR, "ignoring IPA response " - "message with malformed TLVs\n"); - return ret; - } - if (TLVP_PRESENT(&tvp, IPAC_IDTAG_UNITNAME)) - ipaccess_auth_bsc(&tvp, bsc); - } - - goto exit2; - } - -exit2: - if (imsi) - talloc_free(imsi); - talloc_free(parsed); - msgb_free(msg); - return -1; - -exit3: - /* send a SCCP Connection Refused */ - if (imsi) - talloc_free(imsi); - bsc_send_con_refuse(bsc, parsed, con_type, &cause); - talloc_free(parsed); - msgb_free(msg); - return -1; -} - -static int ipaccess_bsc_read_cb(struct osmo_fd *bfd) -{ - struct bsc_connection *bsc = bfd->data; - struct msgb *msg = NULL; - struct ipaccess_head *hh; - struct ipaccess_head_ext *hh_ext; - int ret; - - ret = ipa_msg_recv_buffered(bfd->fd, &msg, &bsc->pending_msg); - if (ret <= 0) { - if (ret == -EAGAIN) - return 0; - if (ret == 0) - LOGP(DNAT, LOGL_ERROR, - "The connection to the BSC Nr: %d was lost. Cleaning it\n", - bsc->cfg ? bsc->cfg->nr : -1); - else - LOGP(DNAT, LOGL_ERROR, - "Stream error on BSC Nr: %d. Failed to parse ip access message: %d (%s)\n", - bsc->cfg ? bsc->cfg->nr : -1, ret, strerror(-ret)); - - bsc_close_connection(bsc); - return -1; - } - - - LOGP(DNAT, LOGL_DEBUG, "MSG from BSC: %s proto: %d\n", osmo_hexdump(msg->data, msg->len), msg->l2h[0]); - - /* Handle messages from the BSC */ - hh = (struct ipaccess_head *) msg->data; - - /* stop the pong timeout */ - if (hh->proto == IPAC_PROTO_IPACCESS) { - if (msg->l2h[0] == IPAC_MSGT_PONG) { - osmo_timer_del(&bsc->pong_timeout); - msgb_free(msg); - return 0; - } else if (msg->l2h[0] == IPAC_MSGT_PING) { - send_pong(bsc); - msgb_free(msg); - return 0; - } - /* Message contains the ipaccess_head_ext header, investigate further */ - } else if (hh->proto == IPAC_PROTO_OSMO && - msg->len > sizeof(*hh) + sizeof(*hh_ext)) { - - hh_ext = (struct ipaccess_head_ext *) hh->data; - /* l2h is where the actual command data is expected */ - msg->l2h = hh_ext->data; - - if (hh_ext->proto == IPAC_PROTO_EXT_CTRL) - return bsc_nat_handle_ctrlif_msg(bsc, msg); - } - - /* FIXME: Currently no PONG is sent to the BSC */ - /* FIXME: Currently no ID ACK is sent to the BSC */ - forward_sccp_to_msc(bsc, msg); - - return 0; -} - -static int ipaccess_listen_bsc_cb(struct osmo_fd *bfd, unsigned int what) -{ - struct bsc_connection *bsc; - int fd, rc, on; - struct sockaddr_in sa; - socklen_t sa_len = sizeof(sa); - - if (!(what & BSC_FD_READ)) - return 0; - - fd = accept(bfd->fd, (struct sockaddr *) &sa, &sa_len); - if (fd < 0) { - perror("accept"); - return fd; - } - - /* count the reconnect */ - osmo_counter_inc(nat->stats.bsc.reconn); - - /* - * if we are not connected to a msc... just close the socket - */ - if (!bsc_nat_msc_is_connected(nat)) { - LOGP(DNAT, LOGL_NOTICE, "Disconnecting BSC due lack of MSC connection.\n"); - close(fd); - return 0; - } - - if (nat->blocked) { - LOGP(DNAT, LOGL_NOTICE, "Disconnecting BSC due NAT being blocked.\n"); - close(fd); - return 0; - } - - on = 1; - rc = setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &on, sizeof(on)); - if (rc != 0) - LOGP(DNAT, LOGL_ERROR, "Failed to set TCP_NODELAY: %s\n", strerror(errno)); - - rc = setsockopt(fd, IPPROTO_IP, IP_TOS, - &nat->bsc_ip_dscp, sizeof(nat->bsc_ip_dscp)); - if (rc != 0) - LOGP(DNAT, LOGL_ERROR, "Failed to set IP_TOS: %s\n", strerror(errno)); - - /* todo... do something with the connection */ - /* todo... use GNUtls to see if we want to trust this as a BTS */ - - /* - * - */ - bsc = bsc_connection_alloc(nat); - if (!bsc) { - LOGP(DNAT, LOGL_ERROR, "Failed to allocate BSC struct.\n"); - close(fd); - return -1; - } - - bsc->write_queue.bfd.data = bsc; - bsc->write_queue.bfd.fd = fd; - bsc->write_queue.read_cb = ipaccess_bsc_read_cb; - bsc->write_queue.write_cb = bsc_write_cb; - bsc->write_queue.bfd.when = BSC_FD_READ; - if (osmo_fd_register(&bsc->write_queue.bfd) < 0) { - LOGP(DNAT, LOGL_ERROR, "Failed to register BSC fd.\n"); - close(fd); - talloc_free(bsc); - return -2; - } - - LOGP(DNAT, LOGL_NOTICE, "BSC connection on %d with IP: %s\n", - fd, inet_ntoa(sa.sin_addr)); - - llist_add(&bsc->list_entry, &nat->bsc_connections); - bsc->last_id = 0; - - send_id_ack(bsc); - send_id_req(nat, bsc); - send_mgcp_reset(bsc); - - /* - * start the hangup timer - */ - osmo_timer_setup(&bsc->id_timeout, ipaccess_close_bsc, bsc); - osmo_timer_schedule(&bsc->id_timeout, nat->auth_timeout, 0); - return 0; -} - -static void print_usage() -{ - printf("Usage: bsc_nat\n"); -} - -static void print_help() -{ - printf(" Some useful help...\n"); - printf(" -h --help this text\n"); - printf(" -d option --debug=DRLL:DCC:DMM:DRR:DRSL:DNM enable debugging\n"); - printf(" -D --daemonize Fork the process into a background daemon\n"); - printf(" -s --disable-color\n"); - printf(" -c --config-file filename The config file to use.\n"); - printf(" -m --msc=IP. The address of the MSC.\n"); - printf(" -l --local=IP. The local address of this BSC.\n"); -} - -static void handle_options(int argc, char **argv) -{ - while (1) { - int option_index = 0, c; - static struct option long_options[] = { - {"help", 0, 0, 'h'}, - {"debug", 1, 0, 'd'}, - {"daemonize", 0, 0, 'D'}, - {"config-file", 1, 0, 'c'}, - {"disable-color", 0, 0, 's'}, - {"timestamp", 0, 0, 'T'}, - {"msc", 1, 0, 'm'}, - {"local", 1, 0, 'l'}, - {0, 0, 0, 0} - }; - - c = getopt_long(argc, argv, "hd:sTPc:m:l:D", - long_options, &option_index); - if (c == -1) - break; - - switch (c) { - case 'h': - print_usage(); - print_help(); - exit(0); - case 's': - log_set_use_color(osmo_stderr_target, 0); - break; - case 'd': - log_parse_category_mask(osmo_stderr_target, optarg); - break; - case 'D': - daemonize = 1; - break; - case 'c': - config_file = optarg; - break; - case 'T': - log_set_print_timestamp(osmo_stderr_target, 1); - break; - case 'm': - msc_ip = optarg; - break; - case 'l': - inet_aton(optarg, &local_addr); - break; - default: - /* ignore */ - break; - } - } -} - -static void signal_handler(int signal) -{ - switch (signal) { - case SIGABRT: - /* in case of abort, we want to obtain a talloc report - * and then return to the caller, who will abort the process */ - case SIGUSR1: - talloc_report_full(tall_bsc_ctx, stderr); - break; - default: - break; - } -} - -static void sccp_close_unconfirmed(void *_data) -{ - int destroyed = 0; - struct bsc_connection *bsc, *bsc_tmp; - struct nat_sccp_connection *conn, *tmp1; - struct timespec now; - clock_gettime(CLOCK_MONOTONIC, &now); - - llist_for_each_entry_safe(conn, tmp1, &nat->sccp_connections, list_entry) { - if (conn->has_remote_ref) - continue; - - int diff = (now.tv_sec - conn->creation_time.tv_sec) / 60; - if (diff < SCCP_CLOSE_TIME_TIMEOUT) - continue; - - LOGP(DNAT, LOGL_ERROR, - "SCCP connection 0x%x/0x%x was never confirmed on bsc nr. %d\n", - sccp_src_ref_to_int(&conn->real_ref), - sccp_src_ref_to_int(&conn->patched_ref), - conn->bsc->cfg->nr); - sccp_connection_destroy(conn); - destroyed = 1; - } - - if (!destroyed) - goto out; - - /* now close out any BSC */ - llist_for_each_entry_safe(bsc, bsc_tmp, &nat->bsc_connections, list_entry) - bsc_maybe_close(bsc); - -out: - osmo_timer_schedule(&sccp_close, SCCP_CLOSE_TIME, 0); -} - -extern void *tall_ctr_ctx; -static void talloc_init_ctx() -{ - tall_bsc_ctx = talloc_named_const(NULL, 0, "nat"); - msgb_talloc_ctx_init(tall_bsc_ctx, 0); - tall_ctr_ctx = talloc_named_const(tall_bsc_ctx, 0, "counter"); -} - -extern int bsc_vty_go_parent(struct vty *vty); - -static struct vty_app_info vty_info = { - .name = "OsmoBSCNAT", - .version = PACKAGE_VERSION, - .go_parent_cb = bsc_vty_go_parent, - .is_config_node = bsc_vty_is_config_node, -}; - - -int main(int argc, char **argv) -{ - int rc; - - talloc_init_ctx(); - - osmo_init_logging(&log_info); - - nat = bsc_nat_alloc(); - if (!nat) { - fprintf(stderr, "Failed to allocate the BSC nat.\n"); - return -4; - } - - nat->mgcp_cfg = mgcp_config_alloc(); - if (!nat->mgcp_cfg) { - fprintf(stderr, "Failed to allocate MGCP cfg.\n"); - return -5; - } - - /* We need to add mode-set for amr codecs */ - nat->sdp_ensure_amr_mode_set = 1; - - vty_info.copyright = openbsc_copyright; - vty_init(&vty_info); - logging_vty_add_cmds(NULL); - osmo_stats_vty_add_cmds(&log_info); - bsc_nat_vty_init(nat); - ctrl_vty_init(tall_bsc_ctx); - - - /* parse options */ - local_addr.s_addr = INADDR_ANY; - handle_options(argc, argv); - - nat->include_base = dirname(talloc_strdup(tall_bsc_ctx, config_file)); - - rate_ctr_init(tall_bsc_ctx); - osmo_stats_init(tall_bsc_ctx); - - /* init vty and parse */ - if (mgcp_parse_config(config_file, nat->mgcp_cfg, MGCP_BSC_NAT) < 0) { - fprintf(stderr, "Failed to parse the config file: '%s'\n", config_file); - return -3; - } - - /* start telnet after reading config for vty_get_bind_addr() */ - if (telnet_init_dynif(tall_bsc_ctx, NULL, vty_get_bind_addr(), - OSMO_VTY_PORT_BSC_NAT)) { - fprintf(stderr, "Creating VTY telnet line failed\n"); - return -5; - } - - /* over rule the VTY config for MSC IP */ - if (msc_ip) - bsc_nat_set_msc_ip(nat, msc_ip); - - /* seed the PRNG */ - srand(time(NULL)); - - LOGP(DNAT, LOGL_NOTICE, "BSCs configured from %s\n", nat->resolved_path); - - /* - * Setup the MGCP code.. - */ - if (bsc_mgcp_nat_init(nat) != 0) - return -4; - - /* connect to the MSC */ - nat->msc_con = bsc_msc_create(nat, &nat->dests); - if (!nat->msc_con) { - fprintf(stderr, "Creating a bsc_msc_connection failed.\n"); - exit(1); - } - - /* start control interface after reading config for - * ctrl_vty_get_bind_addr() */ - nat->ctrl = bsc_nat_controlif_setup(nat, ctrl_vty_get_bind_addr(), - OSMO_CTRL_PORT_BSC_NAT); - if (!nat->ctrl) { - fprintf(stderr, "Creating the control interface failed.\n"); - exit(1); - } - - nat->msc_con->name = "main MSC"; - nat->msc_con->connection_loss = msc_connection_was_lost; - nat->msc_con->connected = msc_connection_connected; - nat->msc_con->write_queue.read_cb = ipaccess_msc_read_cb; - nat->msc_con->write_queue.write_cb = ipaccess_msc_write_cb;; - nat->msc_con->write_queue.bfd.data = nat->msc_con; - bsc_msc_connect(nat->msc_con); - - /* wait for the BSC */ - rc = make_sock(&bsc_listen, IPPROTO_TCP, ntohl(local_addr.s_addr), - 5000, 0, ipaccess_listen_bsc_cb, nat); - if (rc != 0) { - fprintf(stderr, "Failed to listen for BSC.\n"); - exit(1); - } - - rc = bsc_ussd_init(nat); - if (rc != 0) { - LOGP(DNAT, LOGL_ERROR, "Failed to bind the USSD socket.\n"); - exit(1); - } - - signal(SIGABRT, &signal_handler); - signal(SIGUSR1, &signal_handler); - osmo_init_ignore_signals(); - - if (daemonize) { - rc = osmo_daemonize(); - if (rc < 0) { - perror("Error during daemonize"); - exit(1); - } - } - - /* recycle timer */ - sccp_set_log_area(DSCCP); - osmo_timer_setup(&sccp_close, sccp_close_unconfirmed, NULL); - osmo_timer_schedule(&sccp_close, SCCP_CLOSE_TIME, 0); - - while (1) { - osmo_select_main(0); - } - - return 0; -} - -/* Close all connections handed out to the USSD module */ -int bsc_ussd_close_connections(struct bsc_nat *nat) -{ - struct nat_sccp_connection *con; - llist_for_each_entry(con, &nat->sccp_connections, list_entry) { - if (con->con_local != NAT_CON_END_USSD) - continue; - if (!con->bsc) - continue; - - nat_send_clrc_bsc(con); - nat_send_rlsd_bsc(con); - } - - return 0; -} diff --git a/openbsc/src/osmo-bsc_nat/bsc_nat_ctrl.c b/openbsc/src/osmo-bsc_nat/bsc_nat_ctrl.c deleted file mode 100644 index 128ea651..00000000 --- a/openbsc/src/osmo-bsc_nat/bsc_nat_ctrl.c +++ /dev/null @@ -1,524 +0,0 @@ -/* - * (C) 2011-2012 by Holger Hans Peter Freyther - * (C) 2011-2012 by On-Waves - * (C) 2011 by Daniel Willmann - * All Rights Reserved - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation; either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - * - */ - -#include - -#include -#include -#include - -#include - -#include -#include -#include -#include -#include - -#include -#include -#include - - -#define NAT_MAX_CTRL_ID 65535 - -static struct bsc_nat *g_nat; - -static int bsc_id_unused(int id, struct bsc_connection *bsc) -{ - struct bsc_cmd_list *pending; - - llist_for_each_entry(pending, &bsc->cmd_pending, list_entry) { - if (pending->nat_id == id) - return 0; - } - return 1; -} - -static int get_next_free_bsc_id(struct bsc_connection *bsc) -{ - int new_id, overflow = 0; - - new_id = bsc->last_id; - - do { - new_id++; - if (new_id == NAT_MAX_CTRL_ID) { - new_id = 1; - overflow++; - } - - if (bsc_id_unused(new_id, bsc)) { - bsc->last_id = new_id; - return new_id; - } - } while (overflow != 2); - - return -1; -} - -void bsc_nat_ctrl_del_pending(struct bsc_cmd_list *pending) -{ - llist_del(&pending->list_entry); - osmo_timer_del(&pending->timeout); - talloc_free(pending->cmd); - talloc_free(pending); -} - -static struct bsc_cmd_list *bsc_get_pending(struct bsc_connection *bsc, char *id_str) -{ - struct bsc_cmd_list *cmd_entry; - int id = atoi(id_str); - if (id == 0) - return NULL; - - llist_for_each_entry(cmd_entry, &bsc->cmd_pending, list_entry) { - if (cmd_entry->nat_id == id) { - return cmd_entry; - } - } - return NULL; -} - -int bsc_nat_handle_ctrlif_msg(struct bsc_connection *bsc, struct msgb *msg) -{ - struct ctrl_cmd *cmd; - struct bsc_cmd_list *pending; - char *var, *id; - - cmd = ctrl_cmd_parse(bsc, msg); - msgb_free(msg); - - if (!cmd) { - cmd = talloc_zero(bsc, struct ctrl_cmd); - if (!cmd) { - LOGP(DNAT, LOGL_ERROR, "OOM!\n"); - return -ENOMEM; - } - cmd->type = CTRL_TYPE_ERROR; - cmd->id = "err"; - cmd->reply = "Failed to parse command."; - goto err; - } - - if (bsc->cfg && !llist_empty(&bsc->cfg->lac_list)) { - if (cmd->variable) { - var = talloc_asprintf(cmd, "net.0.bsc.%i.%s", bsc->cfg->nr, - cmd->variable); - if (!var) { - cmd->type = CTRL_TYPE_ERROR; - cmd->reply = "OOM"; - goto err; - } - talloc_free(cmd->variable); - cmd->variable = var; - } - - /* We have to handle TRAPs before matching pending */ - if (cmd->type == CTRL_TYPE_TRAP) { - ctrl_cmd_send_to_all(bsc->nat->ctrl, cmd); - talloc_free(cmd); - return 0; - } - - /* Find the pending command */ - pending = bsc_get_pending(bsc, cmd->id); - if (pending) { - id = talloc_strdup(cmd, pending->cmd->id); - if (!id) { - cmd->type = CTRL_TYPE_ERROR; - cmd->reply = "OOM"; - goto err; - } - cmd->id = id; - ctrl_cmd_send(&pending->ccon->write_queue, cmd); - bsc_nat_ctrl_del_pending(pending); - } else { - /* We need to handle TRAPS here */ - if ((cmd->type != CTRL_TYPE_ERROR) && - (cmd->type != CTRL_TYPE_TRAP)) { - LOGP(DNAT, LOGL_NOTICE, "Got control message " - "from BSC without pending entry\n"); - cmd->type = CTRL_TYPE_ERROR; - cmd->reply = "No request outstanding"; - goto err; - } - } - } - talloc_free(cmd); - return 0; -err: - ctrl_cmd_send(&bsc->write_queue, cmd); - talloc_free(cmd); - return 0; -} - -static void pending_timeout_cb(void *data) -{ - struct bsc_cmd_list *pending = data; - LOGP(DNAT, LOGL_ERROR, "Command timed out\n"); - pending->cmd->type = CTRL_TYPE_ERROR; - pending->cmd->reply = "Command timed out"; - ctrl_cmd_send(&pending->ccon->write_queue, pending->cmd); - - bsc_nat_ctrl_del_pending(pending); -} - -static void ctrl_conn_closed_cb(struct ctrl_connection *connection) -{ - struct bsc_connection *bsc; - struct bsc_cmd_list *pending, *tmp; - - llist_for_each_entry(bsc, &g_nat->bsc_connections, list_entry) { - llist_for_each_entry_safe(pending, tmp, &bsc->cmd_pending, list_entry) { - if (pending->ccon == connection) - bsc_nat_ctrl_del_pending(pending); - } - } -} - -static int extract_bsc_nr_variable(char *variable, unsigned int *nr, char **bsc_variable) -{ - char *nr_str, *tmp, *saveptr = NULL; - - tmp = strtok_r(variable, ".", &saveptr); - tmp = strtok_r(NULL, ".", &saveptr); - tmp = strtok_r(NULL, ".", &saveptr); - nr_str = strtok_r(NULL, ".", &saveptr); - if (!nr_str) - return 0; - *nr = atoi(nr_str); - - tmp = strtok_r(NULL, "\0", &saveptr); - if (!tmp) - return 0; - - *bsc_variable = tmp; - return 1; -} - -static int forward_to_bsc(struct ctrl_cmd *cmd) -{ - int ret = CTRL_CMD_HANDLED; - struct ctrl_cmd *bsc_cmd = NULL; - struct bsc_connection *bsc; - struct bsc_cmd_list *pending; - unsigned int nr; - char *bsc_variable; - - /* Skip over the beginning (bsc.) */ - if (!extract_bsc_nr_variable(cmd->variable, &nr, &bsc_variable)) { - cmd->reply = "command incomplete"; - goto err; - } - - - llist_for_each_entry(bsc, &g_nat->bsc_connections, list_entry) { - if (!bsc->cfg) - continue; - if (!bsc->authenticated) - continue; - if (bsc->cfg->nr == nr) { - /* Add pending command to list */ - pending = talloc_zero(bsc, struct bsc_cmd_list); - if (!pending) { - cmd->reply = "OOM"; - goto err; - } - - pending->nat_id = get_next_free_bsc_id(bsc); - if (pending->nat_id < 0) { - cmd->reply = "No free ID found"; - goto err; - } - - bsc_cmd = ctrl_cmd_cpy(bsc, cmd); - if (!bsc_cmd) { - cmd->reply = "Could not forward command"; - goto err; - } - - talloc_free(bsc_cmd->id); - bsc_cmd->id = talloc_asprintf(bsc_cmd, "%i", pending->nat_id); - if (!bsc_cmd->id) { - cmd->reply = "OOM"; - goto err; - } - - talloc_free(bsc_cmd->variable); - bsc_cmd->variable = talloc_strdup(bsc_cmd, bsc_variable); - if (!bsc_cmd->variable) { - cmd->reply = "OOM"; - goto err; - } - - if (ctrl_cmd_send(&bsc->write_queue, bsc_cmd)) { - cmd->reply = "Sending failed"; - goto err; - } - pending->ccon = cmd->ccon; - pending->ccon->closed_cb = ctrl_conn_closed_cb; - pending->cmd = cmd; - - /* Setup the timeout */ - osmo_timer_setup(&pending->timeout, pending_timeout_cb, - pending); - /* TODO: Make timeout configurable */ - osmo_timer_schedule(&pending->timeout, 10, 0); - llist_add_tail(&pending->list_entry, &bsc->cmd_pending); - - goto done; - } - } - /* We end up here if there's no bsc to handle our LAC */ - cmd->reply = "no BSC with this nr"; -err: - ret = CTRL_CMD_ERROR; -done: - talloc_free(bsc_cmd); - return ret; - -} - - -CTRL_CMD_DEFINE(fwd_cmd, "net 0 bsc *"); -static int get_fwd_cmd(struct ctrl_cmd *cmd, void *data) -{ - return forward_to_bsc(cmd); -} - -static int set_fwd_cmd(struct ctrl_cmd *cmd, void *data) -{ - return forward_to_bsc(cmd); -} - -static int verify_fwd_cmd(struct ctrl_cmd *cmd, const char *value, void *data) -{ - return 0; -} - -static int extract_bsc_cfg_variable(struct ctrl_cmd *cmd, struct bsc_config **cfg, - char **bsc_variable) -{ - unsigned int nr; - - if (!extract_bsc_nr_variable(cmd->variable, &nr, bsc_variable)) { - cmd->reply = "command incomplete"; - return 0; - } - - *cfg = bsc_config_num(g_nat, nr); - if (!*cfg) { - cmd->reply = "Unknown BSC"; - return 0; - } - - return 1; -} - -CTRL_CMD_DEFINE(net_cfg_cmd, "net 0 bsc_cfg *"); -static int get_net_cfg_cmd(struct ctrl_cmd *cmd, void *data) -{ - char *bsc_variable; - struct bsc_config *bsc_cfg; - - if (!extract_bsc_cfg_variable(cmd, &bsc_cfg, &bsc_variable)) - return CTRL_CMD_ERROR; - - if (strcmp(bsc_variable, "access-list-name") == 0) { - cmd->reply = talloc_asprintf(cmd, "%s", - bsc_cfg->acc_lst_name ? bsc_cfg->acc_lst_name : ""); - return CTRL_CMD_REPLY; - } - - cmd->reply = "unknown command"; - return CTRL_CMD_ERROR; -} - -static int set_net_cfg_cmd(struct ctrl_cmd *cmd, void *data) -{ - char *bsc_variable; - struct bsc_config *bsc_cfg; - - if (!extract_bsc_cfg_variable(cmd, &bsc_cfg, &bsc_variable)) - return CTRL_CMD_ERROR; - - if (strcmp(bsc_variable, "access-list-name") == 0) { - osmo_talloc_replace_string(bsc_cfg, &bsc_cfg->acc_lst_name, cmd->value); - cmd->reply = talloc_asprintf(cmd, "%s", - bsc_cfg->acc_lst_name ? bsc_cfg->acc_lst_name : ""); - return CTRL_CMD_REPLY; - } else if (strcmp(bsc_variable, "no-access-list-name") == 0) { - talloc_free(bsc_cfg->acc_lst_name); - bsc_cfg->acc_lst_name = NULL; - cmd->reply = ""; - return CTRL_CMD_REPLY; - } - - cmd->reply = "unknown command"; - return CTRL_CMD_ERROR; -} - -static int verify_net_cfg_cmd(struct ctrl_cmd *cmd, const char *value, void *data) -{ - return 0; -} - -CTRL_CMD_DEFINE(net_cfg_acc_cmd, "net 0 add allow access-list *"); -static const char *extract_acc_name(const char *var) -{ - char *str; - - str = strstr(var, "net.0.add.allow.access-list."); - if (!str) - return NULL; - str += strlen("net.0.add.allow.access-list."); - if (strlen(str) == 0) - return NULL; - return str; -} - -static int get_net_cfg_acc_cmd(struct ctrl_cmd *cmd, void *data) -{ - cmd->reply = "Append only"; - return CTRL_CMD_ERROR; -} - -static int set_net_cfg_acc_cmd(struct ctrl_cmd *cmd, void *data) -{ - const char *access_name = extract_acc_name(cmd->variable); - struct bsc_msg_acc_lst *acc; - struct bsc_msg_acc_lst_entry *entry; - const char *value = cmd->value; - int rc; - - /* Should have been caught by verify_net_cfg_acc_cmd */ - acc = bsc_msg_acc_lst_find(&g_nat->access_lists, access_name); - if (!acc) { - cmd->reply = "Access list not found"; - return CTRL_CMD_ERROR; - } - - entry = bsc_msg_acc_lst_entry_create(acc); - if (!entry) { - cmd->reply = "OOM"; - return CTRL_CMD_ERROR; - } - - rc = gsm_parse_reg(acc, &entry->imsi_allow_re, &entry->imsi_allow, 1, &value); - if (rc != 0) { - cmd->reply = "Failed to compile expression"; - return CTRL_CMD_ERROR; - } - - cmd->reply = "IMSI allow added to access list"; - return CTRL_CMD_REPLY; -} - -static int verify_net_cfg_acc_cmd(struct ctrl_cmd *cmd, const char *value, void *data) -{ - const char *access_name = extract_acc_name(cmd->variable); - struct bsc_msg_acc_lst *acc = bsc_msg_acc_lst_find(&g_nat->access_lists, access_name); - - if (!acc) { - cmd->reply = "Access list not known"; - return -1; - } - - return 0; -} - -CTRL_CMD_DEFINE_WO_NOVRF(net_save_cmd, "net 0 save-configuration"); - -static int set_net_save_cmd(struct ctrl_cmd *cmd, void *data) -{ - int rc = osmo_vty_save_config_file(); - cmd->reply = talloc_asprintf(cmd, "%d", rc); - if (!cmd->reply) { - cmd->reply = "OOM"; - return CTRL_CMD_ERROR; - } - - return CTRL_CMD_REPLY; -} - -struct ctrl_handle *bsc_nat_controlif_setup(struct bsc_nat *nat, - const char *bind_addr, int port) -{ - struct ctrl_handle *ctrl; - int rc; - - - ctrl = bsc_controlif_setup(NULL, bind_addr, OSMO_CTRL_PORT_BSC_NAT); - if (!ctrl) { - fprintf(stderr, "Failed to initialize the control interface. Exiting.\n"); - return NULL; - } - - rc = ctrl_cmd_install(CTRL_NODE_ROOT, &cmd_fwd_cmd); - if (rc) { - fprintf(stderr, "Failed to install the control command. Exiting.\n"); - goto error; - } - rc = ctrl_cmd_install(CTRL_NODE_ROOT, &cmd_net_cfg_cmd); - if (rc) { - fprintf(stderr, "Failed to install the net cfg command. Exiting.\n"); - goto error; - } - rc = ctrl_cmd_install(CTRL_NODE_ROOT, &cmd_net_cfg_acc_cmd); - if (rc) { - fprintf(stderr, "Failed to install the net acc command. Exiting.\n"); - goto error; - } - rc = ctrl_cmd_install(CTRL_NODE_ROOT, &cmd_net_save_cmd); - if (rc) { - fprintf(stderr, "Failed to install the net save command. Exiting.\n"); - goto error; - } - - g_nat = nat; - return ctrl; - -error: - osmo_fd_unregister(&ctrl->listen_fd); - close(ctrl->listen_fd.fd); - talloc_free(ctrl); - return NULL; -} - -void bsc_nat_inform_reject(struct bsc_connection *conn, const char *imsi) -{ - struct ctrl_cmd *cmd; - - cmd = ctrl_cmd_create(conn, CTRL_TYPE_TRAP); - if (!cmd) { - LOGP(DCTRL, LOGL_ERROR, "Failed to create TRAP command.\n"); - return; - } - - cmd->id = "0"; - cmd->variable = talloc_asprintf(cmd, "net.0.bsc.%d.notification-rejection-v1", - conn->cfg->nr); - cmd->reply = talloc_asprintf(cmd, "imsi=%s", imsi); - - ctrl_cmd_send_to_all(conn->cfg->nat->ctrl, cmd); - talloc_free(cmd); -} diff --git a/openbsc/src/osmo-bsc_nat/bsc_nat_filter.c b/openbsc/src/osmo-bsc_nat/bsc_nat_filter.c deleted file mode 100644 index e7352909..00000000 --- a/openbsc/src/osmo-bsc_nat/bsc_nat_filter.c +++ /dev/null @@ -1,119 +0,0 @@ -/* - * (C) 2010-2015 by Holger Hans Peter Freyther - * (C) 2010-2012 by On-Waves - * All Rights Reserved - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation; either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - * - */ - -#include -#include -#include -#include - -#include - -#include -#include - -#include - -/* Filter out CR data... */ -int bsc_nat_filter_sccp_cr(struct bsc_connection *bsc, struct msgb *msg, - struct bsc_nat_parsed *parsed, int *con_type, - char **imsi, struct bsc_filter_reject_cause *cause) -{ - struct bsc_filter_request req; - struct tlv_parsed tp; - struct gsm48_hdr *hdr48; - int hdr48_len; - int len; - - *con_type = FLT_CON_TYPE_NONE; - cause->cm_reject_cause = GSM48_REJECT_PLMN_NOT_ALLOWED; - cause->lu_reject_cause = GSM48_REJECT_PLMN_NOT_ALLOWED; - *imsi = NULL; - - if (parsed->gsm_type != BSS_MAP_MSG_COMPLETE_LAYER_3) { - LOGP(DNAT, LOGL_ERROR, - "Rejecting CR message due wrong GSM Type %d\n", parsed->gsm_type); - return -1; - } - - /* the parsed has had some basic l3 length check */ - len = msg->l3h[1]; - if (msgb_l3len(msg) - 3 < len) { - LOGP(DNAT, LOGL_ERROR, - "The CR Data has not enough space...\n"); - return -1; - } - - msg->l4h = &msg->l3h[3]; - len -= 1; - - tlv_parse(&tp, gsm0808_att_tlvdef(), msg->l4h, len, 0, 0); - - if (!TLVP_PRESENT(&tp, GSM0808_IE_LAYER_3_INFORMATION)) { - LOGP(DNAT, LOGL_ERROR, "CR Data does not contain layer3 information.\n"); - return -1; - } - - hdr48_len = TLVP_LEN(&tp, GSM0808_IE_LAYER_3_INFORMATION); - - if (hdr48_len < sizeof(*hdr48)) { - LOGP(DNAT, LOGL_ERROR, "GSM48 header does not fit.\n"); - return -1; - } - - hdr48 = (struct gsm48_hdr *) TLVP_VAL(&tp, GSM0808_IE_LAYER_3_INFORMATION); - req.ctx = bsc; - req.black_list = &bsc->nat->imsi_black_list; - req.access_lists = &bsc->nat->access_lists; - req.local_lst_name = bsc->cfg->acc_lst_name; - req.global_lst_name = bsc->nat->acc_lst_name; - req.bsc_nr = bsc->cfg->nr; - return bsc_msg_filter_initial(hdr48, hdr48_len, &req, con_type, imsi, cause); -} - -int bsc_nat_filter_dt(struct bsc_connection *bsc, struct msgb *msg, - struct nat_sccp_connection *con, struct bsc_nat_parsed *parsed, - struct bsc_filter_reject_cause *cause) -{ - uint32_t len; - struct gsm48_hdr *hdr48; - struct bsc_filter_request req; - - cause->cm_reject_cause = GSM48_REJECT_PLMN_NOT_ALLOWED; - cause->lu_reject_cause = GSM48_REJECT_PLMN_NOT_ALLOWED; - - if (con->filter_state.imsi_checked) - return 0; - - /* only care about DTAP messages */ - if (parsed->bssap != BSSAP_MSG_DTAP) - return 0; - - hdr48 = bsc_unpack_dtap(parsed, msg, &len); - if (!hdr48) - return -1; - - req.ctx = con; - req.black_list = &bsc->nat->imsi_black_list; - req.access_lists = &bsc->nat->access_lists; - req.local_lst_name = bsc->cfg->acc_lst_name; - req.global_lst_name = bsc->nat->acc_lst_name; - req.bsc_nr = bsc->cfg->nr; - return bsc_msg_filter_data(hdr48, len, &req, &con->filter_state, cause); -} diff --git a/openbsc/src/osmo-bsc_nat/bsc_nat_rewrite.c b/openbsc/src/osmo-bsc_nat/bsc_nat_rewrite.c deleted file mode 100644 index e7c387c2..00000000 --- a/openbsc/src/osmo-bsc_nat/bsc_nat_rewrite.c +++ /dev/null @@ -1,714 +0,0 @@ -/* - * Message rewriting functionality - */ -/* - * (C) 2010-2013 by Holger Hans Peter Freyther - * (C) 2010-2013 by On-Waves - * All Rights Reserved - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation; either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - * - */ - -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include - -#include -#include - -#include - -static char *trie_lookup(struct nat_rewrite *trie, const char *number, - regoff_t off, void *ctx) -{ - struct nat_rewrite_rule *rule; - - if (!trie) { - LOGP(DCC, LOGL_ERROR, - "Asked to do a table lookup but no table.\n"); - return NULL; - } - - rule = nat_rewrite_lookup(trie, number); - if (!rule) { - LOGP(DCC, LOGL_DEBUG, - "Couldn't find a prefix rule for %s\n", number); - return NULL; - } - - return talloc_asprintf(ctx, "%s%s", rule->rewrite, &number[off]); -} - -static char *match_and_rewrite_number(void *ctx, const char *number, - const char *imsi, struct llist_head *list, - struct nat_rewrite *trie) -{ - struct bsc_nat_num_rewr_entry *entry; - char *new_number = NULL; - - /* need to find a replacement and then fix it */ - llist_for_each_entry(entry, list, list) { - regmatch_t matches[2]; - - /* check the IMSI match */ - if (regexec(&entry->msisdn_reg, imsi, 0, NULL, 0) != 0) - continue; - - /* this regexp matches... */ - if (regexec(&entry->num_reg, number, 2, matches, 0) == 0 - && matches[1].rm_eo != -1) { - if (entry->is_prefix_lookup) - new_number = trie_lookup(trie, number, - matches[1].rm_so, ctx); - else - new_number = talloc_asprintf(ctx, "%s%s", - entry->replace, - &number[matches[1].rm_so]); - } - - if (new_number) - break; - } - - return new_number; -} - -static char *rewrite_isdn_number(struct bsc_nat *nat, struct llist_head *rewr_list, - void *ctx, const char *imsi, - struct gsm_mncc_number *called) -{ - char int_number[sizeof(called->number) + 2]; - char *number = called->number; - - if (llist_empty(&nat->num_rewr)) { - LOGP(DCC, LOGL_DEBUG, "Rewrite rules empty.\n"); - return NULL; - } - - /* only ISDN plan */ - if (called->plan != 1) { - LOGP(DCC, LOGL_DEBUG, "Called plan is not 1 it was %d\n", - called->plan); - return NULL; - } - - /* international, prepend */ - if (called->type == 1) { - int_number[0] = '+'; - memcpy(&int_number[1], number, strlen(number) + 1); - number = int_number; - } - - return match_and_rewrite_number(ctx, number, - imsi, rewr_list, nat->num_rewr_trie); -} - -static void update_called_number(struct gsm_mncc_number *called, - const char *chosen_number) -{ - if (strncmp(chosen_number, "00", 2) == 0) { - called->type = 1; - osmo_strlcpy(called->number, chosen_number + 2, - sizeof(called->number)); - } else { - /* rewrite international to unknown */ - if (called->type == 1) - called->type = 0; - osmo_strlcpy(called->number, chosen_number, - sizeof(called->number)); - } -} - -/** - * Rewrite non global numbers... according to rules based on the IMSI - */ -static struct msgb *rewrite_setup(struct bsc_nat *nat, struct msgb *msg, - struct bsc_nat_parsed *parsed, const char *imsi, - struct gsm48_hdr *hdr48, const uint32_t len) -{ - struct tlv_parsed tp; - unsigned int payload_len; - struct gsm_mncc_number called; - struct msgb *out; - char *new_number_pre = NULL, *new_number_post = NULL, *chosen_number; - uint8_t *outptr; - const uint8_t *msgptr; - int sec_len; - - /* decode and rewrite the message */ - payload_len = len - sizeof(*hdr48); - tlv_parse(&tp, &gsm48_att_tlvdef, hdr48->data, payload_len, 0, 0); - - /* no number, well let us ignore it */ - if (!TLVP_PRESENT(&tp, GSM48_IE_CALLED_BCD)) - return NULL; - - memset(&called, 0, sizeof(called)); - gsm48_decode_called(&called, - TLVP_VAL(&tp, GSM48_IE_CALLED_BCD) - 1); - - /* check if it looks international and stop */ - LOGP(DCC, LOGL_DEBUG, - "Pre-Rewrite for IMSI(%s) Plan(%d) Type(%d) Number(%s)\n", - imsi, called.plan, called.type, called.number); - new_number_pre = rewrite_isdn_number(nat, &nat->num_rewr, msg, imsi, &called); - - if (!new_number_pre) { - LOGP(DCC, LOGL_DEBUG, "No IMSI(%s) match found, returning message.\n", - imsi); - return NULL; - } - - if (strlen(new_number_pre) > sizeof(called.number)) { - LOGP(DCC, LOGL_ERROR, "Number %s is too long for structure.\n", - new_number_pre); - talloc_free(new_number_pre); - return NULL; - } - update_called_number(&called, new_number_pre); - - /* another run through the re-write engine with other rules */ - LOGP(DCC, LOGL_DEBUG, - "Post-Rewrite for IMSI(%s) Plan(%d) Type(%d) Number(%s)\n", - imsi, called.plan, called.type, called.number); - new_number_post = rewrite_isdn_number(nat, &nat->num_rewr_post, msg, - imsi, &called); - chosen_number = new_number_post ? new_number_post : new_number_pre; - - - if (strlen(chosen_number) > sizeof(called.number)) { - LOGP(DCC, LOGL_ERROR, "Number %s is too long for structure.\n", - chosen_number); - talloc_free(new_number_pre); - talloc_free(new_number_post); - return NULL; - } - - /* - * Need to create a new message now based on the old onew - * with a new number. We can sadly not patch this in place - * so we will need to regenerate it. - */ - - out = msgb_alloc_headroom(4096, 128, "changed-setup"); - if (!out) { - LOGP(DCC, LOGL_ERROR, "Failed to allocate.\n"); - talloc_free(new_number_pre); - talloc_free(new_number_post); - return NULL; - } - - /* copy the header */ - outptr = msgb_put(out, sizeof(*hdr48)); - memcpy(outptr, hdr48, sizeof(*hdr48)); - - /* copy everything up to the number */ - sec_len = TLVP_VAL(&tp, GSM48_IE_CALLED_BCD) - 2 - &hdr48->data[0]; - outptr = msgb_put(out, sec_len); - memcpy(outptr, &hdr48->data[0], sec_len); - - /* create the new number */ - update_called_number(&called, chosen_number); - LOGP(DCC, LOGL_DEBUG, - "Chosen number for IMSI(%s) is Plan(%d) Type(%d) Number(%s)\n", - imsi, called.plan, called.type, called.number); - gsm48_encode_called(out, &called); - - /* copy thre rest */ - msgptr = TLVP_VAL(&tp, GSM48_IE_CALLED_BCD) + - TLVP_LEN(&tp, GSM48_IE_CALLED_BCD); - sec_len = payload_len - (msgptr - &hdr48->data[0]); - outptr = msgb_put(out, sec_len); - memcpy(outptr, msgptr, sec_len); - - talloc_free(new_number_pre); - talloc_free(new_number_post); - return out; -} - -/** - * Find a new SMSC address, returns an allocated string that needs to be - * freed or is NULL. - */ -static char *find_new_smsc(struct bsc_nat *nat, void *ctx, const char *imsi, - const char *smsc_addr, const char *dest_nr) -{ - struct bsc_nat_num_rewr_entry *entry; - char *new_number = NULL; - uint8_t dest_match = llist_empty(&nat->tpdest_match); - - /* We will find a new number now */ - llist_for_each_entry(entry, &nat->smsc_rewr, list) { - regmatch_t matches[2]; - - /* check the IMSI match */ - if (regexec(&entry->msisdn_reg, imsi, 0, NULL, 0) != 0) - continue; - - /* this regexp matches... */ - if (regexec(&entry->num_reg, smsc_addr, 2, matches, 0) == 0 && - matches[1].rm_eo != -1) - new_number = talloc_asprintf(ctx, "%s%s", - entry->replace, - &smsc_addr[matches[1].rm_so]); - if (new_number) - break; - } - - if (!new_number) - return NULL; - - /* - * now match the number against another list - */ - llist_for_each_entry(entry, &nat->tpdest_match, list) { - /* check the IMSI match */ - if (regexec(&entry->msisdn_reg, imsi, 0, NULL, 0) != 0) - continue; - - if (regexec(&entry->num_reg, dest_nr, 0, NULL, 0) == 0) { - dest_match = 1; - break; - } - } - - if (!dest_match) { - talloc_free(new_number); - return NULL; - } - - return new_number; -} - -/** - * Clear the TP-SRR from the TPDU header - */ -static uint8_t sms_new_tpdu_hdr(struct bsc_nat *nat, const char *imsi, - const char *dest_nr, uint8_t hdr) -{ - struct bsc_nat_num_rewr_entry *entry; - - /* We will find a new number now */ - llist_for_each_entry(entry, &nat->sms_clear_tp_srr, list) { - /* check the IMSI match */ - if (regexec(&entry->msisdn_reg, imsi, 0, NULL, 0) != 0) - continue; - if (regexec(&entry->num_reg, dest_nr, 0, NULL, 0) != 0) - continue; - - /* matched phone number and imsi */ - return hdr & ~0x20; - } - - return hdr; -} - -/** - * Check if we need to rewrite the number. For this SMS. - */ -static char *sms_new_dest_nr(struct bsc_nat *nat, void *ctx, - const char *imsi, const char *dest_nr) -{ - return match_and_rewrite_number(ctx, dest_nr, imsi, - &nat->sms_num_rewr, NULL); -} - -/** - * This is a helper for GSM 04.11 8.2.5.2 Destination address element - */ -void sms_encode_addr_element(struct msgb *out, const char *new_number, - int format, int tp_data) -{ - uint8_t new_addr_len; - uint8_t new_addr[26]; - - /* - * Copy the new number. We let libosmocore encode it, then set - * the extension followed after the length. Depending on if - * we want to write RP we will let the TLV code add the - * length for us or we need to use strlen... This is not very clear - * as of 03.40 and 04.11. - */ - new_addr_len = gsm48_encode_bcd_number(new_addr, ARRAY_SIZE(new_addr), - 1, new_number); - new_addr[1] = format; - if (tp_data) { - uint8_t *data = msgb_put(out, new_addr_len); - memcpy(data, new_addr, new_addr_len); - data[0] = strlen(new_number); - } else { - msgb_lv_put(out, new_addr_len - 1, new_addr + 1); - } -} - -static struct msgb *sms_create_new(uint8_t type, uint8_t ref, - struct gsm48_hdr *old_hdr48, - const uint8_t *orig_addr_ptr, - int orig_addr_len, const char *new_number, - const uint8_t *data_ptr, int data_len, - uint8_t tpdu_first_byte, - const int old_dest_len, const char *new_dest_nr) -{ - struct gsm48_hdr *new_hdr48; - struct msgb *out; - - /* - * We need to re-create the patched structure. This is why we have - * saved the above pointers. - */ - out = msgb_alloc_headroom(4096, 128, "changed-smsc"); - if (!out) { - LOGP(DNAT, LOGL_ERROR, "Failed to allocate.\n"); - return NULL; - } - - out->l2h = out->data; - msgb_v_put(out, GSM411_MT_RP_DATA_MO); - msgb_v_put(out, ref); - msgb_lv_put(out, orig_addr_len, orig_addr_ptr); - - sms_encode_addr_element(out, new_number, 0x91, 0); - - - /* Patch the TPDU from here on */ - - /** - * Do we need to put a new TP-Destination-Address (TP-DA) here or - * can we copy the old thing? For the TP-DA we need to find out the - * new size. - */ - if (new_dest_nr) { - uint8_t *data, *new_size; - - /* reserve the size and write the header */ - new_size = msgb_put(out, 1); - out->l3h = new_size + 1; - msgb_v_put(out, tpdu_first_byte); - msgb_v_put(out, data_ptr[1]); - - /* encode the new number and put it */ - if (strncmp(new_dest_nr, "00", 2) == 0) - sms_encode_addr_element(out, new_dest_nr + 2, 0x91, 1); - else - sms_encode_addr_element(out, new_dest_nr, 0x81, 1); - - /* Copy the rest after the TP-DS */ - data = msgb_put(out, data_len - 2 - 1 - old_dest_len); - memcpy(data, &data_ptr[2 + 1 + old_dest_len], data_len - 2 - 1 - old_dest_len); - - /* fill in the new size */ - new_size[0] = msgb_l3len(out); - } else { - msgb_v_put(out, data_len); - msgb_tv_fixed_put(out, tpdu_first_byte, data_len - 1, &data_ptr[1]); - } - - /* prepend GSM 04.08 header */ - new_hdr48 = (struct gsm48_hdr *) msgb_push(out, sizeof(*new_hdr48) + 1); - memcpy(new_hdr48, old_hdr48, sizeof(*old_hdr48)); - new_hdr48->data[0] = msgb_l2len(out); - - return out; -} - -/** - * Parse the SMS and check if it needs to be rewritten - */ -static struct msgb *rewrite_sms(struct bsc_nat *nat, struct msgb *msg, - struct bsc_nat_parsed *parsed, const char *imsi, - struct gsm48_hdr *hdr48, const uint32_t len) -{ - unsigned int payload_len; - unsigned int cp_len; - - uint8_t ref; - uint8_t orig_addr_len, *orig_addr_ptr; - uint8_t dest_addr_len, *dest_addr_ptr; - uint8_t data_len, *data_ptr; - char smsc_addr[30]; - - - uint8_t dest_len, orig_dest_len; - char _dest_nr[30]; - char *dest_nr; - char *new_dest_nr; - - char *new_number = NULL; - uint8_t tpdu_hdr; - struct msgb *out; - - payload_len = len - sizeof(*hdr48); - if (payload_len < 1) { - LOGP(DNAT, LOGL_ERROR, "SMS too short for things. %d\n", payload_len); - return NULL; - } - - cp_len = hdr48->data[0]; - if (payload_len + 1 < cp_len) { - LOGP(DNAT, LOGL_ERROR, "SMS RPDU can not fit in: %d %d\n", cp_len, payload_len); - return NULL; - } - - if (hdr48->data[1] != GSM411_MT_RP_DATA_MO) - return NULL; - - if (cp_len < 5) { - LOGP(DNAT, LOGL_ERROR, "RD-DATA can not fit in the CP len: %d\n", cp_len); - return NULL; - } - - /* RP */ - ref = hdr48->data[2]; - orig_addr_len = hdr48->data[3]; - orig_addr_ptr = &hdr48->data[4]; - - /* the +1 is for checking if the following element has some space */ - if (cp_len < 3 + orig_addr_len + 1) { - LOGP(DNAT, LOGL_ERROR, "RP-Originator addr does not fit: %d\n", orig_addr_len); - return NULL; - } - - dest_addr_len = hdr48->data[3 + orig_addr_len + 1]; - dest_addr_ptr = &hdr48->data[3 + orig_addr_len + 2]; - - if (cp_len < 3 + orig_addr_len + 1 + dest_addr_len + 1) { - LOGP(DNAT, LOGL_ERROR, "RP-Destination addr does not fit: %d\n", dest_addr_len); - return NULL; - } - gsm48_decode_bcd_number(smsc_addr, ARRAY_SIZE(smsc_addr), dest_addr_ptr - 1, 1); - - data_len = hdr48->data[3 + orig_addr_len + 1 + dest_addr_len + 1]; - data_ptr = &hdr48->data[3 + orig_addr_len + 1 + dest_addr_len + 2]; - - if (cp_len < 3 + orig_addr_len + 1 + dest_addr_len + 1 + data_len) { - LOGP(DNAT, LOGL_ERROR, "RP-Data does not fit: %d\n", data_len); - return NULL; - } - - if (data_len < 3) { - LOGP(DNAT, LOGL_ERROR, "SMS-SUBMIT is too short.\n"); - return NULL; - } - - /* TP-PDU starts here */ - if ((data_ptr[0] & 0x03) != GSM340_SMS_SUBMIT_MS2SC) - return NULL; - - /* - * look into the phone number. The length is in semi-octets, we will - * need to add the byte for the number type as well. - */ - orig_dest_len = data_ptr[2]; - dest_len = ((orig_dest_len + 1) / 2) + 1; - if (data_len < dest_len + 3 || dest_len < 2) { - LOGP(DNAT, LOGL_ERROR, "SMS-SUBMIT can not have TP-DestAddr.\n"); - return NULL; - } - - if ((data_ptr[3] & 0x80) == 0) { - LOGP(DNAT, LOGL_ERROR, "TP-DestAddr has extension. Not handled.\n"); - return NULL; - } - - if ((data_ptr[3] & 0x0F) == 0) { - LOGP(DNAT, LOGL_ERROR, "TP-DestAddr is of unknown type.\n"); - return NULL; - } - - /** - * Besides of what I think I read in GSM 03.40 and 04.11 the TP-DA - * contains the semi-octets as length (strlen), change it to the - * the number of bytes, but then change it back. - */ - data_ptr[2] = dest_len; - gsm48_decode_bcd_number(_dest_nr + 2, ARRAY_SIZE(_dest_nr) - 2, - &data_ptr[2], 1); - data_ptr[2] = orig_dest_len; - if ((data_ptr[3] & 0x70) == 0x10) { - _dest_nr[0] = _dest_nr[1] = '0'; - dest_nr = &_dest_nr[0]; - } else { - dest_nr = &_dest_nr[2]; - } - - /** - * Call functions to rewrite the data - */ - tpdu_hdr = sms_new_tpdu_hdr(nat, imsi, dest_nr, data_ptr[0]); - new_number = find_new_smsc(nat, msg, imsi, smsc_addr, dest_nr); - new_dest_nr = sms_new_dest_nr(nat, msg, imsi, dest_nr); - - if (tpdu_hdr == data_ptr[0] && !new_number && !new_dest_nr) - return NULL; - - out = sms_create_new(GSM411_MT_RP_DATA_MO, ref, hdr48, - orig_addr_ptr, orig_addr_len, - new_number ? new_number : smsc_addr, - data_ptr, data_len, tpdu_hdr, - dest_len, new_dest_nr); - talloc_free(new_number); - talloc_free(new_dest_nr); - return out; -} - -struct msgb *bsc_nat_rewrite_msg(struct bsc_nat *nat, struct msgb *msg, struct bsc_nat_parsed *parsed, const char *imsi) -{ - struct gsm48_hdr *hdr48; - uint32_t len; - uint8_t msg_type, proto; - struct msgb *new_msg = NULL, *sccp; - uint8_t link_id; - - if (!imsi || strlen(imsi) < 5) - return msg; - - /* only care about DTAP messages */ - if (parsed->bssap != BSSAP_MSG_DTAP) - return msg; - if (!parsed->dest_local_ref) - return msg; - - hdr48 = bsc_unpack_dtap(parsed, msg, &len); - if (!hdr48) - return msg; - - link_id = msg->l3h[1]; - proto = gsm48_hdr_pdisc(hdr48); - msg_type = gsm48_hdr_msg_type(hdr48); - - if (proto == GSM48_PDISC_CC && msg_type == GSM48_MT_CC_SETUP) - new_msg = rewrite_setup(nat, msg, parsed, imsi, hdr48, len); - else if (proto == GSM48_PDISC_SMS && msg_type == GSM411_MT_CP_DATA) - new_msg = rewrite_sms(nat, msg, parsed, imsi, hdr48, len); - - if (!new_msg) - return msg; - - /* wrap with DTAP, SCCP, then IPA. TODO: Stop copying */ - gsm0808_prepend_dtap_header(new_msg, link_id); - sccp = sccp_create_dt1(parsed->dest_local_ref, new_msg->data, new_msg->len); - talloc_free(new_msg); - - if (!sccp) { - LOGP(DNAT, LOGL_ERROR, "Failed to allocate.\n"); - return msg; - } - - ipa_prepend_header(sccp, IPAC_PROTO_SCCP); - - /* the parsed hangs off from msg but it needs to survive */ - talloc_steal(sccp, parsed); - msgb_free(msg); - return sccp; -} - -static void num_rewr_free_data(struct bsc_nat_num_rewr_entry *entry) -{ - regfree(&entry->msisdn_reg); - regfree(&entry->num_reg); - talloc_free(entry->replace); -} - -void bsc_nat_num_rewr_entry_adapt(void *ctx, struct llist_head *head, - const struct osmo_config_list *list) -{ - struct bsc_nat_num_rewr_entry *entry, *tmp; - struct osmo_config_entry *cfg_entry; - - /* free the old data */ - llist_for_each_entry_safe(entry, tmp, head, list) { - num_rewr_free_data(entry); - llist_del(&entry->list); - talloc_free(entry); - } - - - if (!list) - return; - - llist_for_each_entry(cfg_entry, &list->entry, list) { - char *regexp; - if (cfg_entry->text[0] == '+') { - LOGP(DNAT, LOGL_ERROR, - "Plus is not allowed in the number\n"); - continue; - } - - entry = talloc_zero(ctx, struct bsc_nat_num_rewr_entry); - if (!entry) { - LOGP(DNAT, LOGL_ERROR, - "Allocation of the num_rewr entry failed.\n"); - continue; - } - - entry->replace = talloc_strdup(entry, cfg_entry->text); - if (!entry->replace) { - LOGP(DNAT, LOGL_ERROR, - "Failed to copy the replacement text.\n"); - talloc_free(entry); - continue; - } - - if (strcmp("prefix_lookup", entry->replace) == 0) - entry->is_prefix_lookup = 1; - - /* we will now build a regexp string */ - if (cfg_entry->mcc[0] == '^') { - regexp = talloc_strdup(entry, cfg_entry->mcc); - } else { - regexp = talloc_asprintf(entry, "^%s%s", - cfg_entry->mcc[0] == '*' ? - "[0-9][0-9][0-9]" : cfg_entry->mcc, - cfg_entry->mnc[0] == '*' ? - "[0-9][0-9]" : cfg_entry->mnc); - } - - if (!regexp) { - LOGP(DNAT, LOGL_ERROR, "Failed to create a regexp string.\n"); - talloc_free(entry); - continue; - } - - if (regcomp(&entry->msisdn_reg, regexp, 0) != 0) { - LOGP(DNAT, LOGL_ERROR, - "Failed to compile regexp '%s'\n", regexp); - talloc_free(regexp); - talloc_free(entry); - continue; - } - - talloc_free(regexp); - if (regcomp(&entry->num_reg, cfg_entry->option, REG_EXTENDED) != 0) { - LOGP(DNAT, LOGL_ERROR, - "Failed to compile regexp '%s'\n", cfg_entry->option); - regfree(&entry->msisdn_reg); - talloc_free(entry); - continue; - } - - /* we have copied the number */ - llist_add_tail(&entry->list, head); - } -} diff --git a/openbsc/src/osmo-bsc_nat/bsc_nat_rewrite_trie.c b/openbsc/src/osmo-bsc_nat/bsc_nat_rewrite_trie.c deleted file mode 100644 index 633fa87d..00000000 --- a/openbsc/src/osmo-bsc_nat/bsc_nat_rewrite_trie.c +++ /dev/null @@ -1,259 +0,0 @@ -/* Handling for loading a re-write file/database */ -/* - * (C) 2013 by On-Waves - * (C) 2013 by Holger Hans Peter Freyther - * All Rights Reserved - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation; either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - * - */ - -#include -#include -#include - -#include -#include - -#include -#include -#include -#include - -#define CHECK_IS_DIGIT_OR_FAIL(prefix, pos) \ - if (!isdigit(prefix[pos]) && prefix[pos] != '+') { \ - LOGP(DNAT, LOGL_ERROR, \ - "Prefix(%s) contains non ascii text at(%d=%c)\n", \ - prefix, pos, prefix[pos]); \ - goto fail; \ - } -#define TO_INT(c) \ - ((c) == '+' ? 10 : ((c - '0') % 10)) - -static void insert_rewrite_node(struct nat_rewrite_rule *rule, struct nat_rewrite *root) -{ - struct nat_rewrite_rule *new = &root->rule; - - const int len = strlen(rule->prefix); - int i; - - if (len <= 0) { - LOGP(DNAT, LOGL_ERROR, "An empty prefix does not make sense.\n"); - goto fail; - } - - for (i = 0; i < len - 1; ++i) { - int pos; - - /* check if the input is valid */ - CHECK_IS_DIGIT_OR_FAIL(rule->prefix, i); - - /* check if the next node is already valid */ - pos = TO_INT(rule->prefix[i]); - if (!new->rules[pos]) { - new->rules[pos] = talloc_zero(root, struct nat_rewrite_rule); - if (!new->rules[pos]) { - LOGP(DNAT, LOGL_ERROR, - "Failed to allocate memory.\n"); - goto fail; - } - - new->rules[pos]->empty = 1; - } - - /* we continue here */ - new = new->rules[pos]; - } - - /* new now points to the place where we want to add it */ - int pos; - - /* check if the input is valid */ - CHECK_IS_DIGIT_OR_FAIL(rule->prefix, (len - 1)); - - /* check if the next node is already valid */ - pos = TO_INT(rule->prefix[len - 1]); - if (!new->rules[pos]) - new->rules[pos] = rule; - else if (new->rules[pos]->empty) { - /* copy over entries */ - new->rules[pos]->empty = 0; - memcpy(new->rules[pos]->prefix, rule->prefix, sizeof(rule->prefix)); - memcpy(new->rules[pos]->rewrite, rule->rewrite, sizeof(rule->rewrite)); - talloc_free(rule); - } else { - LOGP(DNAT, LOGL_ERROR, - "Prefix(%s) is already installed\n", rule->prefix); - goto fail; - } - - root->prefixes += 1; - return; - -fail: - talloc_free(rule); - return; -} - -static void handle_line(struct nat_rewrite *rewrite, char *line) -{ - char *split; - struct nat_rewrite_rule *rule; - size_t size_prefix, size_end, len; - - - /* Find the ',' in the line */ - len = strlen(line); - split = strstr(line, ","); - if (!split) { - LOGP(DNAT, LOGL_ERROR, "Line doesn't contain ','\n"); - return; - } - - /* Check if there is space for the rewrite rule */ - size_prefix = split - line; - if (len - size_prefix <= 2) { - LOGP(DNAT, LOGL_ERROR, "No rewrite available.\n"); - return; - } - - /* Continue after the ',' to the end */ - split = &line[size_prefix + 1]; - size_end = strlen(split) - 1; - - /* Check if both strings can fit into the static array */ - if (size_prefix > sizeof(rule->prefix) - 1) { - LOGP(DNAT, LOGL_ERROR, - "Prefix is too long with %zu\n", size_prefix); - return; - } - - if (size_end > sizeof(rule->rewrite) - 1) { - LOGP(DNAT, LOGL_ERROR, - "Rewrite is too long with %zu on %s\n", - size_end, &line[size_prefix + 1]); - return; - } - - /* Now create the entry and insert it into the trie */ - rule = talloc_zero(rewrite, struct nat_rewrite_rule); - if (!rule) { - LOGP(DNAT, LOGL_ERROR, "Can not allocate memory\n"); - return; - } - - memcpy(rule->prefix, line, size_prefix); - assert(size_prefix < sizeof(rule->prefix)); - rule->prefix[size_prefix] = '\0'; - - memcpy(rule->rewrite, split, size_end); - assert(size_end < sizeof(rule->rewrite)); - rule->rewrite[size_end] = '\0'; - - /* now insert and balance the tree */ - insert_rewrite_node(rule, rewrite); -} - -struct nat_rewrite *nat_rewrite_parse(void *ctx, const char *filename) -{ - FILE *file; - char *line = NULL; - size_t n = 0; - struct nat_rewrite *res; - - file = fopen(filename, "r"); - if (!file) - return NULL; - - res = talloc_zero(ctx, struct nat_rewrite); - if (!res) { - fclose(file); - return NULL; - } - - /* mark the root as empty */ - res->rule.empty = 1; - - while (getline(&line, &n, file) != -1) { - handle_line(res, line); - } - - free(line); - fclose(file); - return res; -} - -/** - * Simple find that tries to do a longest match... - */ -struct nat_rewrite_rule *nat_rewrite_lookup(struct nat_rewrite *rewrite, - const char *prefix) -{ - struct nat_rewrite_rule *rule = &rewrite->rule; - struct nat_rewrite_rule *last = NULL; - const int len = OSMO_MIN(strlen(prefix), (sizeof(rule->prefix) - 1)); - int i; - - for (i = 0; rule && i < len; ++i) { - int pos; - - CHECK_IS_DIGIT_OR_FAIL(prefix, i); - pos = TO_INT(prefix[i]); - - rule = rule->rules[pos]; - if (rule && !rule->empty) - last = rule; - } - - return last; - -fail: - return NULL; -} - -static void nat_rewrite_dump_rec(struct nat_rewrite_rule *rule) -{ - int i; - if (!rule->empty) - printf("%s,%s\n", rule->prefix, rule->rewrite); - - for (i = 0; i < ARRAY_SIZE(rule->rules); ++i) { - if (!rule->rules[i]) - continue; - nat_rewrite_dump_rec(rule->rules[i]); - } -} - -void nat_rewrite_dump(struct nat_rewrite *rewrite) -{ - nat_rewrite_dump_rec(&rewrite->rule); -} - -static void nat_rewrite_dump_rec_vty(struct vty *vty, struct nat_rewrite_rule *rule) -{ - int i; - if (!rule->empty) - vty_out(vty, "%s,%s%s", rule->prefix, rule->rewrite, VTY_NEWLINE); - - for (i = 0; i < ARRAY_SIZE(rule->rules); ++i) { - if (!rule->rules[i]) - continue; - nat_rewrite_dump_rec_vty(vty, rule->rules[i]); - } -} - -void nat_rewrite_dump_vty(struct vty *vty, struct nat_rewrite *rewrite) -{ - nat_rewrite_dump_rec_vty(vty, &rewrite->rule); -} diff --git a/openbsc/src/osmo-bsc_nat/bsc_nat_utils.c b/openbsc/src/osmo-bsc_nat/bsc_nat_utils.c deleted file mode 100644 index c12b29f1..00000000 --- a/openbsc/src/osmo-bsc_nat/bsc_nat_utils.c +++ /dev/null @@ -1,535 +0,0 @@ - -/* BSC Multiplexer/NAT Utilities */ - -/* - * (C) 2010-2011 by Holger Hans Peter Freyther - * (C) 2010-2011 by On-Waves - * All Rights Reserved - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation; either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - * - */ - -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include - -#include -#include - -#include - -#include -#include -#include - -static const struct rate_ctr_desc bsc_cfg_ctr_description[] = { - [BCFG_CTR_SCCP_CONN] = { "sccp.conn", "SCCP Connections "}, - [BCFG_CTR_SCCP_CALLS] = { "sccp.calls", "SCCP Assignment Commands "}, - [BCFG_CTR_NET_RECONN] = { "net.reconnects", "Network reconnects "}, - [BCFG_CTR_DROPPED_SCCP] = { "dropped.sccp", "Dropped SCCP connections."}, - [BCFG_CTR_DROPPED_CALLS] = { "dropped.calls", "Dropped active calls. "}, - [BCFG_CTR_REJECTED_CR] = { "rejected.cr", "Rejected CR due filter "}, - [BCFG_CTR_REJECTED_MSG] = { "rejected.msg", "Rejected MSG due filter "}, - [BCFG_CTR_ILL_PACKET] = { "rejected.ill", "Rejected due parse error "}, - [BCFG_CTR_CON_TYPE_LU] = { "conn.lu", "Conn Location Update "}, - [BCFG_CTR_CON_CMSERV_RQ] = { "conn.rq", "Conn CM Service Req "}, - [BCFG_CTR_CON_PAG_RESP] = { "conn.pag", "Conn Paging Response "}, - [BCFG_CTR_CON_SSA] = { "conn.ssa", "Conn USSD "}, - [BCFG_CTR_CON_OTHER] = { "conn.other", "Conn Other "}, -}; - -static const struct rate_ctr_group_desc bsc_cfg_ctrg_desc = { - .group_name_prefix = "nat.bsc", - .group_description = "NAT BSC Statistics", - .num_ctr = ARRAY_SIZE(bsc_cfg_ctr_description), - .ctr_desc = bsc_cfg_ctr_description, - .class_id = OSMO_STATS_CLASS_PEER, -}; - -struct bsc_nat *bsc_nat_alloc(void) -{ - struct bsc_nat *nat = talloc_zero(tall_bsc_ctx, struct bsc_nat); - if (!nat) - return NULL; - - nat->main_dest = talloc_zero(nat, struct bsc_msc_dest); - if (!nat->main_dest) { - talloc_free(nat); - return NULL; - } - - INIT_LLIST_HEAD(&nat->sccp_connections); - INIT_LLIST_HEAD(&nat->bsc_connections); - INIT_LLIST_HEAD(&nat->paging_groups); - INIT_LLIST_HEAD(&nat->bsc_configs); - INIT_LLIST_HEAD(&nat->access_lists); - INIT_LLIST_HEAD(&nat->dests); - INIT_LLIST_HEAD(&nat->num_rewr); - INIT_LLIST_HEAD(&nat->num_rewr_post); - INIT_LLIST_HEAD(&nat->smsc_rewr); - INIT_LLIST_HEAD(&nat->tpdest_match); - INIT_LLIST_HEAD(&nat->sms_clear_tp_srr); - INIT_LLIST_HEAD(&nat->sms_num_rewr); - - nat->stats.sccp.conn = osmo_counter_alloc("nat.sccp.conn"); - nat->stats.sccp.calls = osmo_counter_alloc("nat.sccp.calls"); - nat->stats.bsc.reconn = osmo_counter_alloc("nat.bsc.conn"); - nat->stats.bsc.auth_fail = osmo_counter_alloc("nat.bsc.auth_fail"); - nat->stats.msc.reconn = osmo_counter_alloc("nat.msc.conn"); - nat->stats.ussd.reconn = osmo_counter_alloc("nat.ussd.conn"); - nat->auth_timeout = 2; - nat->ping_timeout = 20; - nat->pong_timeout = 5; - - llist_add(&nat->main_dest->list, &nat->dests); - nat->main_dest->ip = talloc_strdup(nat, "127.0.0.1"); - nat->main_dest->port = 5000; - - return nat; -} - -void bsc_nat_free(struct bsc_nat *nat) -{ - struct bsc_config *cfg, *tmp; - struct bsc_msg_acc_lst *lst, *tmp_lst; - - llist_for_each_entry_safe(cfg, tmp, &nat->bsc_configs, entry) - bsc_config_free(cfg); - llist_for_each_entry_safe(lst, tmp_lst, &nat->access_lists, list) - bsc_msg_acc_lst_delete(lst); - - bsc_nat_num_rewr_entry_adapt(nat, &nat->num_rewr, NULL); - bsc_nat_num_rewr_entry_adapt(nat, &nat->num_rewr_post, NULL); - bsc_nat_num_rewr_entry_adapt(nat, &nat->sms_clear_tp_srr, NULL); - bsc_nat_num_rewr_entry_adapt(nat, &nat->sms_num_rewr, NULL); - bsc_nat_num_rewr_entry_adapt(nat, &nat->tpdest_match, NULL); - - osmo_counter_free(nat->stats.sccp.conn); - osmo_counter_free(nat->stats.sccp.calls); - osmo_counter_free(nat->stats.bsc.reconn); - osmo_counter_free(nat->stats.bsc.auth_fail); - osmo_counter_free(nat->stats.msc.reconn); - osmo_counter_free(nat->stats.ussd.reconn); - talloc_free(nat->mgcp_cfg); - talloc_free(nat); -} - -void bsc_nat_set_msc_ip(struct bsc_nat *nat, const char *ip) -{ - osmo_talloc_replace_string(nat, &nat->main_dest->ip, ip); -} - -struct bsc_connection *bsc_connection_alloc(struct bsc_nat *nat) -{ - struct bsc_connection *con = talloc_zero(nat, struct bsc_connection); - if (!con) - return NULL; - - con->nat = nat; - osmo_wqueue_init(&con->write_queue, 100); - INIT_LLIST_HEAD(&con->cmd_pending); - INIT_LLIST_HEAD(&con->pending_dlcx); - return con; -} - -struct bsc_config *bsc_config_alloc(struct bsc_nat *nat, const char *token, - unsigned int number) -{ - struct bsc_config *conf = talloc_zero(nat, struct bsc_config); - if (!conf) - return NULL; - - conf->token = talloc_strdup(conf, token); - conf->nr = number; - conf->nat = nat; - conf->max_endpoints = 32; - conf->paging_group = PAGIN_GROUP_UNASSIGNED; - - INIT_LLIST_HEAD(&conf->lac_list); - - llist_add_tail(&conf->entry, &nat->bsc_configs); - ++nat->num_bsc; - - conf->stats.ctrg = rate_ctr_group_alloc(conf, &bsc_cfg_ctrg_desc, conf->nr); - if (!conf->stats.ctrg) { - llist_del(&conf->entry); - talloc_free(conf); - return NULL; - } - - return conf; -} - -struct bsc_config *bsc_config_by_token(struct bsc_nat *nat, const char *token, int len) -{ - struct bsc_config *conf; - - llist_for_each_entry(conf, &nat->bsc_configs, entry) { - /* - * Add the '\0' of the token for the memcmp, the IPA messages - * for some reason added null termination. - */ - const int token_len = strlen(conf->token) + 1; - - if (token_len == len && memcmp(conf->token, token, token_len) == 0) - return conf; - } - - return NULL; -} - -void bsc_config_free(struct bsc_config *cfg) -{ - llist_del(&cfg->entry); - rate_ctr_group_free(cfg->stats.ctrg); - cfg->nat->num_bsc--; - OSMO_ASSERT(cfg->nat->num_bsc >= 0) - talloc_free(cfg); -} - -static void _add_lac(void *ctx, struct llist_head *list, int _lac) -{ - struct bsc_lac_entry *lac; - - llist_for_each_entry(lac, list, entry) - if (lac->lac == _lac) - return; - - lac = talloc_zero(ctx, struct bsc_lac_entry); - if (!lac) { - LOGP(DNAT, LOGL_ERROR, "Failed to allocate.\n"); - return; - } - - lac->lac = _lac; - llist_add_tail(&lac->entry, list); -} - -static void _del_lac(struct llist_head *list, int _lac) -{ - struct bsc_lac_entry *lac; - - llist_for_each_entry(lac, list, entry) - if (lac->lac == _lac) { - llist_del(&lac->entry); - talloc_free(lac); - return; - } -} - -void bsc_config_add_lac(struct bsc_config *cfg, int _lac) -{ - _add_lac(cfg, &cfg->lac_list, _lac); -} - -void bsc_config_del_lac(struct bsc_config *cfg, int _lac) -{ - _del_lac(&cfg->lac_list, _lac); -} - -struct bsc_nat_paging_group *bsc_nat_paging_group_create(struct bsc_nat *nat, int group) -{ - struct bsc_nat_paging_group *pgroup; - - pgroup = talloc_zero(nat, struct bsc_nat_paging_group); - if (!pgroup) { - LOGP(DNAT, LOGL_ERROR, "Failed to allocate a paging group.\n"); - return NULL; - } - - pgroup->nr = group; - INIT_LLIST_HEAD(&pgroup->lists); - llist_add_tail(&pgroup->entry, &nat->paging_groups); - return pgroup; -} - -void bsc_nat_paging_group_delete(struct bsc_nat_paging_group *pgroup) -{ - llist_del(&pgroup->entry); - talloc_free(pgroup); -} - -struct bsc_nat_paging_group *bsc_nat_paging_group_num(struct bsc_nat *nat, int group) -{ - struct bsc_nat_paging_group *pgroup; - - llist_for_each_entry(pgroup, &nat->paging_groups, entry) - if (pgroup->nr == group) - return pgroup; - - return NULL; -} - -void bsc_nat_paging_group_add_lac(struct bsc_nat_paging_group *pgroup, int lac) -{ - _add_lac(pgroup, &pgroup->lists, lac); -} - -void bsc_nat_paging_group_del_lac(struct bsc_nat_paging_group *pgroup, int lac) -{ - _del_lac(&pgroup->lists, lac); -} - -int bsc_config_handles_lac(struct bsc_config *cfg, int lac_nr) -{ - struct bsc_nat_paging_group *pgroup; - struct bsc_lac_entry *entry; - - llist_for_each_entry(entry, &cfg->lac_list, entry) - if (entry->lac == lac_nr) - return 1; - - /* now lookup the paging group */ - pgroup = bsc_nat_paging_group_num(cfg->nat, cfg->paging_group); - if (!pgroup) - return 0; - - llist_for_each_entry(entry, &pgroup->lists, entry) - if (entry->lac == lac_nr) - return 1; - - return 0; -} - -void sccp_connection_destroy(struct nat_sccp_connection *conn) -{ - LOGP(DNAT, LOGL_DEBUG, "Destroy 0x%x <-> 0x%x mapping for con %p\n", - sccp_src_ref_to_int(&conn->real_ref), - sccp_src_ref_to_int(&conn->patched_ref), conn->bsc); - bsc_mgcp_dlcx(conn); - llist_del(&conn->list_entry); - talloc_free(conn); -} - - -int bsc_nat_find_paging(struct msgb *msg, - const uint8_t **out_data, int *out_leng) -{ - int data_length; - const uint8_t *data; - struct tlv_parsed tp; - - if (!msg->l3h || msgb_l3len(msg) < 3) { - LOGP(DNAT, LOGL_ERROR, "Paging message is too short.\n"); - return -1; - } - - tlv_parse(&tp, gsm0808_att_tlvdef(), msg->l3h + 3, msgb_l3len(msg) - 3, 0, 0); - if (!TLVP_PRESENT(&tp, GSM0808_IE_CELL_IDENTIFIER_LIST)) { - LOGP(DNAT, LOGL_ERROR, "No CellIdentifier List inside paging msg.\n"); - return -2; - } - - data_length = TLVP_LEN(&tp, GSM0808_IE_CELL_IDENTIFIER_LIST); - data = TLVP_VAL(&tp, GSM0808_IE_CELL_IDENTIFIER_LIST); - - /* No need to try a different BSS */ - if (data[0] == CELL_IDENT_BSS) { - return -3; - } else if (data[0] != CELL_IDENT_LAC) { - LOGP(DNAT, LOGL_ERROR, "Unhandled cell ident discrminator: %d\n", data[0]); - return -4; - } - - *out_data = &data[1]; - *out_leng = data_length - 1; - return 0; -} - -int bsc_write_mgcp(struct bsc_connection *bsc, const uint8_t *data, unsigned int length) -{ - struct msgb *msg; - - if (length > 4096 - 128) { - LOGP(DLINP, LOGL_ERROR, "Can not send message of that size.\n"); - return -1; - } - - msg = msgb_alloc_headroom(4096, 128, "to-bsc"); - if (!msg) { - LOGP(DLINP, LOGL_ERROR, "Failed to allocate memory for BSC msg.\n"); - return -1; - } - - /* copy the data */ - msg->l3h = msgb_put(msg, length); - memcpy(msg->l3h, data, length); - - return bsc_write(bsc, msg, IPAC_PROTO_MGCP_OLD); -} - -int bsc_write(struct bsc_connection *bsc, struct msgb *msg, int proto) -{ - return bsc_do_write(&bsc->write_queue, msg, proto); -} - -int bsc_do_write(struct osmo_wqueue *queue, struct msgb *msg, int proto) -{ - /* prepend the header */ - ipa_prepend_header(msg, proto); - return bsc_write_msg(queue, msg); -} - -int bsc_write_msg(struct osmo_wqueue *queue, struct msgb *msg) -{ - if (osmo_wqueue_enqueue(queue, msg) != 0) { - LOGP(DLINP, LOGL_ERROR, "Failed to enqueue the write.\n"); - msgb_free(msg); - return -1; - } - - return 0; -} - -struct gsm48_hdr *bsc_unpack_dtap(struct bsc_nat_parsed *parsed, - struct msgb *msg, uint32_t *len) -{ - /* gsm_type is actually the size of the dtap */ - *len = parsed->gsm_type; - if (*len < msgb_l3len(msg) - 3) { - LOGP(DNAT, LOGL_ERROR, "Not enough space for DTAP.\n"); - return NULL; - } - - if (msgb_l3len(msg) - 3 < msg->l3h[2]) { - LOGP(DNAT, LOGL_ERROR, - "GSM48 payload does not fit: %d %d\n", - msg->l3h[2], msgb_l3len(msg) - 3); - return NULL; - } - - msg->l4h = &msg->l3h[3]; - return (struct gsm48_hdr *) msg->l4h; -} - -static const char *con_types [] = { - [FLT_CON_TYPE_NONE] = "n/a", - [FLT_CON_TYPE_LU] = "Location Update", - [FLT_CON_TYPE_CM_SERV_REQ] = "CM Serv Req", - [FLT_CON_TYPE_PAG_RESP] = "Paging Response", - [FLT_CON_TYPE_SSA] = "Supplementar Service Activation", - [FLT_CON_TYPE_LOCAL_REJECT] = "Local Reject", - [FLT_CON_TYPE_OTHER] = "Other", -}; - -const char *bsc_con_type_to_string(int type) -{ - return con_types[type]; -} - -int bsc_nat_msc_is_connected(struct bsc_nat *nat) -{ - return nat->msc_con->is_connected; -} - -static const int con_to_ctr[] = { - [FLT_CON_TYPE_NONE] = -1, - [FLT_CON_TYPE_LU] = BCFG_CTR_CON_TYPE_LU, - [FLT_CON_TYPE_CM_SERV_REQ] = BCFG_CTR_CON_CMSERV_RQ, - [FLT_CON_TYPE_PAG_RESP] = BCFG_CTR_CON_PAG_RESP, - [FLT_CON_TYPE_SSA] = BCFG_CTR_CON_SSA, - [FLT_CON_TYPE_LOCAL_REJECT] = -1, - [FLT_CON_TYPE_OTHER] = BCFG_CTR_CON_OTHER, -}; - -int bsc_conn_type_to_ctr(struct nat_sccp_connection *conn) -{ - return con_to_ctr[conn->filter_state.con_type]; -} - -int bsc_write_cb(struct osmo_fd *bfd, struct msgb *msg) -{ - int rc; - - rc = write(bfd->fd, msg->data, msg->len); - if (rc != msg->len) - LOGP(DNAT, LOGL_ERROR, "Failed to write message to the BSC.\n"); - - return rc; -} - -static void extract_lac(const uint8_t *data, uint16_t *lac, uint16_t *ci) -{ - memcpy(lac, &data[0], sizeof(*lac)); - memcpy(ci, &data[2], sizeof(*ci)); - - *lac = ntohs(*lac); - *ci = ntohs(*ci); -} - -int bsc_nat_extract_lac(struct bsc_connection *bsc, - struct nat_sccp_connection *con, - struct bsc_nat_parsed *parsed, struct msgb *msg) -{ - int data_length; - const uint8_t *data; - struct tlv_parsed tp; - uint16_t lac, ci; - - if (parsed->gsm_type != BSS_MAP_MSG_COMPLETE_LAYER_3) { - LOGP(DNAT, LOGL_ERROR, "Can only extract LAC from Complete Layer3\n"); - return -1; - } - - if (!msg->l3h || msgb_l3len(msg) < 3) { - LOGP(DNAT, LOGL_ERROR, "Complete Layer3 mssage is too short.\n"); - return -1; - } - - tlv_parse(&tp, gsm0808_att_tlvdef(), msg->l3h + 3, msgb_l3len(msg) - 3, 0, 0); - if (!TLVP_PRESENT(&tp, GSM0808_IE_CELL_IDENTIFIER)) { - LOGP(DNAT, LOGL_ERROR, "No CellIdentifier List inside paging msg.\n"); - return -2; - } - - data_length = TLVP_LEN(&tp, GSM0808_IE_CELL_IDENTIFIER); - data = TLVP_VAL(&tp, GSM0808_IE_CELL_IDENTIFIER); - - /* Attemt to get the LAC/CI from it */ - if (data[0] == CELL_IDENT_WHOLE_GLOBAL) { - if (data_length != 8) { - LOGP(DNAT, LOGL_ERROR, - "Ident too short: %d\n", data_length); - return -3; - } - extract_lac(&data[1 + 3], &lac, &ci); - } else if (data[0] == CELL_IDENT_LAC_AND_CI) { - if (data_length != 5) { - LOGP(DNAT, LOGL_ERROR, - "Ident too short: %d\n", data_length); - return -3; - } - extract_lac(&data[1], &lac, &ci); - } else { - LOGP(DNAT, LOGL_ERROR, - "Unhandled cell identifier: %d\n", data[0]); - return -1; - } - - con->lac = lac; - con->ci = ci; - return 0; -} diff --git a/openbsc/src/osmo-bsc_nat/bsc_nat_vty.c b/openbsc/src/osmo-bsc_nat/bsc_nat_vty.c deleted file mode 100644 index a11ae151..00000000 --- a/openbsc/src/osmo-bsc_nat/bsc_nat_vty.c +++ /dev/null @@ -1,1336 +0,0 @@ -/* OpenBSC NAT interface to quagga VTY */ -/* (C) 2010-2015 by Holger Hans Peter Freyther - * (C) 2010-2015 by On-Waves - * All Rights Reserved - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation; either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - * - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include - -#include - -#include -#include - -static struct bsc_nat *_nat; - - -#define BSC_STR "Information about BSCs\n" -#define MGCP_STR "MGCP related status\n" -#define PAGING_STR "Paging\n" -#define SMSC_REWRITE "SMSC Rewriting\n" - -static struct cmd_node nat_node = { - NAT_NODE, - "%s(config-nat)# ", - 1, -}; - -static struct cmd_node bsc_node = { - NAT_BSC_NODE, - "%s(config-nat-bsc)# ", - 1, -}; - -static struct cmd_node pgroup_node = { - PGROUP_NODE, - "%s(config-nat-paging-group)# ", - 1, -}; - -static int config_write_pgroup(struct vty *vty) -{ - return CMD_SUCCESS; -} - -static void dump_lac(struct vty *vty, struct llist_head *head) -{ - struct bsc_lac_entry *lac; - llist_for_each_entry(lac, head, entry) - vty_out(vty, " location_area_code %u%s", lac->lac, VTY_NEWLINE); -} - - -static void write_pgroup_lst(struct vty *vty, struct bsc_nat_paging_group *pgroup) -{ - vty_out(vty, " paging-group %d%s", pgroup->nr, VTY_NEWLINE); - dump_lac(vty, &pgroup->lists); -} - -static int config_write_nat(struct vty *vty) -{ - struct bsc_msg_acc_lst *lst; - struct bsc_nat_paging_group *pgroup; - - vty_out(vty, "nat%s", VTY_NEWLINE); - vty_out(vty, " msc ip %s%s", _nat->main_dest->ip, VTY_NEWLINE); - vty_out(vty, " msc port %d%s", _nat->main_dest->port, VTY_NEWLINE); - vty_out(vty, " timeout auth %d%s", _nat->auth_timeout, VTY_NEWLINE); - vty_out(vty, " timeout ping %d%s", _nat->ping_timeout, VTY_NEWLINE); - vty_out(vty, " timeout pong %d%s", _nat->pong_timeout, VTY_NEWLINE); - if (_nat->include_file) - vty_out(vty, " bscs-config-file %s%s", _nat->include_file, VTY_NEWLINE); - if (_nat->token) - vty_out(vty, " token %s%s", _nat->token, VTY_NEWLINE); - vty_out(vty, " ip-dscp %d%s", _nat->bsc_ip_dscp, VTY_NEWLINE); - if (_nat->acc_lst_name) - vty_out(vty, " access-list-name %s%s", _nat->acc_lst_name, VTY_NEWLINE); - if (_nat->imsi_black_list_fn) - vty_out(vty, " imsi-black-list-file-name %s%s", - _nat->imsi_black_list_fn, VTY_NEWLINE); - if (_nat->ussd_lst_name) - vty_out(vty, " ussd-list-name %s%s", _nat->ussd_lst_name, VTY_NEWLINE); - if (_nat->ussd_query) - vty_out(vty, " ussd-query %s%s", _nat->ussd_query, VTY_NEWLINE); - if (_nat->ussd_token) - vty_out(vty, " ussd-token %s%s", _nat->ussd_token, VTY_NEWLINE); - if (_nat->ussd_local) - vty_out(vty, " ussd-local-ip %s%s", _nat->ussd_local, VTY_NEWLINE); - - if (_nat->num_rewr_name) - vty_out(vty, " number-rewrite %s%s", _nat->num_rewr_name, VTY_NEWLINE); - if (_nat->num_rewr_post_name) - vty_out(vty, " number-rewrite-post %s%s", - _nat->num_rewr_post_name, VTY_NEWLINE); - - if (_nat->smsc_rewr_name) - vty_out(vty, " rewrite-smsc addr %s%s", - _nat->smsc_rewr_name, VTY_NEWLINE); - if (_nat->tpdest_match_name) - vty_out(vty, " rewrite-smsc tp-dest-match %s%s", - _nat->tpdest_match_name, VTY_NEWLINE); - if (_nat->sms_clear_tp_srr_name) - vty_out(vty, " sms-clear-tp-srr %s%s", - _nat->sms_clear_tp_srr_name, VTY_NEWLINE); - if (_nat->sms_num_rewr_name) - vty_out(vty, " sms-number-rewrite %s%s", - _nat->sms_num_rewr_name, VTY_NEWLINE); - if (_nat->num_rewr_trie_name) - vty_out(vty, " prefix-tree %s%s", - _nat->num_rewr_trie_name, VTY_NEWLINE); - - llist_for_each_entry(lst, &_nat->access_lists, list) - bsc_msg_acc_lst_write(vty, lst); - llist_for_each_entry(pgroup, &_nat->paging_groups, entry) - write_pgroup_lst(vty, pgroup); - if (_nat->mgcp_ipa) - vty_out(vty, " use-msc-ipa-for-mgcp%s", VTY_NEWLINE); - vty_out(vty, " %ssdp-ensure-amr-mode-set%s", - _nat->sdp_ensure_amr_mode_set ? "" : "no ", VTY_NEWLINE); - - return CMD_SUCCESS; -} - -static void config_write_bsc_single(struct vty *vty, struct bsc_config *bsc) -{ - vty_out(vty, " bsc %u%s", bsc->nr, VTY_NEWLINE); - vty_out(vty, " token %s%s", bsc->token, VTY_NEWLINE); - if (bsc->key_present) - vty_out(vty, " auth-key %s%s", osmo_hexdump(bsc->key, 16), VTY_NEWLINE); - dump_lac(vty, &bsc->lac_list); - if (bsc->description) - vty_out(vty, " description %s%s", bsc->description, VTY_NEWLINE); - if (bsc->acc_lst_name) - vty_out(vty, " access-list-name %s%s", bsc->acc_lst_name, VTY_NEWLINE); - vty_out(vty, " max-endpoints %d%s", bsc->max_endpoints, VTY_NEWLINE); - if (bsc->paging_group != -1) - vty_out(vty, " paging group %d%s", bsc->paging_group, VTY_NEWLINE); - vty_out(vty, " paging forbidden %d%s", bsc->forbid_paging, VTY_NEWLINE); - switch (bsc->osmux) { - case OSMUX_USAGE_ON: - vty_out(vty, " osmux on%s", VTY_NEWLINE); - break; - case OSMUX_USAGE_ONLY: - vty_out(vty, " osmux only%s", VTY_NEWLINE); - break; - } -} - -static int config_write_bsc(struct vty *vty) -{ - struct bsc_config *bsc; - - llist_for_each_entry(bsc, &_nat->bsc_configs, entry) - config_write_bsc_single(vty, bsc); - return CMD_SUCCESS; -} - -DEFUN(show_bscs, show_bscs_cmd, "show bscs-config", - SHOW_STR "Show configured BSCs\n" - "Both from included file and vty\n") -{ - vty_out(vty, "BSCs configuration loaded from %s:%s", _nat->resolved_path, - VTY_NEWLINE); - return config_write_bsc(vty); -} - -DEFUN(show_sccp, show_sccp_cmd, "show sccp connections", - SHOW_STR "Display information about SCCP\n" - "All active connections\n") -{ - struct nat_sccp_connection *con; - vty_out(vty, "Listing all open SCCP connections%s", VTY_NEWLINE); - - llist_for_each_entry(con, &_nat->sccp_connections, list_entry) { - vty_out(vty, "For BSC Nr: %d BSC ref: 0x%x; MUX ref: 0x%x; Network has ref: %d ref: 0x%x MSC/BSC mux: 0x%x/0x%x type: %s%s", - con->bsc->cfg ? con->bsc->cfg->nr : -1, - sccp_src_ref_to_int(&con->real_ref), - sccp_src_ref_to_int(&con->patched_ref), - con->has_remote_ref, - sccp_src_ref_to_int(&con->remote_ref), - con->msc_endp, con->bsc_endp, - bsc_con_type_to_string(con->filter_state.con_type), - VTY_NEWLINE); - } - - return CMD_SUCCESS; -} - -DEFUN(show_nat_bsc, show_nat_bsc_cmd, "show nat num-bscs-configured", - SHOW_STR "Display NAT configuration details\n" - "BSCs-related\n") -{ - vty_out(vty, "%d BSCs configured%s", _nat->num_bsc, VTY_NEWLINE); - return CMD_SUCCESS; -} - -DEFUN(show_bsc, show_bsc_cmd, "show bsc connections", - SHOW_STR BSC_STR - "All active connections\n") -{ - struct bsc_connection *con; - struct sockaddr_in sock; - socklen_t len = sizeof(sock); - - llist_for_each_entry(con, &_nat->bsc_connections, list_entry) { - getpeername(con->write_queue.bfd.fd, (struct sockaddr *) &sock, &len); - vty_out(vty, "BSC nr: %d auth: %d fd: %d peername: %s pending-stats: %u%s", - con->cfg ? con->cfg->nr : -1, - con->authenticated, con->write_queue.bfd.fd, - inet_ntoa(sock.sin_addr), con->pending_dlcx_count, - VTY_NEWLINE); - } - - return CMD_SUCCESS; -} - -DEFUN(show_bsc_mgcp, show_bsc_mgcp_cmd, "show bsc mgcp NR", - SHOW_STR BSC_STR MGCP_STR "Identifier of the BSC\n") -{ - struct bsc_connection *con; - int nr = atoi(argv[0]); - int i, j, endp; - - llist_for_each_entry(con, &_nat->bsc_connections, list_entry) { - int max; - if (!con->cfg) - continue; - if (con->cfg->nr != nr) - continue; - - /* this bsc has no audio endpoints yet */ - if (!con->_endpoint_status) - continue; - - vty_out(vty, "MGCP Status for %d%s", con->cfg->nr, VTY_NEWLINE); - max = bsc_mgcp_nr_multiplexes(con->max_endpoints); - for (i = 0; i < max; ++i) { - for (j = 1; j < 32; ++j) { - endp = mgcp_timeslot_to_endpoint(i, j); - vty_out(vty, " Endpoint 0x%x %s%s", endp, - con->_endpoint_status[endp] == 0 - ? "free" : "allocated", - VTY_NEWLINE); - } - } - break; - } - - return CMD_SUCCESS; -} - -DEFUN(show_bsc_cfg, show_bsc_cfg_cmd, "show bsc config", - SHOW_STR BSC_STR "Configuration of BSCs\n") -{ - struct bsc_config *conf; - llist_for_each_entry(conf, &_nat->bsc_configs, entry) { - vty_out(vty, "BSC token: '%s' nr: %u%s", - conf->token, conf->nr, VTY_NEWLINE); - if (conf->acc_lst_name) - vty_out(vty, " access-list: %s%s", - conf->acc_lst_name, VTY_NEWLINE); - vty_out(vty, " paging forbidden: %d%s", - conf->forbid_paging, VTY_NEWLINE); - if (conf->description) - vty_out(vty, " description: %s%s", conf->description, VTY_NEWLINE); - else - vty_out(vty, " No description.%s", VTY_NEWLINE); - - } - - return CMD_SUCCESS; -} - -static void dump_stat_total(struct vty *vty, struct bsc_nat *nat) -{ - vty_out(vty, "NAT statistics%s", VTY_NEWLINE); - vty_out(vty, " SCCP Connections %lu total, %lu calls%s", - osmo_counter_get(nat->stats.sccp.conn), - osmo_counter_get(nat->stats.sccp.calls), VTY_NEWLINE); - vty_out(vty, " MSC Connections %lu%s", - osmo_counter_get(nat->stats.msc.reconn), VTY_NEWLINE); - vty_out(vty, " MSC Connected: %d%s", - bsc_nat_msc_is_connected(nat), VTY_NEWLINE); - vty_out(vty, " BSC Connections %lu total, %lu auth failed.%s", - osmo_counter_get(nat->stats.bsc.reconn), - osmo_counter_get(nat->stats.bsc.auth_fail), VTY_NEWLINE); -} - -static void dump_stat_bsc(struct vty *vty, struct bsc_config *conf) -{ - int connected = 0; - struct bsc_connection *con; - - vty_out(vty, " BSC nr: %d%s", - conf->nr, VTY_NEWLINE); - vty_out_rate_ctr_group(vty, " ", conf->stats.ctrg); - - llist_for_each_entry(con, &conf->nat->bsc_connections, list_entry) { - if (con->cfg != conf) - continue; - connected = 1; - break; - } - - vty_out(vty, " Connected: %d%s", connected, VTY_NEWLINE); -} - -DEFUN(show_stats, - show_stats_cmd, - "show statistics [NR]", - SHOW_STR "Display network statistics\n" - "Number of the BSC\n") -{ - struct bsc_config *conf; - - int nr = -1; - - if (argc == 1) - nr = atoi(argv[0]); - - dump_stat_total(vty, _nat); - llist_for_each_entry(conf, &_nat->bsc_configs, entry) { - if (argc == 1 && nr != conf->nr) - continue; - dump_stat_bsc(vty, conf); - } - - return CMD_SUCCESS; -} - -DEFUN(show_stats_lac, - show_stats_lac_cmd, - "show statistics-by-lac <0-65535>", - SHOW_STR "Display network statistics by lac\n" - "The lac of the BSC\n") -{ - int lac; - struct bsc_config *conf; - - lac = atoi(argv[0]); - - dump_stat_total(vty, _nat); - llist_for_each_entry(conf, &_nat->bsc_configs, entry) { - if (!bsc_config_handles_lac(conf, lac)) - continue; - dump_stat_bsc(vty, conf); - } - - return CMD_SUCCESS; -} - -DEFUN(show_msc, - show_msc_cmd, - "show msc connection", - SHOW_STR "MSC related information\n" - "Status of the A-link connection\n") -{ - if (!_nat->msc_con) { - vty_out(vty, "The MSC is not yet configured.%s", VTY_NEWLINE); - return CMD_WARNING; - } - - vty_out(vty, "MSC is connected: %d%s", - bsc_nat_msc_is_connected(_nat), VTY_NEWLINE); - return CMD_SUCCESS; -} - -DEFUN(close_bsc, - close_bsc_cmd, - "close bsc connection BSC_NR", - "Close\n" "A-link\n" "Connection\n" "Identifier of the BSC\n") -{ - struct bsc_connection *bsc; - int bsc_nr = atoi(argv[0]); - - llist_for_each_entry(bsc, &_nat->bsc_connections, list_entry) { - if (!bsc->cfg || bsc->cfg->nr != bsc_nr) - continue; - bsc_close_connection(bsc); - break; - } - - return CMD_SUCCESS; -} - -DEFUN(cfg_nat, cfg_nat_cmd, "nat", "Configure the NAT") -{ - vty->index = _nat; - vty->node = NAT_NODE; - - return CMD_SUCCESS; -} - -DEFUN(cfg_nat_msc_ip, - cfg_nat_msc_ip_cmd, - "msc ip A.B.C.D", - "MSC related configuration\n" - "Configure the IP address\n" IP_STR) -{ - bsc_nat_set_msc_ip(_nat, argv[0]); - return CMD_SUCCESS; -} - -DEFUN(cfg_nat_msc_port, - cfg_nat_msc_port_cmd, - "msc port <1-65500>", - "MSC related configuration\n" - "Configure the port\n" - "Port number\n") -{ - _nat->main_dest->port = atoi(argv[0]); - return CMD_SUCCESS; -} - -DEFUN(cfg_nat_auth_time, - cfg_nat_auth_time_cmd, - "timeout auth <1-256>", - "Timeout configuration\n" - "Authentication timeout\n" - "Timeout in seconds\n") -{ - _nat->auth_timeout = atoi(argv[0]); - return CMD_SUCCESS; -} - -DEFUN(cfg_nat_ping_time, - cfg_nat_ping_time_cmd, - "timeout ping NR", - "Timeout configuration\n" - "Time between two pings\n" - "Timeout in seconds\n") -{ - _nat->ping_timeout = atoi(argv[0]); - return CMD_SUCCESS; -} - -DEFUN(cfg_nat_pong_time, - cfg_nat_pong_time_cmd, - "timeout pong NR", - "Timeout configuration\n" - "Waiting for pong timeout\n" - "Timeout in seconds\n") -{ - _nat->pong_timeout = atoi(argv[0]); - return CMD_SUCCESS; -} - -DEFUN(cfg_nat_token, cfg_nat_token_cmd, - "token TOKEN", - "Authentication token configuration\n" - "Token of the BSC, currently transferred in cleartext\n") -{ - osmo_talloc_replace_string(_nat, &_nat->token, argv[0]); - return CMD_SUCCESS; -} - -DEFUN(cfg_nat_bsc_ip_dscp, cfg_nat_bsc_ip_dscp_cmd, - "ip-dscp <0-255>", - "Set the IP DSCP for the BSCs to use\n" "Set the IP_TOS attribute") -{ - _nat->bsc_ip_dscp = atoi(argv[0]); - return CMD_SUCCESS; -} - -ALIAS_DEPRECATED(cfg_nat_bsc_ip_dscp, cfg_nat_bsc_ip_tos_cmd, - "ip-tos <0-255>", - "Use ip-dscp in the future.\n" "Set the DSCP\n") - - -DEFUN(cfg_nat_acc_lst_name, - cfg_nat_acc_lst_name_cmd, - "access-list-name NAME", - "Set the name of the access list to use.\n" - "The name of the to be used access list.") -{ - osmo_talloc_replace_string(_nat, &_nat->acc_lst_name, argv[0]); - return CMD_SUCCESS; -} - -DEFUN(cfg_nat_include, - cfg_nat_include_cmd, - "bscs-config-file NAME", - "Set the filename of the BSC configuration to include.\n" - "The filename to be included.") -{ - char *path; - int rc; - struct bsc_config *cf1, *cf2; - struct bsc_connection *con1, *con2; - - if ('/' == argv[0][0]) - osmo_talloc_replace_string(_nat, &_nat->resolved_path, argv[0]); - else { - path = talloc_asprintf(_nat, "%s/%s", _nat->include_base, - argv[0]); - osmo_talloc_replace_string(_nat, &_nat->resolved_path, path); - talloc_free(path); - } - - llist_for_each_entry_safe(cf1, cf2, &_nat->bsc_configs, entry) { - cf1->remove = true; - cf1->token_updated = false; - } - - rc = vty_read_config_file(_nat->resolved_path, NULL); - if (rc < 0) { - vty_out(vty, "Failed to parse the config file %s: %s%s", - _nat->resolved_path, strerror(-rc), VTY_NEWLINE); - return CMD_WARNING; - } - - osmo_talloc_replace_string(_nat, &_nat->include_file, argv[0]); - - llist_for_each_entry_safe(con1, con2, &_nat->bsc_connections, - list_entry) { - if (con1->cfg) - if (con1->cfg->token_updated || con1->cfg->remove) - bsc_close_connection(con1); - } - - llist_for_each_entry_safe(cf1, cf2, &_nat->bsc_configs, entry) { - if (cf1->remove) - bsc_config_free(cf1); - } - - return CMD_SUCCESS; -} - -DEFUN(cfg_nat_no_acc_lst_name, - cfg_nat_no_acc_lst_name_cmd, - "no access-list-name", - NO_STR "Remove the access list from the NAT.\n") -{ - if (_nat->acc_lst_name) { - talloc_free(_nat->acc_lst_name); - _nat->acc_lst_name = NULL; - } - - return CMD_SUCCESS; -} - -DEFUN(cfg_nat_imsi_black_list_fn, - cfg_nat_imsi_black_list_fn_cmd, - "imsi-black-list-file-name NAME", - "IMSI black listing\n" "Filename IMSI and reject-cause\n") -{ - - osmo_talloc_replace_string(_nat, &_nat->imsi_black_list_fn, argv[0]); - if (_nat->imsi_black_list_fn) { - int rc; - struct osmo_config_list *rewr = NULL; - rewr = osmo_config_list_parse(_nat, _nat->imsi_black_list_fn); - rc = bsc_filter_barr_adapt(_nat, &_nat->imsi_black_list, rewr); - if (rc != 0) { - vty_out(vty, "%%There was an error parsing the list." - " Please see the error log.%s", VTY_NEWLINE); - return CMD_WARNING; - } - - return CMD_SUCCESS; - } - - bsc_filter_barr_adapt(_nat, &_nat->imsi_black_list, NULL); - return CMD_SUCCESS; -} - -DEFUN(cfg_nat_no_imsi_black_list_fn, - cfg_nat_no_imsi_black_list_fn_cmd, - "no imsi-black-list-file-name", - NO_STR "Remove the imsi-black-list\n") -{ - talloc_free(_nat->imsi_black_list_fn); - _nat->imsi_black_list_fn = NULL; - bsc_filter_barr_adapt(_nat, &_nat->imsi_black_list, NULL); - return CMD_SUCCESS; -} - -static int replace_rules(struct bsc_nat *nat, char **name, - struct llist_head *head, const char *file) -{ - struct osmo_config_list *rewr = NULL; - - osmo_talloc_replace_string(nat, name, file); - if (*name) { - rewr = osmo_config_list_parse(nat, *name); - bsc_nat_num_rewr_entry_adapt(nat, head, rewr); - talloc_free(rewr); - return CMD_SUCCESS; - } else { - bsc_nat_num_rewr_entry_adapt(nat, head, NULL); - return CMD_SUCCESS; - } -} - -DEFUN(cfg_nat_number_rewrite, - cfg_nat_number_rewrite_cmd, - "number-rewrite FILENAME", - "Set the file with rewriting rules.\n" "Filename") -{ - return replace_rules(_nat, &_nat->num_rewr_name, - &_nat->num_rewr, argv[0]); -} - -DEFUN(cfg_nat_no_number_rewrite, - cfg_nat_no_number_rewrite_cmd, - "no number-rewrite", - NO_STR "Set the file with rewriting rules.\n") -{ - talloc_free(_nat->num_rewr_name); - _nat->num_rewr_name = NULL; - - bsc_nat_num_rewr_entry_adapt(NULL, &_nat->num_rewr, NULL); - return CMD_SUCCESS; -} - -DEFUN(cfg_nat_number_rewrite_post, - cfg_nat_number_rewrite_post_cmd, - "number-rewrite-post FILENAME", - "Set the file with post-routing rewriting rules.\n" "Filename") -{ - return replace_rules(_nat, &_nat->num_rewr_post_name, - &_nat->num_rewr_post, argv[0]); -} - -DEFUN(cfg_nat_no_number_rewrite_post, - cfg_nat_no_number_rewrite_post_cmd, - "no number-rewrite-post", - NO_STR "Set the file with post-routing rewriting rules.\n") -{ - talloc_free(_nat->num_rewr_post_name); - _nat->num_rewr_post_name = NULL; - - bsc_nat_num_rewr_entry_adapt(NULL, &_nat->num_rewr_post, NULL); - return CMD_SUCCESS; -} - -DEFUN(cfg_nat_smsc_addr, - cfg_nat_smsc_addr_cmd, - "rewrite-smsc addr FILENAME", - SMSC_REWRITE - "The SMSC Address to match and replace in RP-DATA\n" - "File with rules for the SMSC Address replacing\n") -{ - return replace_rules(_nat, &_nat->smsc_rewr_name, - &_nat->smsc_rewr, argv[0]); -} - -DEFUN(cfg_nat_smsc_tpdest, - cfg_nat_smsc_tpdest_cmd, - "rewrite-smsc tp-dest-match FILENAME", - SMSC_REWRITE - "Match TP-Destination of a SMS.\n" - "File with rules for matching MSISDN and TP-DEST\n") -{ - return replace_rules(_nat, &_nat->tpdest_match_name, - &_nat->tpdest_match, argv[0]); -} - -DEFUN(cfg_nat_sms_clear_tpsrr, - cfg_nat_sms_clear_tpsrr_cmd, - "sms-clear-tp-srr FILENAME", - "SMS TPDU Sender Report Request clearing\n" - "Files with rules for matching MSISDN\n") -{ - return replace_rules(_nat, &_nat->sms_clear_tp_srr_name, - &_nat->sms_clear_tp_srr, argv[0]); -} - -DEFUN(cfg_nat_no_sms_clear_tpsrr, - cfg_nat_no_sms_clear_tpsrr_cmd, - "no sms-clear-tp-srr", - NO_STR - "SMS TPDU Sender Report Request clearing\n") -{ - talloc_free(_nat->sms_clear_tp_srr_name); - _nat->sms_clear_tp_srr_name = NULL; - - bsc_nat_num_rewr_entry_adapt(NULL, &_nat->sms_clear_tp_srr, NULL); - return CMD_SUCCESS; -} - -DEFUN(cfg_nat_sms_number_rewrite, - cfg_nat_sms_number_rewrite_cmd, - "sms-number-rewrite FILENAME", - "SMS TP-DA Number rewriting\n" - "Files with rules for matching MSISDN\n") -{ - return replace_rules(_nat, &_nat->sms_num_rewr_name, - &_nat->sms_num_rewr, argv[0]); -} - -DEFUN(cfg_nat_no_sms_number_rewrite, - cfg_nat_no_sms_number_rewrite_cmd, - "no sms-number-rewrite", - NO_STR "Disable SMS TP-DA rewriting\n") -{ - talloc_free(_nat->sms_num_rewr_name); - _nat->sms_num_rewr_name = NULL; - - bsc_nat_num_rewr_entry_adapt(NULL, &_nat->sms_num_rewr, NULL); - return CMD_SUCCESS; -} - -DEFUN(cfg_nat_prefix_trie, - cfg_nat_prefix_trie_cmd, - "prefix-tree FILENAME", - "Prefix tree for number rewriting\n" "File to load\n") -{ - /* give up the old data */ - talloc_free(_nat->num_rewr_trie); - _nat->num_rewr_trie = NULL; - - /* replace the file name */ - osmo_talloc_replace_string(_nat, &_nat->num_rewr_trie_name, argv[0]); - if (!_nat->num_rewr_trie_name) { - vty_out(vty, "%% prefix-tree no filename is present.%s", VTY_NEWLINE); - return CMD_WARNING; - } - - _nat->num_rewr_trie = nat_rewrite_parse(_nat, _nat->num_rewr_trie_name); - if (!_nat->num_rewr_trie) { - vty_out(vty, "%% prefix-tree parsing has failed.%s", VTY_NEWLINE); - return CMD_WARNING; - } - - vty_out(vty, "%% prefix-tree loaded %zu rules.%s", - _nat->num_rewr_trie->prefixes, VTY_NEWLINE); - return CMD_SUCCESS; -} - -DEFUN(cfg_nat_no_prefix_trie, cfg_nat_no_prefix_trie_cmd, - "no prefix-tree", - NO_STR "Prefix tree for number rewriting\n") -{ - talloc_free(_nat->num_rewr_trie); - _nat->num_rewr_trie = NULL; - talloc_free(_nat->num_rewr_trie_name); - _nat->num_rewr_trie_name = NULL; - - return CMD_SUCCESS; -} - -DEFUN(show_prefix_tree, show_prefix_tree_cmd, - "show prefix-tree", - SHOW_STR "Prefix tree for number rewriting\n") -{ - if (!_nat->num_rewr_trie) { - vty_out(vty, "%% there is now prefix tree loaded.%s", - VTY_NEWLINE); - return CMD_WARNING; - } - - nat_rewrite_dump_vty(vty, _nat->num_rewr_trie); - return CMD_SUCCESS; -} - -DEFUN(cfg_nat_ussd_lst_name, - cfg_nat_ussd_lst_name_cmd, - "ussd-list-name NAME", - "Set the name of the access list to check for IMSIs for USSD message\n" - "The name of the access list for HLR USSD handling") -{ - osmo_talloc_replace_string(_nat, &_nat->ussd_lst_name, argv[0]); - return CMD_SUCCESS; -} - -DEFUN(cfg_nat_ussd_query, - cfg_nat_ussd_query_cmd, - "ussd-query REGEXP", - "Set the USSD query to match with the ussd-list-name\n" - "The query to match") -{ - if (gsm_parse_reg(_nat, &_nat->ussd_query_re, &_nat->ussd_query, argc, argv) != 0) - return CMD_WARNING; - return CMD_SUCCESS; -} - -DEFUN(cfg_nat_ussd_token, - cfg_nat_ussd_token_cmd, - "ussd-token TOKEN", - "Set the token used to identify the USSD module\n" "Secret key\n") -{ - osmo_talloc_replace_string(_nat, &_nat->ussd_token, argv[0]); - return CMD_SUCCESS; -} - -DEFUN(cfg_nat_ussd_local, - cfg_nat_ussd_local_cmd, - "ussd-local-ip A.B.C.D", - "Set the IP to listen for the USSD Provider\n" "IP Address\n") -{ - osmo_talloc_replace_string(_nat, &_nat->ussd_local, argv[0]); - return CMD_SUCCESS; -} - -DEFUN(cfg_nat_use_ipa_for_mgcp, - cfg_nat_use_ipa_for_mgcp_cmd, - "use-msc-ipa-for-mgcp", - "This needs to be set at start. Handle MGCP messages through " - "the IPA protocol and not through the UDP socket.\n") -{ - if (_nat->mgcp_cfg->data) - vty_out(vty, - "%%the setting will not be applied right now.%s", - VTY_NEWLINE); - _nat->mgcp_ipa = 1; - return CMD_SUCCESS; -} - -DEFUN(cfg_nat_sdp_amr_mode_set, - cfg_nat_sdp_amr_mode_set_cmd, - "sdp-ensure-amr-mode-set", - "Ensure that SDP records include a mode-set\n") -{ - _nat->sdp_ensure_amr_mode_set = 1; - return CMD_SUCCESS; -} - -DEFUN(cfg_nat_no_sdp_amr_mode_set, - cfg_nat_no_sdp_amr_mode_set_cmd, - "no sdp-ensure-amr-mode-set", - NO_STR "Ensure that SDP records include a mode-set\n") -{ - _nat->sdp_ensure_amr_mode_set = 0; - return CMD_SUCCESS; -} - -/* per BSC configuration */ -DEFUN(cfg_bsc, cfg_bsc_cmd, "bsc BSC_NR", - "BSC configuration\n" "Identifier of the BSC\n") -{ - int bsc_nr = atoi(argv[0]); - struct bsc_config *bsc = bsc_config_num(_nat, bsc_nr); - - /* allocate a new one */ - if (!bsc) - bsc = bsc_config_alloc(_nat, "unknown", bsc_nr); - - if (!bsc) - return CMD_WARNING; - - bsc->remove = false; - vty->index = bsc; - vty->node = NAT_BSC_NODE; - - return CMD_SUCCESS; -} - -DEFUN(cfg_bsc_token, cfg_bsc_token_cmd, "token TOKEN", - "Authentication token configuration\n" - "Token of the BSC, currently transferred in cleartext\n") -{ - struct bsc_config *conf = vty->index; - - if (strncmp(conf->token, argv[0], 128) != 0) - conf->token_updated = true; - - osmo_talloc_replace_string(conf, &conf->token, argv[0]); - return CMD_SUCCESS; -} - -DEFUN(cfg_bsc_auth_key, cfg_bsc_auth_key_cmd, - "auth-key KEY", - "Authentication (secret) key configuration\n" - "Non null security key\n") -{ - struct bsc_config *conf = vty->index; - - memset(conf->key, 0, sizeof(conf->key)); - osmo_hexparse(argv[0], conf->key, sizeof(conf->key)); - conf->key_present = 1; - return CMD_SUCCESS; -} - -DEFUN(cfg_bsc_no_auth_key, cfg_bsc_no_auth_key_cmd, - "no auth-key", - NO_STR "Authentication (secret) key configuration\n") -{ - struct bsc_config *conf = vty->index; - - memset(conf->key, 0, sizeof(conf->key)); - conf->key_present = 0; - return CMD_SUCCESS; -} - -DEFUN(cfg_bsc_lac, cfg_bsc_lac_cmd, "location_area_code <0-65535>", - "Add the Location Area Code (LAC) of this BSC\n" "LAC\n") -{ - struct bsc_config *tmp; - struct bsc_config *conf = vty->index; - - int lac = atoi(argv[0]); - - if (lac == GSM_LAC_RESERVED_DETACHED || lac == GSM_LAC_RESERVED_ALL_BTS) { - vty_out(vty, "%% LAC %d is reserved by GSM 04.08%s", - lac, VTY_NEWLINE); - return CMD_WARNING; - } - - /* verify that the LACs are unique */ - llist_for_each_entry(tmp, &_nat->bsc_configs, entry) { - if (bsc_config_handles_lac(tmp, lac)) { - if (tmp->nr != conf->nr) { - vty_out(vty, "%% LAC %d is already used.%s", lac, - VTY_NEWLINE); - return CMD_ERR_INCOMPLETE; - } - } - } - - bsc_config_add_lac(conf, lac); - - return CMD_SUCCESS; -} - -DEFUN(cfg_bsc_no_lac, cfg_bsc_no_lac_cmd, - "no location_area_code <0-65535>", - NO_STR "Remove the Location Area Code (LAC) of this BSC\n" "LAC\n") -{ - int lac = atoi(argv[0]); - struct bsc_config *conf = vty->index; - - bsc_config_del_lac(conf, lac); - return CMD_SUCCESS; -} - -DEFUN(show_bar_lst, - show_bar_lst_cmd, - "show imsi-black-list", - SHOW_STR "IMSIs barred from the network\n") -{ - struct rb_node *node; - - vty_out(vty, "IMSIs barred from the network:%s", VTY_NEWLINE); - - for (node = rb_first(&_nat->imsi_black_list); node; node = rb_next(node)) { - struct bsc_filter_barr_entry *entry; - entry = rb_entry(node, struct bsc_filter_barr_entry, node); - - vty_out(vty, " IMSI(%s) CM-Reject-Cause(%d) LU-Reject-Cause(%d)%s", - entry->imsi, entry->cm_reject_cause, entry->lu_reject_cause, - VTY_NEWLINE); - } - - return CMD_SUCCESS; -} - - -DEFUN(cfg_bsc_acc_lst_name, - cfg_bsc_acc_lst_name_cmd, - "access-list-name NAME", - "Set the name of the access list to use.\n" - "The name of the to be used access list.") -{ - struct bsc_config *conf = vty->index; - - osmo_talloc_replace_string(conf, &conf->acc_lst_name, argv[0]); - return CMD_SUCCESS; -} - -DEFUN(cfg_bsc_no_acc_lst_name, - cfg_bsc_no_acc_lst_name_cmd, - "no access-list-name", - NO_STR "Do not use an access-list for the BSC.\n") -{ - struct bsc_config *conf = vty->index; - - if (conf->acc_lst_name) { - talloc_free(conf->acc_lst_name); - conf->acc_lst_name = NULL; - } - - return CMD_SUCCESS; -} - -DEFUN(cfg_bsc_max_endps, cfg_bsc_max_endps_cmd, - "max-endpoints <1-1024>", - "Highest endpoint to use (exclusively)\n" "Number of ports\n") -{ - struct bsc_config *conf = vty->index; - - conf->max_endpoints = atoi(argv[0]); - return CMD_SUCCESS; -} - -DEFUN(cfg_bsc_paging, - cfg_bsc_paging_cmd, - "paging forbidden (0|1)", - PAGING_STR "Forbid sending PAGING REQUESTS to the BSC.\n" - "Do not forbid\n" "Forbid\n") -{ - struct bsc_config *conf = vty->index; - - if (strcmp("1", argv[0]) == 0) - conf->forbid_paging = 1; - else - conf->forbid_paging = 0; - - return CMD_SUCCESS; -} - -DEFUN(cfg_bsc_desc, - cfg_bsc_desc_cmd, - "description DESC", - "Provide a description for the given BSC.\n" "Description\n") -{ - struct bsc_config *conf = vty->index; - - osmo_talloc_replace_string(conf, &conf->description, argv[0]); - return CMD_SUCCESS; -} - -DEFUN(cfg_bsc_paging_grp, - cfg_bsc_paging_grp_cmd, - "paging group <0-1000>", - PAGING_STR "Use a paging group\n" "Paging Group to use\n") -{ - struct bsc_config *conf = vty->index; - conf->paging_group = atoi(argv[0]); - return CMD_SUCCESS; -} - -ALIAS_DEPRECATED(cfg_bsc_paging_grp, cfg_bsc_old_grp_cmd, - "paging-group <0-1000>", - "Use a paging group\n" "Paging Group to use\n") - -DEFUN(cfg_bsc_no_paging_grp, - cfg_bsc_no_paging_grp_cmd, - "no paging group", - NO_STR PAGING_STR "Disable the usage of a paging group.\n") -{ - struct bsc_config *conf = vty->index; - conf->paging_group = PAGIN_GROUP_UNASSIGNED; - return CMD_SUCCESS; -} - -DEFUN(test_regex, test_regex_cmd, - "test regex PATTERN STRING", - "Test utilities\n" - "Regexp testing\n" "The regexp pattern\n" - "The string to match\n") -{ - regex_t reg; - char *str = NULL; - - memset(®, 0, sizeof(reg)); - if (gsm_parse_reg(_nat, ®, &str, 1, argv) != 0) - return CMD_WARNING; - - vty_out(vty, "String matches allow pattern: %d%s", - regexec(®, argv[1], 0, NULL, 0) == 0, VTY_NEWLINE); - - talloc_free(str); - regfree(®); - return CMD_SUCCESS; -} - -DEFUN(set_last_endp, set_last_endp_cmd, - "set bsc last-used-endpoint <0-9999999999> <0-1024>", - "Set a value\n" "Operate on a BSC\n" - "Last used endpoint for an assignment\n" "BSC configuration number\n" - "Endpoint number used\n") -{ - struct bsc_connection *con; - int nr = atoi(argv[0]); - int endp = atoi(argv[1]); - - - llist_for_each_entry(con, &_nat->bsc_connections, list_entry) { - if (!con->cfg) - continue; - if (con->cfg->nr != nr) - continue; - - con->last_endpoint = endp; - vty_out(vty, "Updated the last endpoint for %d to %d.%s", - con->cfg->nr, con->last_endpoint, VTY_NEWLINE); - } - - return CMD_SUCCESS; -} - -DEFUN(block_new_conn, block_new_conn_cmd, - "nat-block (block|unblock)", - "Block the NAT for new connections\n" - "Block\n" "Unblock\n") -{ - _nat->blocked = argv[0][0] == 'b'; - vty_out(vty, "%%Going to %s the NAT.%s", - _nat->blocked ? "block" : "unblock", VTY_NEWLINE); - return CMD_SUCCESS; -} - -/* paging group */ -DEFUN(cfg_nat_pgroup, cfg_nat_pgroup_cmd, - "paging-group <0-1000>", - "Create a Paging Group\n" "Number of the Group\n") -{ - int group = atoi(argv[0]); - struct bsc_nat_paging_group *pgroup; - pgroup = bsc_nat_paging_group_num(_nat, group); - if (!pgroup) - pgroup = bsc_nat_paging_group_create(_nat, group); - if (!pgroup) { - vty_out(vty, "Failed to create the group.%s", VTY_NEWLINE); - return CMD_WARNING; - } - - vty->index = pgroup; - vty->node = PGROUP_NODE; - return CMD_SUCCESS; -} - -DEFUN(cfg_nat_no_pgroup, cfg_nat_no_pgroup_cmd, - "no paging-group <0-1000>", - NO_STR "Delete paging-group\n" "Paging-group number\n") -{ - int group = atoi(argv[0]); - struct bsc_nat_paging_group *pgroup; - pgroup = bsc_nat_paging_group_num(_nat, group); - if (!pgroup) { - vty_out(vty, "No such paging group %d.%s", group, VTY_NEWLINE); - return CMD_WARNING; - } - - bsc_nat_paging_group_delete(pgroup); - return CMD_SUCCESS; -} - -DEFUN(cfg_pgroup_lac, cfg_pgroup_lac_cmd, - "location_area_code <0-65535>", - "Add the Location Area Code (LAC)\n" "LAC\n") -{ - struct bsc_nat_paging_group *pgroup = vty->index; - - int lac = atoi(argv[0]); - if (lac == GSM_LAC_RESERVED_DETACHED || lac == GSM_LAC_RESERVED_ALL_BTS) { - vty_out(vty, "%% LAC %d is reserved by GSM 04.08%s", - lac, VTY_NEWLINE); - return CMD_WARNING; - } - - bsc_nat_paging_group_add_lac(pgroup, lac); - return CMD_SUCCESS; -} - -DEFUN(cfg_pgroup_no_lac, cfg_pgroup_no_lac_cmd, - "no location_area_code <0-65535>", - NO_STR "Remove the Location Area Code (LAC)\n" "LAC\n") -{ - int lac = atoi(argv[0]); - struct bsc_nat_paging_group *pgroup = vty->index; - - bsc_nat_paging_group_del_lac(pgroup, lac); - return CMD_SUCCESS; -} - -DEFUN(show_ussd_connection, - show_ussd_connection_cmd, - "show ussd-connection", - SHOW_STR "USSD connection related information\n") -{ - vty_out(vty, "The USSD side channel provider is %sconnected and %sauthorized.%s", - _nat->ussd_con ? "" : "not ", - _nat->ussd_con && _nat->ussd_con->authorized? "" : "not ", - VTY_NEWLINE); - return CMD_SUCCESS; -} - -#define OSMUX_STR "RTP multiplexing\n" -DEFUN(cfg_bsc_osmux, - cfg_bsc_osmux_cmd, - "osmux (on|off|only)", - OSMUX_STR "Enable OSMUX\n" "Disable OSMUX\n" "Only OSMUX\n") -{ - struct bsc_config *conf = vty->index; - int old = conf->osmux; - - if (strcmp(argv[0], "on") == 0) - conf->osmux = OSMUX_USAGE_ON; - else if (strcmp(argv[0], "off") == 0) - conf->osmux = OSMUX_USAGE_OFF; - else if (strcmp(argv[0], "only") == 0) - conf->osmux = OSMUX_USAGE_ONLY; - - if (old == 0 && conf->osmux > 0 && !conf->nat->mgcp_cfg->osmux_init) { - LOGP(DMGCP, LOGL_NOTICE, "Setting up OSMUX socket\n"); - if (osmux_init(OSMUX_ROLE_BSC_NAT, conf->nat->mgcp_cfg) < 0) { - LOGP(DMGCP, LOGL_ERROR, "Cannot init OSMUX\n"); - vty_out(vty, "%% failed to create Osmux socket%s", - VTY_NEWLINE); - return CMD_WARNING; - } - } else if (old > 0 && conf->osmux == 0) { - LOGP(DMGCP, LOGL_NOTICE, "Disabling OSMUX socket\n"); - /* Don't stop the socket, we may already have ongoing voice - * flows already using Osmux. This just switch indicates that - * new upcoming flows should use RTP. - */ - } - - return CMD_SUCCESS; -} - -int bsc_nat_vty_init(struct bsc_nat *nat) -{ - _nat = nat; - - /* show commands */ - install_element_ve(&show_sccp_cmd); - install_element_ve(&show_bsc_cmd); - install_element_ve(&show_nat_bsc_cmd); - install_element_ve(&show_bsc_cfg_cmd); - install_element_ve(&show_stats_cmd); - install_element_ve(&show_stats_lac_cmd); - install_element_ve(&close_bsc_cmd); - install_element_ve(&show_msc_cmd); - install_element_ve(&test_regex_cmd); - install_element_ve(&show_bsc_mgcp_cmd); - install_element_ve(&show_bscs_cmd); - install_element_ve(&show_bar_lst_cmd); - install_element_ve(&show_prefix_tree_cmd); - install_element_ve(&show_ussd_connection_cmd); - - install_element(ENABLE_NODE, &set_last_endp_cmd); - install_element(ENABLE_NODE, &block_new_conn_cmd); - - /* nat group */ - install_element(CONFIG_NODE, &cfg_nat_cmd); - install_node(&nat_node, config_write_nat); - vty_install_default(NAT_NODE); - install_element(NAT_NODE, &cfg_nat_msc_ip_cmd); - install_element(NAT_NODE, &cfg_nat_msc_port_cmd); - install_element(NAT_NODE, &cfg_nat_auth_time_cmd); - install_element(NAT_NODE, &cfg_nat_ping_time_cmd); - install_element(NAT_NODE, &cfg_nat_pong_time_cmd); - install_element(NAT_NODE, &cfg_nat_token_cmd); - install_element(NAT_NODE, &cfg_nat_bsc_ip_dscp_cmd); - install_element(NAT_NODE, &cfg_nat_bsc_ip_tos_cmd); - install_element(NAT_NODE, &cfg_nat_acc_lst_name_cmd); - install_element(NAT_NODE, &cfg_nat_no_acc_lst_name_cmd); - install_element(NAT_NODE, &cfg_nat_include_cmd); - install_element(NAT_NODE, &cfg_nat_imsi_black_list_fn_cmd); - install_element(NAT_NODE, &cfg_nat_no_imsi_black_list_fn_cmd); - install_element(NAT_NODE, &cfg_nat_ussd_lst_name_cmd); - install_element(NAT_NODE, &cfg_nat_ussd_query_cmd); - install_element(NAT_NODE, &cfg_nat_ussd_token_cmd); - install_element(NAT_NODE, &cfg_nat_ussd_local_cmd); - install_element(NAT_NODE, &cfg_nat_use_ipa_for_mgcp_cmd); - - bsc_msg_lst_vty_init(nat, &nat->access_lists, NAT_NODE); - - /* number rewriting */ - install_element(NAT_NODE, &cfg_nat_number_rewrite_cmd); - install_element(NAT_NODE, &cfg_nat_no_number_rewrite_cmd); - install_element(NAT_NODE, &cfg_nat_number_rewrite_post_cmd); - install_element(NAT_NODE, &cfg_nat_no_number_rewrite_post_cmd); - install_element(NAT_NODE, &cfg_nat_smsc_addr_cmd); - install_element(NAT_NODE, &cfg_nat_smsc_tpdest_cmd); - install_element(NAT_NODE, &cfg_nat_sms_clear_tpsrr_cmd); - install_element(NAT_NODE, &cfg_nat_no_sms_clear_tpsrr_cmd); - install_element(NAT_NODE, &cfg_nat_sms_number_rewrite_cmd); - install_element(NAT_NODE, &cfg_nat_no_sms_number_rewrite_cmd); - install_element(NAT_NODE, &cfg_nat_prefix_trie_cmd); - install_element(NAT_NODE, &cfg_nat_no_prefix_trie_cmd); - - install_element(NAT_NODE, &cfg_nat_sdp_amr_mode_set_cmd); - install_element(NAT_NODE, &cfg_nat_no_sdp_amr_mode_set_cmd); - - install_element(NAT_NODE, &cfg_nat_pgroup_cmd); - install_element(NAT_NODE, &cfg_nat_no_pgroup_cmd); - install_node(&pgroup_node, config_write_pgroup); - vty_install_default(PGROUP_NODE); - install_element(PGROUP_NODE, &cfg_pgroup_lac_cmd); - install_element(PGROUP_NODE, &cfg_pgroup_no_lac_cmd); - - /* BSC subgroups */ - install_element(NAT_NODE, &cfg_bsc_cmd); - install_node(&bsc_node, NULL); - vty_install_default(NAT_BSC_NODE); - install_element(NAT_BSC_NODE, &cfg_bsc_token_cmd); - install_element(NAT_BSC_NODE, &cfg_bsc_auth_key_cmd); - install_element(NAT_BSC_NODE, &cfg_bsc_no_auth_key_cmd); - install_element(NAT_BSC_NODE, &cfg_bsc_lac_cmd); - install_element(NAT_BSC_NODE, &cfg_bsc_no_lac_cmd); - install_element(NAT_BSC_NODE, &cfg_bsc_paging_cmd); - install_element(NAT_BSC_NODE, &cfg_bsc_desc_cmd); - install_element(NAT_BSC_NODE, &cfg_bsc_acc_lst_name_cmd); - install_element(NAT_BSC_NODE, &cfg_bsc_no_acc_lst_name_cmd); - install_element(NAT_BSC_NODE, &cfg_bsc_max_endps_cmd); - install_element(NAT_BSC_NODE, &cfg_bsc_old_grp_cmd); - install_element(NAT_BSC_NODE, &cfg_bsc_paging_grp_cmd); - install_element(NAT_BSC_NODE, &cfg_bsc_no_paging_grp_cmd); - install_element(NAT_BSC_NODE, &cfg_bsc_osmux_cmd); - - mgcp_vty_init(); - - return 0; -} - - -/* called by the telnet interface... we have our own init above */ -int bsc_vty_init(struct gsm_network *network) -{ - logging_vty_add_cmds(NULL); - return 0; -} diff --git a/openbsc/src/osmo-bsc_nat/bsc_sccp.c b/openbsc/src/osmo-bsc_nat/bsc_sccp.c deleted file mode 100644 index c6c265f7..00000000 --- a/openbsc/src/osmo-bsc_nat/bsc_sccp.c +++ /dev/null @@ -1,247 +0,0 @@ -/* SCCP patching and handling routines */ -/* - * (C) 2010 by Holger Hans Peter Freyther - * (C) 2010 by On-Waves - * All Rights Reserved - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation; either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - * - */ - -#include -#include -#include - -#include - -#include - -#include -#include - -static int equal(struct sccp_source_reference *ref1, struct sccp_source_reference *ref2) -{ - return memcmp(ref1, ref2, sizeof(*ref1)) == 0; -} - -/* - * SCCP patching below - */ - -/* check if we are using this ref for patched already */ -static int sccp_ref_is_free(struct sccp_source_reference *ref, struct bsc_nat *nat) -{ - struct nat_sccp_connection *conn; - - llist_for_each_entry(conn, &nat->sccp_connections, list_entry) { - if (equal(ref, &conn->patched_ref)) - return -1; - } - - return 0; -} - -/* copied from sccp.c */ -static int assign_src_local_reference(struct sccp_source_reference *ref, struct bsc_nat *nat) -{ - static uint32_t last_ref = 0x50000; - int wrapped = 0; - - do { - struct sccp_source_reference reference; - reference.octet1 = (last_ref >> 0) & 0xff; - reference.octet2 = (last_ref >> 8) & 0xff; - reference.octet3 = (last_ref >> 16) & 0xff; - - ++last_ref; - /* do not use the reversed word and wrap around */ - if ((last_ref & 0x00FFFFFF) == 0x00FFFFFF) { - LOGP(DNAT, LOGL_NOTICE, "Wrapped searching for a free code\n"); - last_ref = 0; - ++wrapped; - } - - if (sccp_ref_is_free(&reference, nat) == 0) { - *ref = reference; - return 0; - } - } while (wrapped != 2); - - LOGP(DNAT, LOGL_ERROR, "Finding a free reference failed\n"); - return -1; -} - -struct nat_sccp_connection *create_sccp_src_ref(struct bsc_connection *bsc, - struct bsc_nat_parsed *parsed) -{ - struct nat_sccp_connection *conn; - - /* Some commercial BSCs like to reassign there SRC ref */ - llist_for_each_entry(conn, &bsc->nat->sccp_connections, list_entry) { - if (conn->bsc != bsc) - continue; - if (!equal(parsed->src_local_ref, &conn->real_ref)) - continue; - - /* the BSC has reassigned the SRC ref and we failed to keep track */ - memset(&conn->remote_ref, 0, sizeof(conn->remote_ref)); - if (assign_src_local_reference(&conn->patched_ref, bsc->nat) != 0) { - LOGP(DNAT, LOGL_ERROR, "BSC %d reused src ref: %d and we failed to generate a new id.\n", - bsc->cfg->nr, sccp_src_ref_to_int(parsed->src_local_ref)); - bsc_mgcp_dlcx(conn); - llist_del(&conn->list_entry); - talloc_free(conn); - return NULL; - } else { - clock_gettime(CLOCK_MONOTONIC, &conn->creation_time); - bsc_mgcp_dlcx(conn); - return conn; - } - } - - - conn = talloc_zero(bsc->nat, struct nat_sccp_connection); - if (!conn) { - LOGP(DNAT, LOGL_ERROR, "Memory allocation failure.\n"); - return NULL; - } - - conn->bsc = bsc; - clock_gettime(CLOCK_MONOTONIC, &conn->creation_time); - conn->real_ref = *parsed->src_local_ref; - if (assign_src_local_reference(&conn->patched_ref, bsc->nat) != 0) { - LOGP(DNAT, LOGL_ERROR, "Failed to assign a ref.\n"); - talloc_free(conn); - return NULL; - } - - bsc_mgcp_init(conn); - llist_add_tail(&conn->list_entry, &bsc->nat->sccp_connections); - rate_ctr_inc(&bsc->cfg->stats.ctrg->ctr[BCFG_CTR_SCCP_CONN]); - osmo_counter_inc(bsc->cfg->nat->stats.sccp.conn); - - LOGP(DNAT, LOGL_DEBUG, "Created 0x%x <-> 0x%x mapping for con %p\n", - sccp_src_ref_to_int(&conn->real_ref), - sccp_src_ref_to_int(&conn->patched_ref), bsc); - - return conn; -} - -int update_sccp_src_ref(struct nat_sccp_connection *sccp, struct bsc_nat_parsed *parsed) -{ - if (!parsed->dest_local_ref || !parsed->src_local_ref) { - LOGP(DNAT, LOGL_ERROR, "CC MSG should contain both local and dest address.\n"); - return -1; - } - - sccp->remote_ref = *parsed->src_local_ref; - sccp->has_remote_ref = 1; - LOGP(DNAT, LOGL_DEBUG, "Updating 0x%x to remote 0x%x on %p\n", - sccp_src_ref_to_int(&sccp->patched_ref), - sccp_src_ref_to_int(&sccp->remote_ref), sccp->bsc); - - return 0; -} - -void remove_sccp_src_ref(struct bsc_connection *bsc, struct msgb *msg, struct bsc_nat_parsed *parsed) -{ - struct nat_sccp_connection *conn; - - llist_for_each_entry(conn, &bsc->nat->sccp_connections, list_entry) { - if (equal(parsed->src_local_ref, &conn->patched_ref)) { - sccp_connection_destroy(conn); - return; - } - } - - LOGP(DNAT, LOGL_ERROR, "Can not remove connection: 0x%x\n", - sccp_src_ref_to_int(parsed->src_local_ref)); -} - -/* - * We have a message from the MSC to the BSC. The MSC is using - * an address that was assigned by the MUX, we need to update the - * dest reference to the real network. - */ -struct nat_sccp_connection *patch_sccp_src_ref_to_bsc(struct msgb *msg, - struct bsc_nat_parsed *parsed, - struct bsc_nat *nat) -{ - struct nat_sccp_connection *conn; - - if (!parsed->dest_local_ref) { - LOGP(DNAT, LOGL_ERROR, "MSG should contain dest_local_ref.\n"); - return NULL; - } - - - llist_for_each_entry(conn, &nat->sccp_connections, list_entry) { - if (!equal(parsed->dest_local_ref, &conn->patched_ref)) - continue; - - /* Change the dest address to the real one */ - *parsed->dest_local_ref = conn->real_ref; - return conn; - } - - return NULL; -} - -/* - * These are message to the MSC. We will need to find the BSC - * Connection by either the SRC or the DST local reference. - * - * In case of a CR we need to work by the SRC local reference - * in all other cases we need to work by the destination local - * reference.. - */ -struct nat_sccp_connection *patch_sccp_src_ref_to_msc(struct msgb *msg, - struct bsc_nat_parsed *parsed, - struct bsc_connection *bsc) -{ - struct nat_sccp_connection *conn; - - llist_for_each_entry(conn, &bsc->nat->sccp_connections, list_entry) { - if (conn->bsc != bsc) - continue; - - if (parsed->src_local_ref) { - if (equal(parsed->src_local_ref, &conn->real_ref)) { - *parsed->src_local_ref = conn->patched_ref; - return conn; - } - } else if (parsed->dest_local_ref) { - if (equal(parsed->dest_local_ref, &conn->remote_ref)) - return conn; - } else { - LOGP(DNAT, LOGL_ERROR, "Header has neither loc/dst ref.\n"); - return NULL; - } - } - - return NULL; -} - -struct nat_sccp_connection *bsc_nat_find_con_by_bsc(struct bsc_nat *nat, - struct sccp_source_reference *ref) -{ - struct nat_sccp_connection *conn; - - llist_for_each_entry(conn, &nat->sccp_connections, list_entry) { - if (equal(ref, &conn->real_ref)) - return conn; - } - - return NULL; -} diff --git a/openbsc/src/osmo-bsc_nat/bsc_ussd.c b/openbsc/src/osmo-bsc_nat/bsc_ussd.c deleted file mode 100644 index 0ba63270..00000000 --- a/openbsc/src/osmo-bsc_nat/bsc_ussd.c +++ /dev/null @@ -1,453 +0,0 @@ -/* USSD Filter Code */ - -/* - * (C) 2010-2011 by Holger Hans Peter Freyther - * (C) 2010-2011 by On-Waves - * All Rights Reserved - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation; either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - * - */ - -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include - -#include - -#include - -#include -#include -#include - -#define USSD_LAC_IE 0 -#define USSD_CI_IE 1 - -static void ussd_auth_con(struct tlv_parsed *, struct bsc_nat_ussd_con *); - -static struct bsc_nat_ussd_con *bsc_nat_ussd_alloc(struct bsc_nat *nat) -{ - struct bsc_nat_ussd_con *con; - - con = talloc_zero(nat, struct bsc_nat_ussd_con); - if (!con) - return NULL; - - con->nat = nat; - return con; -} - -static void bsc_nat_ussd_destroy(struct bsc_nat_ussd_con *con) -{ - if (con->nat->ussd_con == con) { - bsc_ussd_close_connections(con->nat); - con->nat->ussd_con = NULL; - } - - close(con->queue.bfd.fd); - osmo_fd_unregister(&con->queue.bfd); - osmo_timer_del(&con->auth_timeout); - osmo_wqueue_clear(&con->queue); - - msgb_free(con->pending_msg); - talloc_free(con); -} - -static void ussd_pong(struct bsc_nat_ussd_con *conn) -{ - struct msgb *msg; - - msg = msgb_alloc_headroom(4096, 128, "pong message"); - if (!msg) { - LOGP(DNAT, LOGL_ERROR, "Failed to allocate pong msg\n"); - return; - } - - msgb_v_put(msg, IPAC_MSGT_PONG); - bsc_do_write(&conn->queue, msg, IPAC_PROTO_IPACCESS); -} - -static int forward_sccp(struct bsc_nat *nat, struct msgb *msg) -{ - struct nat_sccp_connection *con; - struct bsc_nat_parsed *parsed; - - - parsed = bsc_nat_parse(msg); - if (!parsed) { - LOGP(DNAT, LOGL_ERROR, "Can not parse msg from USSD.\n"); - msgb_free(msg); - return -1; - } - - if (!parsed->dest_local_ref) { - LOGP(DNAT, LOGL_ERROR, "No destination local reference.\n"); - msgb_free(msg); - return -1; - } - - con = bsc_nat_find_con_by_bsc(nat, parsed->dest_local_ref); - if (!con || !con->bsc) { - LOGP(DNAT, LOGL_ERROR, "No active connection found.\n"); - msgb_free(msg); - return -1; - } - - talloc_free(parsed); - bsc_write_msg(&con->bsc->write_queue, msg); - return 0; -} - -static int ussd_read_cb(struct osmo_fd *bfd) -{ - struct bsc_nat_ussd_con *conn = bfd->data; - struct msgb *msg = NULL; - struct ipaccess_head *hh; - int ret; - - ret = ipa_msg_recv_buffered(bfd->fd, &msg, &conn->pending_msg); - if (ret <= 0) { - if (ret == -EAGAIN) - return 0; - LOGP(DNAT, LOGL_ERROR, "USSD Connection was lost.\n"); - bsc_nat_ussd_destroy(conn); - return -1; - } - - LOGP(DNAT, LOGL_NOTICE, "MSG from USSD: %s proto: %d\n", - osmo_hexdump(msg->data, msg->len), msg->l2h[0]); - hh = (struct ipaccess_head *) msg->data; - - if (hh->proto == IPAC_PROTO_IPACCESS) { - if (msg->l2h[0] == IPAC_MSGT_ID_RESP) { - struct tlv_parsed tvp; - int ret; - ret = ipa_ccm_idtag_parse(&tvp, - (unsigned char *) msg->l2h + 2, - msgb_l2len(msg) - 2); - if (ret < 0) { - LOGP(DNAT, LOGL_ERROR, "ignoring IPA response " - "message with malformed TLVs\n"); - msgb_free(msg); - return ret; - } - if (TLVP_PRESENT(&tvp, IPAC_IDTAG_UNITNAME)) - ussd_auth_con(&tvp, conn); - } else if (msg->l2h[0] == IPAC_MSGT_PING) { - LOGP(DNAT, LOGL_DEBUG, "Got USSD ping request.\n"); - ussd_pong(conn); - } else { - LOGP(DNAT, LOGL_NOTICE, "Got unknown IPACCESS message 0x%02x.\n", msg->l2h[0]); - } - - msgb_free(msg); - } else if (hh->proto == IPAC_PROTO_SCCP) { - forward_sccp(conn->nat, msg); - } else { - msgb_free(msg); - } - - return 0; -} - -static void ussd_auth_cb(void *_data) -{ - LOGP(DNAT, LOGL_ERROR, "USSD module didn't authenticate\n"); - bsc_nat_ussd_destroy((struct bsc_nat_ussd_con *) _data); -} - -static void ussd_auth_con(struct tlv_parsed *tvp, struct bsc_nat_ussd_con *conn) -{ - const char *token; - int len; - if (!conn->nat->ussd_token) { - LOGP(DNAT, LOGL_ERROR, "No USSD token set. Closing\n"); - bsc_nat_ussd_destroy(conn); - return; - } - - token = (const char *) TLVP_VAL(tvp, IPAC_IDTAG_UNITNAME); - len = TLVP_LEN(tvp, IPAC_IDTAG_UNITNAME); - - /* last byte should be a NULL */ - if (strlen(conn->nat->ussd_token) != len - 1) - goto disconnect; - /* compare everything including the null byte */ - if (memcmp(conn->nat->ussd_token, token, len) != 0) - goto disconnect; - - /* it is authenticated now */ - if (conn->nat->ussd_con && conn->nat->ussd_con != conn) - bsc_nat_ussd_destroy(conn->nat->ussd_con); - - LOGP(DNAT, LOGL_ERROR, "USSD token specified. USSD provider is connected.\n"); - osmo_timer_del(&conn->auth_timeout); - conn->authorized = 1; - conn->nat->ussd_con = conn; - return; - -disconnect: - LOGP(DNAT, LOGL_ERROR, "Wrong USSD token by client: %d\n", - conn->queue.bfd.fd); - bsc_nat_ussd_destroy(conn); -} - -static void ussd_start_auth(struct bsc_nat_ussd_con *conn) -{ - struct msgb *msg; - - osmo_timer_setup(&conn->auth_timeout, ussd_auth_cb, conn); - osmo_timer_schedule(&conn->auth_timeout, conn->nat->auth_timeout, 0); - - msg = msgb_alloc_headroom(4096, 128, "auth message"); - if (!msg) { - LOGP(DNAT, LOGL_ERROR, "Failed to allocate auth msg\n"); - return; - } - - msgb_v_put(msg, IPAC_MSGT_ID_GET); - bsc_do_write(&conn->queue, msg, IPAC_PROTO_IPACCESS); -} - -static int ussd_listen_cb(struct osmo_fd *bfd, unsigned int what) -{ - struct bsc_nat_ussd_con *conn; - struct bsc_nat *nat; - struct sockaddr_in sa; - socklen_t sa_len = sizeof(sa); - int fd; - - if (!(what & BSC_FD_READ)) - return 0; - - fd = accept(bfd->fd, (struct sockaddr *) &sa, &sa_len); - if (fd < 0) { - perror("accept"); - return fd; - } - - nat = (struct bsc_nat *) bfd->data; - osmo_counter_inc(nat->stats.ussd.reconn); - - conn = bsc_nat_ussd_alloc(nat); - if (!conn) { - LOGP(DNAT, LOGL_ERROR, "Failed to allocate USSD con struct.\n"); - close(fd); - return -1; - } - - osmo_wqueue_init(&conn->queue, 10); - conn->queue.bfd.data = conn; - conn->queue.bfd.fd = fd; - conn->queue.bfd.when = BSC_FD_READ; - conn->queue.read_cb = ussd_read_cb; - conn->queue.write_cb = bsc_write_cb; - - if (osmo_fd_register(&conn->queue.bfd) < 0) { - LOGP(DNAT, LOGL_ERROR, "Failed to register USSD fd.\n"); - bsc_nat_ussd_destroy(conn); - return -1; - } - - LOGP(DNAT, LOGL_NOTICE, "USSD Connection on %d with IP: %s\n", - fd, inet_ntoa(sa.sin_addr)); - - /* do authentication */ - ussd_start_auth(conn); - return 0; -} - -int bsc_ussd_init(struct bsc_nat *nat) -{ - struct in_addr addr; - - addr.s_addr = INADDR_ANY; - if (nat->ussd_local) - inet_aton(nat->ussd_local, &addr); - - nat->ussd_listen.data = nat; - return make_sock(&nat->ussd_listen, IPPROTO_TCP, - ntohl(addr.s_addr), 5001, 0, ussd_listen_cb, nat); -} - -static int forward_ussd_simple(struct nat_sccp_connection *con, struct msgb *input) -{ - struct msgb *copy; - struct bsc_nat_ussd_con *ussd; - - if (!con->bsc->nat->ussd_con) - return -1; - - copy = msgb_alloc_headroom(4096, 128, "forward bts"); - if (!copy) { - LOGP(DNAT, LOGL_ERROR, "Allocation failed, not forwarding.\n"); - return -1; - } - - /* copy the data into the copy */ - copy->l2h = msgb_put(copy, msgb_l2len(input)); - memcpy(copy->l2h, input->l2h, msgb_l2len(input)); - - /* send it out */ - ussd = con->bsc->nat->ussd_con; - bsc_do_write(&ussd->queue, copy, IPAC_PROTO_SCCP); - return 0; -} - -static int forward_ussd(struct nat_sccp_connection *con, const struct ussd_request *req, - struct msgb *input) -{ - struct msgb *msg, *copy; - struct ipac_msgt_sccp_state *state; - struct bsc_nat_ussd_con *ussd; - uint16_t lac, ci; - - if (!con->bsc->nat->ussd_con) - return -1; - - msg = msgb_alloc_headroom(4096, 128, "forward ussd"); - if (!msg) { - LOGP(DNAT, LOGL_ERROR, "Allocation failed, not forwarding.\n"); - return -1; - } - - copy = msgb_alloc_headroom(4096, 128, "forward bts"); - if (!copy) { - LOGP(DNAT, LOGL_ERROR, "Allocation failed, not forwarding.\n"); - msgb_free(msg); - return -1; - } - - copy->l2h = msgb_put(copy, msgb_l2len(input)); - memcpy(copy->l2h, input->l2h, msgb_l2len(input)); - - msg->l2h = msgb_put(msg, 1); - msg->l2h[0] = IPAC_MSGT_SCCP_OLD; - - /* fill out the data */ - state = (struct ipac_msgt_sccp_state *) msgb_put(msg, sizeof(*state)); - state->trans_id = req->transaction_id; - state->invoke_id = req->invoke_id; - memcpy(&state->src_ref, &con->remote_ref, sizeof(con->remote_ref)); - memcpy(&state->dst_ref, &con->real_ref, sizeof(con->real_ref)); - memcpy(state->imsi, con->filter_state.imsi, strlen(con->filter_state.imsi)); - - /* add additional tag/values */ - lac = htons(con->lac); - ci = htons(con->ci); - msgb_tv_fixed_put(msg, USSD_LAC_IE, sizeof(lac), (const uint8_t *) &lac); - msgb_tv_fixed_put(msg, USSD_CI_IE, sizeof(ci), (const uint8_t *) &ci); - - ussd = con->bsc->nat->ussd_con; - bsc_do_write(&ussd->queue, msg, IPAC_PROTO_IPACCESS); - bsc_do_write(&ussd->queue, copy, IPAC_PROTO_SCCP); - - return 0; -} - -int bsc_ussd_check(struct nat_sccp_connection *con, struct bsc_nat_parsed *parsed, - struct msgb *msg) -{ - uint32_t len; - uint8_t msg_type; - uint8_t proto; - uint8_t ti; - struct gsm48_hdr *hdr48; - struct bsc_msg_acc_lst *lst; - struct ussd_request req; - - /* - * various checks to avoid the decoding work. Right now we only want to - * decode if the connection was created for USSD, we do have a USSD access - * list, a query, a IMSI and such... - */ - if (con->filter_state.con_type != FLT_CON_TYPE_SSA) - return 0; - - if (!con->filter_state.imsi) - return 0; - - /* We have not verified the IMSI yet */ - if (!con->authorized) - return 0; - - if (!con->bsc->nat->ussd_lst_name) - return 0; - if (!con->bsc->nat->ussd_query) - return 0; - - if (parsed->bssap != BSSAP_MSG_DTAP) - return 0; - - if (strlen(con->filter_state.imsi) > GSM23003_IMSI_MAX_DIGITS) - return 0; - - hdr48 = bsc_unpack_dtap(parsed, msg, &len); - if (!hdr48) - return 0; - - proto = gsm48_hdr_pdisc(hdr48); - msg_type = gsm48_hdr_msg_type(hdr48); - ti = gsm48_hdr_trans_id_no_ti(hdr48); - if (proto != GSM48_PDISC_NC_SS) - return 0; - - if (msg_type == GSM0480_MTYPE_REGISTER) { - - /* now check if it is a IMSI we care about */ - lst = bsc_msg_acc_lst_find(&con->bsc->nat->access_lists, - con->bsc->nat->ussd_lst_name); - if (!lst) - return 0; - - if (bsc_msg_acc_lst_check_allow(lst, con->filter_state.imsi) != 0) - return 0; - - /* now decode the message and see if we really want to handle it */ - memset(&req, 0, sizeof(req)); - if (gsm0480_decode_ussd_request(hdr48, len, &req) != 1) - return 0; - if (req.text[0] == 0xff) - return 0; - - if (regexec(&con->bsc->nat->ussd_query_re, - req.text, 0, NULL, 0) == REG_NOMATCH) - return 0; - - /* found a USSD query for our subscriber */ - LOGP(DNAT, LOGL_NOTICE, "Found USSD query for %s\n", - con->filter_state.imsi); - con->ussd_ti[ti] = 1; - if (forward_ussd(con, &req, msg) != 0) - return 0; - return 1; - } else if (msg_type == GSM0480_MTYPE_FACILITY && con->ussd_ti[ti]) { - LOGP(DNAT, LOGL_NOTICE, "Forwarding message part of TI: %d %s\n", - ti, con->filter_state.imsi); - if (forward_ussd_simple(con, msg) != 0) - return 0; - return 1; - } - - return 0; -} diff --git a/openbsc/src/osmo-nitb/Makefile.am b/openbsc/src/osmo-nitb/Makefile.am deleted file mode 100644 index f4ef487c..00000000 --- a/openbsc/src/osmo-nitb/Makefile.am +++ /dev/null @@ -1,45 +0,0 @@ -AM_CPPFLAGS = \ - $(all_includes) \ - -I$(top_srcdir)/include \ - -I$(top_builddir) \ - $(NULL) - -AM_CFLAGS = \ - -Wall \ - $(COVERAGE_CFLAGS) \ - $(LIBOSMOCORE_CFLAGS) \ - $(LIBOSMOGSM_CFLAGS) \ - $(LIBOSMOVTY_CFLAGS) \ - $(LIBOSMOCTRL_CFLAGS) \ - $(LIBOSMOABIS_CFLAGS) \ - $(LIBSMPP34_CFLAGS) \ - $(LIBCRYPTO_CFLAGS) \ - $(NULL) - -AM_LDFLAGS = \ - $(COVERAGE_LDFLAGS) \ - $(NULL) - -bin_PROGRAMS = \ - osmo-nitb \ - $(NULL) - -osmo_nitb_SOURCES = \ - bsc_hack.c \ - $(NULL) - -osmo_nitb_LDADD = \ - $(top_builddir)/src/libbsc/libbsc.a \ - $(top_builddir)/src/libcommon-cs/libcommon-cs.a \ - $(top_builddir)/src/libmsc/libmsc.a \ - $(top_builddir)/src/libtrau/libtrau.a \ - $(top_builddir)/src/libcommon/libcommon.a \ - $(LIBOSMOGSM_LIBS) \ - $(LIBOSMOVTY_LIBS) \ - $(LIBOSMOCORE_LIBS) \ - $(LIBOSMOCTRL_LIBS) \ - $(LIBOSMOABIS_LIBS) \ - $(LIBSMPP34_LIBS) \ - $(LIBCRYPTO_LIBS) \ - -ldbi \ - $(NULL) diff --git a/openbsc/src/osmo-nitb/bsc_hack.c b/openbsc/src/osmo-nitb/bsc_hack.c deleted file mode 100644 index 17b23b2b..00000000 --- a/openbsc/src/osmo-nitb/bsc_hack.c +++ /dev/null @@ -1,402 +0,0 @@ -/* A hackish minimal BSC (+MSC +HLR) implementation */ - -/* (C) 2008-2010 by Harald Welte - * (C) 2009-2012 by Holger Hans Peter Freyther - * All Rights Reserved - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation; either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - * - */ - -#include -#include -#include -#include -#include -#include - -#define _GNU_SOURCE -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "../../bscconfig.h" - -/* MCC and MNC for the Location Area Identifier */ -struct gsm_network *bsc_gsmnet = 0; -static const char *database_name = "hlr.sqlite3"; -static const char *config_file = "openbsc.cfg"; -static const char *rf_ctrl_path = NULL; -extern const char *openbsc_copyright; -static int daemonize = 0; -static const char *mncc_sock_path = NULL; -static int use_db_counter = 1; - -/* timer to store statistics */ -#define DB_SYNC_INTERVAL 60, 0 -#define EXPIRE_INTERVAL 10, 0 - -static struct osmo_timer_list db_sync_timer; - -static void create_pcap_file(char *file) -{ - mode_t mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH; - int fd = open(file, O_WRONLY|O_TRUNC|O_CREAT, mode); - - if (fd < 0) { - perror("Failed to open file for pcap"); - return; - } - - e1_set_pcap_fd(fd); -} - -static void print_usage() -{ - printf("Usage: osmo-nitb\n"); -} - -static void print_help() -{ - printf(" Some useful help...\n"); - printf(" -h --help This text.\n"); - printf(" -d option --debug=DRLL:DCC:DMM:DRR:DRSL:DNM Enable debugging.\n"); - printf(" -D --daemonize Fork the process into a background daemon.\n"); - printf(" -c --config-file filename The config file to use.\n"); - printf(" -s --disable-color\n"); - printf(" -l --database db-name The database to use.\n"); - printf(" -a --authorize-everyone Authorize every new subscriber. Dangerous!\n"); - printf(" -T --timestamp Prefix every log line with a timestamp.\n"); - printf(" -V --version Print the version of OpenBSC.\n"); - printf(" -P --rtp-proxy Enable the RTP Proxy code inside OpenBSC.\n"); - printf(" -e --log-level number Set a global loglevel.\n"); - printf(" -M --mncc-sock-path PATH Disable built-in MNCC handler and offer socket.\n"); - printf(" -m --mncc-sock Same as `-M /tmp/bsc_mncc' (deprecated).\n"); - printf(" -C --no-dbcounter Disable regular syncing of counters to database.\n"); - printf(" -r --rf-ctl PATH A unix domain socket to listen for cmds.\n"); - printf(" -p --pcap PATH Write abis communication to pcap trace file.\n"); -} - -static void handle_options(int argc, char **argv) -{ - while (1) { - int option_index = 0, c; - static struct option long_options[] = { - {"help", 0, 0, 'h'}, - {"debug", 1, 0, 'd'}, - {"daemonize", 0, 0, 'D'}, - {"config-file", 1, 0, 'c'}, - {"disable-color", 0, 0, 's'}, - {"database", 1, 0, 'l'}, - {"authorize-everyone", 0, 0, 'a'}, - {"pcap", 1, 0, 'p'}, - {"timestamp", 0, 0, 'T'}, - {"version", 0, 0, 'V' }, - {"rtp-proxy", 0, 0, 'P'}, - {"log-level", 1, 0, 'e'}, - {"mncc-sock", 0, 0, 'm'}, - {"mncc-sock-path", 1, 0, 'M'}, - {"no-dbcounter", 0, 0, 'C'}, - {"rf-ctl", 1, 0, 'r'}, - {0, 0, 0, 0} - }; - - c = getopt_long(argc, argv, "hd:Dsl:ar:p:TPVc:e:mCr:M:", - long_options, &option_index); - if (c == -1) - break; - - switch (c) { - case 'h': - print_usage(); - print_help(); - exit(0); - case 's': - log_set_use_color(osmo_stderr_target, 0); - break; - case 'd': - log_parse_category_mask(osmo_stderr_target, optarg); - break; - case 'D': - daemonize = 1; - break; - case 'l': - database_name = optarg; - break; - case 'c': - config_file = optarg; - break; - case 'p': - create_pcap_file(optarg); - break; - case 'T': - log_set_print_timestamp(osmo_stderr_target, 1); - break; - case 'P': - ipacc_rtp_direct = 0; - break; - case 'e': - log_set_log_level(osmo_stderr_target, atoi(optarg)); - break; - case 'M': - mncc_sock_path = optarg; - break; - case 'm': - mncc_sock_path = "/tmp/bsc_mncc"; - break; - case 'C': - use_db_counter = 0; - break; - case 'V': - print_version(1); - exit(0); - break; - case 'r': - rf_ctrl_path = optarg; - break; - default: - /* catch unknown options *as well as* missing arguments. */ - fprintf(stderr, "Error in command line options. Exiting.\n"); - exit(-1); - break; - } - } -} - -extern void *tall_vty_ctx; -static void signal_handler(int signal) -{ - fprintf(stdout, "signal %u received\n", signal); - - switch (signal) { - case SIGINT: - bsc_shutdown_net(bsc_gsmnet); - osmo_signal_dispatch(SS_L_GLOBAL, S_L_GLOBAL_SHUTDOWN, NULL); - sleep(3); - exit(0); - break; - case SIGABRT: - osmo_generate_backtrace(); - /* in case of abort, we want to obtain a talloc report - * and then return to the caller, who will abort the process */ - case SIGUSR1: - talloc_report(tall_vty_ctx, stderr); - talloc_report_full(tall_bsc_ctx, stderr); - break; - case SIGUSR2: - talloc_report_full(tall_vty_ctx, stderr); - break; - default: - break; - } -} - -/* timer handling */ -static int _db_store_counter(struct osmo_counter *counter, void *data) -{ - return db_store_counter(counter); -} - -static void db_sync_timer_cb(void *data) -{ - /* store counters to database and re-schedule */ - osmo_counters_for_each(_db_store_counter, NULL); - osmo_timer_schedule(&db_sync_timer, DB_SYNC_INTERVAL); -} - -static void subscr_expire_cb(void *data) -{ - subscr_expire(bsc_gsmnet->subscr_group); - osmo_timer_schedule(&bsc_gsmnet->subscr_expire_timer, EXPIRE_INTERVAL); -} - -extern int bsc_vty_go_parent(struct vty *vty); - -static struct vty_app_info vty_info = { - .name = "OpenBSC", - .version = PACKAGE_VERSION, - .go_parent_cb = bsc_vty_go_parent, - .is_config_node = bsc_vty_is_config_node, -}; - -int main(int argc, char **argv) -{ - int rc; - - vty_info.copyright = openbsc_copyright; - - tall_bsc_ctx = talloc_named_const(NULL, 1, "openbsc"); - talloc_ctx_init(tall_bsc_ctx); - on_dso_load_token(); - on_dso_load_rrlp(); - on_dso_load_ho_dec(); - - libosmo_abis_init(tall_bsc_ctx); - osmo_init_logging(&log_info); - osmo_stats_init(tall_bsc_ctx); - bts_init(); - vty_init(&vty_info); - - /* Parse options */ - handle_options(argc, argv); - - /* Allocate global gsm_network struct; choose socket/internal MNCC */ - rc = bsc_network_alloc(mncc_sock_path? - mncc_sock_from_cc : int_mncc_recv); - if (rc) { - fprintf(stderr, "Allocation failed. Exiting.\n"); - exit(1); - } - - /* Initialize VTY */ - bsc_vty_init(bsc_gsmnet); - ctrl_vty_init(tall_bsc_ctx); - -#ifdef BUILD_SMPP - if (smpp_openbsc_alloc_init(tall_bsc_ctx) < 0) - return -1; -#endif - - /* Initialize MNCC socket if appropriate */ - if (mncc_sock_path) { - rc = mncc_sock_init(bsc_gsmnet, mncc_sock_path); - if (rc) { - fprintf(stderr, "MNCC socket initialization failed. exiting.\n"); - exit(1); - } - } else - DEBUGP(DMNCC, "Using internal MNCC handler.\n"); - - /* - * For osmo-nitb, skip TCH/F for now, because otherwise dyn TS - * always imply the possibility to have a mix of TCH/F and - * TCH/H channels; if two phones request a TCH/F and a TCH/H, - * respectively, they cannot call each other. If we deny TCH/F, - * they will both fall back to TCH/H, and dynamic channels are - * usable. See OS#1778. - * - * A third-party MSC may well be able to handle a TCH/H TCH/F - * mismatch. Moreover, this option may be overwritten in the - * config file or in VTY. - */ - bsc_gsmnet->dyn_ts_allow_tch_f = false; - - /* Read the config */ - rc = bsc_network_configure(config_file); - if (rc < 0) { - fprintf(stderr, "Reading config failed. Exiting.\n"); - exit(1); - } - -#ifdef BUILD_SMPP - smpp_openbsc_start(bsc_gsmnet); -#endif - bsc_api_init(bsc_gsmnet, msc_bsc_api()); - - /* start control interface after reading config for - * ctrl_vty_get_bind_addr() */ - bsc_gsmnet->ctrl = bsc_controlif_setup(bsc_gsmnet, - ctrl_vty_get_bind_addr(), - OSMO_CTRL_PORT_NITB_BSC); - if (!bsc_gsmnet->ctrl) { - printf("Failed to initialize control interface. Exiting.\n"); - return -1; - } - - if (bsc_base_ctrl_cmds_install() != 0) { - printf("Failed to initialize the BSC control commands.\n"); - return -1; - } - - if (msc_ctrl_cmds_install() != 0) { - printf("Failed to initialize the MSC control commands.\n"); - return -1; - } - - /* seed the PRNG */ - srand(time(NULL)); - - bsc_gsmnet->bsc_data->rf_ctrl = osmo_bsc_rf_create(rf_ctrl_path, bsc_gsmnet); - if (!bsc_gsmnet->bsc_data->rf_ctrl) { - fprintf(stderr, "Failed to create the RF service.\n"); - exit(1); - } - - if (db_init(database_name)) { - printf("DB: Failed to init database. Please check the option settings.\n"); - return -1; - } - printf("DB: Database initialized.\n"); - - if (db_prepare()) { - printf("DB: Failed to prepare database.\n"); - return -1; - } - printf("DB: Database prepared.\n"); - - /* setup the timer */ - osmo_timer_setup(&db_sync_timer, db_sync_timer_cb, NULL); - if (use_db_counter) - osmo_timer_schedule(&db_sync_timer, DB_SYNC_INTERVAL); - - osmo_timer_setup(&bsc_gsmnet->subscr_expire_timer, subscr_expire_cb, - NULL); - osmo_timer_schedule(&bsc_gsmnet->subscr_expire_timer, EXPIRE_INTERVAL); - - signal(SIGINT, &signal_handler); - signal(SIGABRT, &signal_handler); - signal(SIGUSR1, &signal_handler); - signal(SIGUSR2, &signal_handler); - osmo_init_ignore_signals(); - - /* start the SMS queue */ - if (sms_queue_start(bsc_gsmnet, 20) != 0) - return -1; - - if (daemonize) { - rc = osmo_daemonize(); - if (rc < 0) { - perror("Error during daemonize"); - exit(1); - } - } - - while (1) { - log_reset_context(); - osmo_select_main(0); - } -} diff --git a/openbsc/src/utils/Makefile.am b/openbsc/src/utils/Makefile.am deleted file mode 100644 index 26494e13..00000000 --- a/openbsc/src/utils/Makefile.am +++ /dev/null @@ -1,147 +0,0 @@ -AM_CPPFLAGS = \ - $(all_includes) \ - -I$(top_srcdir)/include \ - -I$(top_builddir) \ - $(NULL) - -AM_CFLAGS = \ - -Wall \ - $(LIBOSMOCORE_CFLAGS) \ - $(LIBOSMOGSM_CFLAGS) \ - $(LIBOSMOABIS_CFLAGS) \ - $(COVERAGE_CFLAGS) \ - $(SQLITE3_CFLAGS) \ - $(LIBSMPP34_CFLAGS) \ - $(NULL) - -AM_LDFLAGS = \ - $(COVERAGE_LDFLAGS) \ - $(NULL) - -noinst_HEADERS = \ - meas_db.h \ - $(NULL) - -bin_PROGRAMS = \ - bs11_config \ - isdnsync \ - meas_json \ - $(NULL) -if HAVE_SQLITE3 -bin_PROGRAMS += \ - osmo-meas-udp2db \ - $(NULL) -if HAVE_PCAP -bin_PROGRAMS += \ - osmo-meas-pcap2db \ - $(NULL) -endif -endif -if HAVE_LIBCDK -bin_PROGRAMS += \ - meas_vis \ - $(NULL) -endif - -if BUILD_SMPP -noinst_PROGRAMS = \ - smpp_mirror \ - $(NULL) -endif - -bs11_config_SOURCES = \ - bs11_config.c \ - $(NULL) - -bs11_config_LDADD = \ - $(top_builddir)/src/libbsc/libbsc.a \ - $(top_builddir)/src/libcommon-cs/libcommon-cs.a \ - $(top_builddir)/src/libtrau/libtrau.a \ - $(top_builddir)/src/libcommon/libcommon.a \ - $(LIBOSMOCORE_LIBS) \ - $(LIBOSMOGSM_LIBS) \ - $(LIBOSMOABIS_LIBS) \ - $(NULL) - -isdnsync_SOURCES = \ - isdnsync.c \ - $(NULL) - -smpp_mirror_SOURCES = \ - smpp_mirror.c \ - $(NULL) - -smpp_mirror_LDADD = \ - $(top_builddir)/src/libcommon/libcommon.a \ - $(LIBOSMOCORE_LIBS) \ - $(LIBOSMOGSM_LIBS) \ - $(LIBSMPP34_LIBS) \ - $(NULL) - -meas_vis_SOURCES = \ - meas_vis.c \ - $(NULL) - -meas_vis_LDADD = \ - $(LIBOSMOCORE_LIBS) \ - $(LIBOSMOGSM_LIBS) \ - -lcdk \ - -lncurses \ - $(NULL) - -meas_vis_CFLAGS = \ - $(LIBOSMOCORE_CFLAGS) \ - $(LIBOSMOGSM_CFLAGS) \ - $(NULL) - -osmo_meas_pcap2db_SOURCES = \ - meas_pcap2db.c \ - meas_db.c \ - $(NULL) - -osmo_meas_pcap2db_LDADD = \ - $(LIBOSMOCORE_LIBS) \ - $(LIBOSMOGSM_LIBS) \ - $(SQLITE3_LIBS) \ - -lpcap \ - $(NULL) - -osmo_meas_pcap2db_CFLAGS = \ - $(LIBOSMOCORE_CFLAGS) \ - $(LIBOSMOGSM_CFLAGS) \ - $(LIBOSMOABIS_CFLAGS) \ - $(NULL) - -osmo_meas_udp2db_SOURCES = \ - meas_udp2db.c \ - meas_db.c \ - $(NULL) - -osmo_meas_udp2db_LDADD = \ - $(LIBOSMOCORE_LIBS) \ - $(LIBOSMOGSM_LIBS) \ - $(SQLITE3_LIBS) \ - $(NULL) - -osmo_meas_udp2db_CFLAGS = \ - $(LIBOSMOCORE_CFLAGS) \ - $(LIBOSMOGSM_CFLAGS) \ - $(LIBOSMOABIS_CFLAGS) \ - $(NULL) - -meas_json_SOURCES = \ - meas_json.c \ - $(NULL) - -meas_json_LDADD = \ - $(top_builddir)/src/libcommon/libcommon.a \ - $(LIBOSMOCORE_LIBS) \ - $(LIBOSMOGSM_LIBS) \ - $(NULL) - -meas_json_CFLAGS = \ - $(LIBOSMOCORE_CFLAGS) \ - $(LIBOSMOGSM_CFLAGS) \ - $(LIBOSMOABIS_CFLAGS) \ - $(NULL) - diff --git a/openbsc/src/utils/bs11_config.c b/openbsc/src/utils/bs11_config.c deleted file mode 100644 index a0f3cb75..00000000 --- a/openbsc/src/utils/bs11_config.c +++ /dev/null @@ -1,953 +0,0 @@ -/* Siemens BS-11 microBTS configuration tool */ - -/* (C) 2009-2010 by Harald Welte - * All Rights Reserved - * - * This software is based on ideas (but not code) of BS11Config - * (C) 2009 by Dieter Spaar - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation; either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - * - */ - -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -static void *tall_bs11cfg_ctx; -static struct e1inp_sign_link *oml_link; - -/* state of our bs11_config application */ -enum bs11cfg_state { - STATE_NONE, - STATE_LOGON_WAIT, - STATE_LOGON_ACK, - STATE_SWLOAD, - STATE_QUERY, -}; -static enum bs11cfg_state bs11cfg_state = STATE_NONE; -static char *command, *value; -struct osmo_timer_list status_timer; - -static const uint8_t obj_li_attr[] = { - NM_ATT_BS11_BIT_ERR_THESH, 0x09, 0x00, - NM_ATT_BS11_L1_PROT_TYPE, 0x00, - NM_ATT_BS11_LINE_CFG, 0x00, -}; -static const uint8_t obj_bbsig0_attr[] = { - NM_ATT_BS11_RSSI_OFFS, 0x02, 0x00, 0x00, - NM_ATT_BS11_DIVERSITY, 0x01, 0x00, -}; -static const uint8_t obj_pa0_attr[] = { - NM_ATT_BS11_TXPWR, 0x01, BS11_TRX_POWER_GSM_30mW, -}; -static const char *trx1_password = "1111111111"; -#define TEI_OML 25 - -/* dummy function to keep gsm_data.c happy */ -struct osmo_counter *osmo_counter_alloc(const char *name) -{ - return NULL; -} - -int handle_serial_msg(struct msgb *rx_msg); - -/* create all objects for an initial configuration */ -static int create_objects(struct gsm_bts *bts) -{ - fprintf(stdout, "Crating Objects for minimal config\n"); - abis_nm_bs11_create_object(bts, BS11_OBJ_LI, 0, sizeof(obj_li_attr), - obj_li_attr); - abis_nm_bs11_create_object(bts, BS11_OBJ_GPSU, 0, 0, NULL); - abis_nm_bs11_create_object(bts, BS11_OBJ_ALCO, 0, 0, NULL); - abis_nm_bs11_create_object(bts, BS11_OBJ_CCLK, 0, 0, NULL); - abis_nm_bs11_create_object(bts, BS11_OBJ_BBSIG, 0, - sizeof(obj_bbsig0_attr), obj_bbsig0_attr); - abis_nm_bs11_create_object(bts, BS11_OBJ_PA, 0, - sizeof(obj_pa0_attr), obj_pa0_attr); - abis_nm_bs11_create_envaBTSE(bts, 0); - abis_nm_bs11_create_envaBTSE(bts, 1); - abis_nm_bs11_create_envaBTSE(bts, 2); - abis_nm_bs11_create_envaBTSE(bts, 3); - - abis_nm_bs11_conn_oml_tei(bts, 0, 1, 0xff, TEI_OML); - - abis_nm_bs11_set_trx_power(bts->c0, BS11_TRX_POWER_GSM_30mW); - - sleep(1); - - abis_nm_bs11_set_trx1_pw(bts, trx1_password); - - sleep(1); - - return 0; -} - -static int create_trx1(struct gsm_bts *bts) -{ - uint8_t bbsig1_attr[sizeof(obj_bbsig0_attr)+12]; - uint8_t *cur = bbsig1_attr; - struct gsm_bts_trx *trx = gsm_bts_trx_num(bts, 1); - - if (!trx) - trx = gsm_bts_trx_alloc(bts); - - fprintf(stdout, "Crating Objects for TRX1\n"); - - abis_nm_bs11_set_trx1_pw(bts, trx1_password); - - sleep(1); - - cur = tlv_put(cur, NM_ATT_BS11_PASSWORD, 10, - (uint8_t *)trx1_password); - memcpy(cur, obj_bbsig0_attr, sizeof(obj_bbsig0_attr)); - abis_nm_bs11_create_object(bts, BS11_OBJ_BBSIG, 1, - sizeof(bbsig1_attr), bbsig1_attr); - abis_nm_bs11_create_object(bts, BS11_OBJ_PA, 1, - sizeof(obj_pa0_attr), obj_pa0_attr); - abis_nm_bs11_set_trx_power(trx, BS11_TRX_POWER_GSM_30mW); - - return 0; -} - -static char *serial_port = "/dev/ttyUSB0"; -static char *fname_safety = "BTSBMC76.SWI"; -static char *fname_software = "HS011106.SWL"; -static int delay_ms = 0; -static int win_size = 8; -static int param_disconnect = 0; -static int param_restart = 0; -static int param_forced = 0; -static struct gsm_bts *g_bts; - -static int file_is_readable(const char *fname) -{ - int rc; - struct stat st; - - rc = stat(fname, &st); - if (rc < 0) - return 0; - - if (S_ISREG(st.st_mode) && (st.st_mode & S_IRUSR)) - return 1; - - return 0; -} - -static int percent; -static int percent_old; - -/* callback function passed to the ABIS OML code */ -static int swload_cbfn(unsigned int hook, unsigned int event, struct msgb *msg, - void *data, void *param) -{ - if (hook != GSM_HOOK_NM_SWLOAD) - return 0; - - switch (event) { - case NM_MT_LOAD_INIT_ACK: - fprintf(stdout, "Software Load Initiate ACK\n"); - break; - case NM_MT_LOAD_INIT_NACK: - fprintf(stderr, "ERROR: Software Load Initiate NACK\n"); - exit(5); - break; - case NM_MT_LOAD_END_ACK: - if (data) { - /* we did a safety load and must activate it */ - abis_nm_software_activate(g_bts, fname_safety, - swload_cbfn, g_bts); - sleep(5); - } - break; - case NM_MT_LOAD_END_NACK: - fprintf(stderr, "ERROR: Software Load End NACK\n"); - exit(3); - break; - case NM_MT_ACTIVATE_SW_NACK: - fprintf(stderr, "ERROR: Activate Software NACK\n"); - exit(4); - break; - case NM_MT_ACTIVATE_SW_ACK: - bs11cfg_state = STATE_NONE; - - break; - case NM_MT_LOAD_SEG_ACK: - percent = abis_nm_software_load_status(g_bts); - if (percent > percent_old) - printf("Software Download Progress: %d%%\n", percent); - percent_old = percent; - break; - } - return 0; -} - -static const struct value_string bs11_linkst_names[] = { - { 0, "Down" }, - { 1, "Up" }, - { 2, "Restoring" }, - { 0, NULL } -}; - -static const char *linkstate_name(uint8_t linkstate) -{ - return get_value_string(bs11_linkst_names, linkstate); -} - -static const struct value_string mbccu_load_names[] = { - { 0, "No Load" }, - { 1, "Load BTSCAC" }, - { 2, "Load BTSDRX" }, - { 3, "Load BTSBBX" }, - { 4, "Load BTSARC" }, - { 5, "Load" }, - { 0, NULL } -}; - -static const char *mbccu_load_name(uint8_t linkstate) -{ - return get_value_string(mbccu_load_names, linkstate); -} - -static const char *bts_phase_name(uint8_t phase) -{ - switch (phase) { - case BS11_STATE_WARM_UP: - case BS11_STATE_WARM_UP_2: - return "Warm Up"; - break; - case BS11_STATE_LOAD_SMU_SAFETY: - return "Load SMU Safety"; - break; - case BS11_STATE_LOAD_SMU_INTENDED: - return "Load SMU Intended"; - break; - case BS11_STATE_LOAD_MBCCU: - return "Load MBCCU"; - break; - case BS11_STATE_SOFTWARE_RQD: - return "Software required"; - break; - case BS11_STATE_WAIT_MIN_CFG: - case BS11_STATE_WAIT_MIN_CFG_2: - return "Wait minimal config"; - break; - case BS11_STATE_MAINTENANCE: - return "Maintenance"; - break; - case BS11_STATE_NORMAL: - return "Normal"; - break; - case BS11_STATE_ABIS_LOAD: - return "Abis load"; - break; - default: - return "Unknown"; - break; - } -} - -static const char *trx_power_name(uint8_t pwr) -{ - switch (pwr) { - case BS11_TRX_POWER_GSM_2W: - return "2W (GSM)"; - case BS11_TRX_POWER_GSM_250mW: - return "250mW (GSM)"; - case BS11_TRX_POWER_GSM_80mW: - return "80mW (GSM)"; - case BS11_TRX_POWER_GSM_30mW: - return "30mW (GSM)"; - case BS11_TRX_POWER_DCS_3W: - return "3W (DCS)"; - case BS11_TRX_POWER_DCS_1W6: - return "1.6W (DCS)"; - case BS11_TRX_POWER_DCS_500mW: - return "500mW (DCS)"; - case BS11_TRX_POWER_DCS_160mW: - return "160mW (DCS)"; - default: - return "unknown value"; - } -} - -static const char *pll_mode_name(uint8_t mode) -{ - switch (mode) { - case BS11_LI_PLL_LOCKED: - return "E1 Locked"; - case BS11_LI_PLL_STANDALONE: - return "Standalone"; - default: - return "unknown"; - } -} - -static const char *cclk_acc_name(uint8_t acc) -{ - switch (acc) { - case 0: - /* Out of the demanded +/- 0.05ppm */ - return "Medium"; - case 1: - /* Synchronized with Abis, within demanded tolerance +/- 0.05ppm */ - return "High"; - default: - return "unknown"; - } -} - -static const char *bport_lcfg_name(uint8_t lcfg) -{ - switch (lcfg) { - case BS11_LINE_CFG_STAR: - return "Star"; - case BS11_LINE_CFG_MULTIDROP: - return "Multi-Drop"; - default: - return "unknown"; - } -} - -static const char *obj_name(struct abis_om_fom_hdr *foh) -{ - static char retbuf[256]; - - retbuf[0] = 0; - - switch (foh->obj_class) { - case NM_OC_BS11: - strcat(retbuf, "BS11 "); - switch (foh->obj_inst.bts_nr) { - case BS11_OBJ_PA: - sprintf(retbuf+strlen(retbuf), "Power Amplifier %d ", - foh->obj_inst.ts_nr); - break; - case BS11_OBJ_LI: - sprintf(retbuf+strlen(retbuf), "Line Interface "); - break; - case BS11_OBJ_CCLK: - sprintf(retbuf+strlen(retbuf), "CCLK "); - break; - } - break; - case NM_OC_SITE_MANAGER: - strcat(retbuf, "SITE MANAGER "); - break; - case NM_OC_BS11_BPORT: - sprintf(retbuf+strlen(retbuf), "BPORT%u ", - foh->obj_inst.bts_nr); - break; - } - return retbuf; -} - -static void print_state(struct tlv_parsed *tp) -{ - if (TLVP_PRESENT(tp, NM_ATT_BS11_BTS_STATE)) { - uint8_t phase, mbccu; - if (TLVP_LEN(tp, NM_ATT_BS11_BTS_STATE) >= 1) { - phase = *TLVP_VAL(tp, NM_ATT_BS11_BTS_STATE); - printf("PHASE: %u %-20s ", phase & 0xf, - bts_phase_name(phase)); - } - if (TLVP_LEN(tp, NM_ATT_BS11_BTS_STATE) >= 2) { - mbccu = *(TLVP_VAL(tp, NM_ATT_BS11_BTS_STATE)+1); - printf("MBCCU0: %-11s MBCCU1: %-11s ", - mbccu_load_name(mbccu & 0xf), mbccu_load_name(mbccu >> 4)); - } - } - if (TLVP_PRESENT(tp, NM_ATT_BS11_E1_STATE) && - TLVP_LEN(tp, NM_ATT_BS11_E1_STATE) >= 1) { - uint8_t e1_state = *TLVP_VAL(tp, NM_ATT_BS11_E1_STATE); - printf("Abis-link: %-9s ", linkstate_name(e1_state & 0xf)); - } - printf("\n"); -} - -static int print_attr(struct tlv_parsed *tp) -{ - if (TLVP_PRESENT(tp, NM_ATT_BS11_ESN_PCB_SERIAL)) { - printf("\tBS-11 ESN PCB Serial Number: %s\n", - TLVP_VAL(tp, NM_ATT_BS11_ESN_PCB_SERIAL)); - } - if (TLVP_PRESENT(tp, NM_ATT_BS11_ESN_HW_CODE_NO)) { - printf("\tBS-11 ESN Hardware Code Number: %s\n", - TLVP_VAL(tp, NM_ATT_BS11_ESN_HW_CODE_NO)+6); - } - if (TLVP_PRESENT(tp, NM_ATT_BS11_ESN_FW_CODE_NO)) { - printf("\tBS-11 ESN Firmware Code Number: %s\n", - TLVP_VAL(tp, NM_ATT_BS11_ESN_FW_CODE_NO)+6); - } -#if 0 - if (TLVP_PRESENT(tp, NM_ATT_BS11_BOOT_SW_VERS)) { - printf("BS-11 Boot Software Version: %s\n", - TLVP_VAL(tp, NM_ATT_BS11_BOOT_SW_VERS)+6); - } -#endif - if (TLVP_PRESENT(tp, NM_ATT_ABIS_CHANNEL) && - TLVP_LEN(tp, NM_ATT_ABIS_CHANNEL) >= 3) { - const uint8_t *chan = TLVP_VAL(tp, NM_ATT_ABIS_CHANNEL); - printf("\tE1 Channel: Port=%u Timeslot=%u ", - chan[0], chan[1]); - if (chan[2] == 0xff) - printf("(Full Slot)\n"); - else - printf("Subslot=%u\n", chan[2]); - } - if (TLVP_PRESENT(tp, NM_ATT_TEI)) - printf("\tTEI: %d\n", *TLVP_VAL(tp, NM_ATT_TEI)); - if (TLVP_PRESENT(tp, NM_ATT_BS11_TXPWR) && - TLVP_LEN(tp, NM_ATT_BS11_TXPWR) >= 1) { - printf("\tTRX Power: %s\n", - trx_power_name(*TLVP_VAL(tp, NM_ATT_BS11_TXPWR))); - } - if (TLVP_PRESENT(tp, NM_ATT_BS11_PLL_MODE) && - TLVP_LEN(tp, NM_ATT_BS11_PLL_MODE) >= 1) { - printf("\tPLL Mode: %s\n", - pll_mode_name(*TLVP_VAL(tp, NM_ATT_BS11_PLL_MODE))); - } - if (TLVP_PRESENT(tp, NM_ATT_BS11_PLL) && - TLVP_LEN(tp, NM_ATT_BS11_PLL) >= 4) { - const uint8_t *vp = TLVP_VAL(tp, NM_ATT_BS11_PLL); - printf("\tPLL Set Value=%d, Work Value=%d\n", - vp[0] << 8 | vp[1], vp[2] << 8 | vp[3]); - } - if (TLVP_PRESENT(tp, NM_ATT_BS11_CCLK_ACCURACY) && - TLVP_LEN(tp, NM_ATT_BS11_CCLK_ACCURACY) >= 1) { - const uint8_t *acc = TLVP_VAL(tp, NM_ATT_BS11_CCLK_ACCURACY); - printf("\tCCLK Accuracy: %s (%d)\n", cclk_acc_name(*acc), *acc); - } - if (TLVP_PRESENT(tp, NM_ATT_BS11_CCLK_TYPE) && - TLVP_LEN(tp, NM_ATT_BS11_CCLK_TYPE) >= 1) { - const uint8_t *acc = TLVP_VAL(tp, NM_ATT_BS11_CCLK_TYPE); - printf("\tCCLK Type=%d\n", *acc); - } - if (TLVP_PRESENT(tp, NM_ATT_BS11_LINE_CFG) && - TLVP_LEN(tp, NM_ATT_BS11_LINE_CFG) >= 1) { - const uint8_t *lcfg = TLVP_VAL(tp, NM_ATT_BS11_LINE_CFG); - printf("\tLine Configuration: %s (%d)\n", - bport_lcfg_name(*lcfg), *lcfg); - } - - - - return 0; -} - -static void cmd_query(void) -{ - struct gsm_bts_trx *trx = g_bts->c0; - - bs11cfg_state = STATE_QUERY; - abis_nm_bs11_get_serno(g_bts); - abis_nm_bs11_get_oml_tei_ts(g_bts); - abis_nm_bs11_get_pll_mode(g_bts); - abis_nm_bs11_get_cclk(g_bts); - abis_nm_bs11_get_trx_power(trx); - trx = gsm_bts_trx_num(g_bts, 1); - if (trx) - abis_nm_bs11_get_trx_power(trx); - abis_nm_bs11_get_bport_line_cfg(g_bts, 0); - abis_nm_bs11_get_bport_line_cfg(g_bts, 1); - sleep(1); - abis_nm_bs11_factory_logon(g_bts, 0); - command = NULL; -} - -/* handle a response from the BTS to a GET STATE command */ -static int handle_state_resp(enum abis_bs11_phase state) -{ - int rc = 0; - - switch (state) { - case BS11_STATE_WARM_UP: - case BS11_STATE_LOAD_SMU_SAFETY: - case BS11_STATE_LOAD_SMU_INTENDED: - case BS11_STATE_LOAD_MBCCU: - break; - case BS11_STATE_SOFTWARE_RQD: - bs11cfg_state = STATE_SWLOAD; - /* send safety load. Use g_bts as private 'param' - * argument, so our swload_cbfn can distinguish - * a safety load from a regular software */ - if (file_is_readable(fname_safety)) - rc = abis_nm_software_load(g_bts, 0xff, fname_safety, - win_size, param_forced, - swload_cbfn, g_bts); - else - fprintf(stderr, "No valid Safety Load file \"%s\"\n", - fname_safety); - break; - case BS11_STATE_WAIT_MIN_CFG: - case BS11_STATE_WAIT_MIN_CFG_2: - bs11cfg_state = STATE_SWLOAD; - rc = create_objects(g_bts); - break; - case BS11_STATE_MAINTENANCE: - if (command) { - if (!strcmp(command, "disconnect")) - abis_nm_bs11_factory_logon(g_bts, 0); - else if (!strcmp(command, "reconnect")) - rc = abis_nm_bs11_bsc_disconnect(g_bts, 1); - else if (!strcmp(command, "software") - && bs11cfg_state != STATE_SWLOAD) { - bs11cfg_state = STATE_SWLOAD; - /* send software (FIXME: over A-bis?) */ - if (file_is_readable(fname_software)) - rc = abis_nm_bs11_load_swl(g_bts, fname_software, - win_size, param_forced, - swload_cbfn); - else - fprintf(stderr, "No valid Software file \"%s\"\n", - fname_software); - } else if (!strcmp(command, "delete-trx1")) { - printf("Locing BBSIG and PA objects of TRX1\n"); - abis_nm_chg_adm_state(g_bts, NM_OC_BS11, - BS11_OBJ_BBSIG, 0, 1, - NM_STATE_LOCKED); - abis_nm_chg_adm_state(g_bts, NM_OC_BS11, - BS11_OBJ_PA, 0, 1, - NM_STATE_LOCKED); - sleep(1); - printf("Deleting BBSIG and PA objects of TRX1\n"); - abis_nm_bs11_delete_object(g_bts, BS11_OBJ_BBSIG, 1); - abis_nm_bs11_delete_object(g_bts, BS11_OBJ_PA, 1); - sleep(1); - abis_nm_bs11_factory_logon(g_bts, 0); - command = NULL; - } else if (!strcmp(command, "create-trx1")) { - create_trx1(g_bts); - sleep(1); - abis_nm_bs11_factory_logon(g_bts, 0); - command = NULL; - } else if (!strcmp(command, "pll-e1-locked")) { - abis_nm_bs11_set_pll_locked(g_bts, 1); - sleep(1); - abis_nm_bs11_factory_logon(g_bts, 0); - command = NULL; - } else if (!strcmp(command, "pll-standalone")) { - abis_nm_bs11_set_pll_locked(g_bts, 0); - sleep(1); - abis_nm_bs11_factory_logon(g_bts, 0); - command = NULL; - } else if (!strcmp(command, "pll-setvalue")) { - abis_nm_bs11_set_pll(g_bts, atoi(value)); - sleep(1); - abis_nm_bs11_factory_logon(g_bts, 0); - command = NULL; - } else if (!strcmp(command, "pll-workvalue")) { - /* To set the work value we need to login as FIELD */ - abis_nm_bs11_factory_logon(g_bts, 0); - sleep(1); - abis_nm_bs11_infield_logon(g_bts, 1); - sleep(1); - abis_nm_bs11_set_pll(g_bts, atoi(value)); - sleep(1); - abis_nm_bs11_infield_logon(g_bts, 0); - command = NULL; - } else if (!strcmp(command, "oml-tei")) { - abis_nm_bs11_conn_oml_tei(g_bts, 0, 1, 0xff, TEI_OML); - command = NULL; - } else if (!strcmp(command, "restart")) { - abis_nm_bs11_restart(g_bts); - command = NULL; - } else if (!strcmp(command, "query")) { - cmd_query(); - } else if (!strcmp(command, "create-bport1")) { - abis_nm_bs11_create_bport(g_bts, 1); - sleep(1); - abis_nm_bs11_factory_logon(g_bts, 0); - command = NULL; - } else if (!strcmp(command, "delete-bport1")) { - abis_nm_chg_adm_state(g_bts, NM_OC_BS11_BPORT, 1, 0xff, 0xff, NM_STATE_LOCKED); - sleep(1); - abis_nm_bs11_delete_bport(g_bts, 1); - sleep(1); - abis_nm_bs11_factory_logon(g_bts, 0); - command = NULL; - } else if (!strcmp(command, "bport0-star")) { - abis_nm_bs11_set_bport_line_cfg(g_bts, 0, BS11_LINE_CFG_STAR); - sleep(1); - abis_nm_bs11_factory_logon(g_bts, 0); - command = NULL; - } else if (!strcmp(command, "bport0-multidrop")) { - abis_nm_bs11_set_bport_line_cfg(g_bts, 0, BS11_LINE_CFG_MULTIDROP); - sleep(1); - abis_nm_bs11_factory_logon(g_bts, 0); - command = NULL; - } else if (!strcmp(command, "bport1-multidrop")) { - abis_nm_bs11_set_bport_line_cfg(g_bts, 1, BS11_LINE_CFG_MULTIDROP); - sleep(1); - abis_nm_bs11_factory_logon(g_bts, 0); - command = NULL; - } - - } - break; - case BS11_STATE_NORMAL: - if (command) { - if (!strcmp(command, "reconnect")) - abis_nm_bs11_factory_logon(g_bts, 0); - else if (!strcmp(command, "disconnect")) - abis_nm_bs11_bsc_disconnect(g_bts, 0); - else if (!strcmp(command, "query")) { - cmd_query(); - } - } else if (param_disconnect) { - param_disconnect = 0; - abis_nm_bs11_bsc_disconnect(g_bts, 0); - if (param_restart) { - param_restart = 0; - abis_nm_bs11_restart(g_bts); - } - } - break; - default: - break; - } - return rc; -} - -/* handle a fully-received message/packet from the RS232 port */ -static int abis_nm_bs11cfg_rcvmsg(struct msgb *rx_msg) -{ - struct e1inp_sign_link *link = rx_msg->dst; - struct abis_om_hdr *oh; - struct abis_om_fom_hdr *foh; - struct tlv_parsed tp; - int rc = -1; - -#if 0 - const uint8_t too_fast[] = { 0x12, 0x80, 0x00, 0x00, 0x02, 0x02 }; - - if (rx_msg->len < LAPD_HDR_LEN - + sizeof(struct abis_om_fom_hdr) - + sizeof(struct abis_om_hdr)) { - if (!memcmp(rx_msg->data + 2, too_fast, - sizeof(too_fast))) { - fprintf(stderr, "BS11 tells us we're too " - "fast, try --delay bigger than %u\n", - delay_ms); - return -E2BIG; - } else - fprintf(stderr, "unknown BS11 message\n"); - } -#endif - - oh = (struct abis_om_hdr *) msgb_l2(rx_msg); - foh = (struct abis_om_fom_hdr *) oh->data; - switch (foh->msg_type) { - case NM_MT_BS11_LMT_LOGON_ACK: - printf("LMT LOGON: ACK\n\n"); - if (bs11cfg_state == STATE_NONE) - bs11cfg_state = STATE_LOGON_ACK; - rc = abis_nm_bs11_get_state(g_bts); - break; - case NM_MT_BS11_LMT_LOGOFF_ACK: - printf("LMT LOGOFF: ACK\n"); - exit(0); - break; - case NM_MT_BS11_GET_STATE_ACK: - rc = abis_nm_tlv_parse(&tp, g_bts, foh->data, oh->length-sizeof(*foh)); - print_state(&tp); - if (TLVP_PRESENT(&tp, NM_ATT_BS11_BTS_STATE) && - TLVP_LEN(&tp, NM_ATT_BS11_BTS_STATE) >= 1) - rc = handle_state_resp(*TLVP_VAL(&tp, NM_ATT_BS11_BTS_STATE)); - break; - case NM_MT_GET_ATTR_RESP: - printf("\n%sATTRIBUTES:\n", obj_name(foh)); - abis_nm_tlv_parse(&tp, g_bts, foh->data, oh->length-sizeof(*foh)); - rc = print_attr(&tp); - //osmo_hexdump(foh->data, oh->length-sizeof(*foh)); - break; - case NM_MT_BS11_SET_ATTR_ACK: - printf("SET ATTRIBUTE ObjClass=0x%02x ObjInst=(%d,%d,%d) ACK\n", - foh->obj_class, foh->obj_inst.bts_nr, - foh->obj_inst.trx_nr, foh->obj_inst.ts_nr); - rc = 0; - break; - case NM_MT_BS11_SET_ATTR_NACK: - printf("SET ATTRIBUTE ObjClass=0x%02x ObjInst=(%d,%d,%d) NACK\n", - foh->obj_class, foh->obj_inst.bts_nr, - foh->obj_inst.trx_nr, foh->obj_inst.ts_nr); - break; - case NM_MT_GET_ATTR_NACK: - printf("\n%sGET ATTR NACK\n", obj_name(foh)); - break; - case NM_MT_BS11_CREATE_OBJ_ACK: - printf("\n%sCREATE OBJECT ACK\n", obj_name(foh)); - break; - case NM_MT_BS11_CREATE_OBJ_NACK: - printf("\n%sCREATE OBJECT NACK\n", obj_name(foh)); - break; - case NM_MT_BS11_DELETE_OBJ_ACK: - printf("\n%sDELETE OBJECT ACK\n", obj_name(foh)); - break; - case NM_MT_BS11_DELETE_OBJ_NACK: - printf("\n%sDELETE OBJECT NACK\n", obj_name(foh)); - break; - default: - rc = abis_nm_rcvmsg(rx_msg); - } - if (rc < 0) { - perror("ERROR in main loop"); - //break; - } - /* flush the queue of pending messages to be sent. */ - abis_nm_queue_send_next(link->trx->bts); - if (rc == 1) - return rc; - - switch (bs11cfg_state) { - case STATE_NONE: - abis_nm_bs11_factory_logon(g_bts, 1); - break; - case STATE_LOGON_ACK: - osmo_timer_schedule(&status_timer, 5, 0); - break; - default: - break; - } - - return rc; -} - -void status_timer_cb(void *data) -{ - abis_nm_bs11_get_state(g_bts); -} - -static void print_banner(void) -{ - printf("bs11_config (C) 2009-2010 by Harald Welte and Dieter Spaar\n"); - printf("This is FREE SOFTWARE with ABSOLUTELY NO WARRANTY\n\n"); -} - -static void print_help(void) -{ - printf("bs11_config [options] [command]\n"); - printf("\nSupported options:\n"); - printf("\t-h --help\t\t\tPrint this help text\n"); - printf("\t-p --port \t\tSpecify serial port\n"); - printf("\t-s --software \t\tSpecify Software file\n"); - printf("\t-S --safety \t\tSpecify Safety Load file\n"); - printf("\t-d --delay \t\t\tSpecify delay in milliseconds\n"); - printf("\t-D --disconnect\t\t\tDisconnect BTS from BSC\n"); - printf("\t-w --win-size \t\tSpecify Window Size\n"); - printf("\t-f --forced\t\t\tForce Software Load\n"); - printf("\nSupported commands:\n"); - printf("\tquery\t\t\tQuery the BS-11 about serial number and configuration\n"); - printf("\tdisconnect\t\tDisconnect A-bis link (go into administrative state)\n"); - printf("\tresconnect\t\tReconnect A-bis link (go into normal state)\n"); - printf("\trestart\t\t\tRestart the BTS\n"); - printf("\tsoftware\t\tDownload Software (only in administrative state)\n"); - printf("\tcreate-trx1\t\tCreate objects for TRX1 (Danger: Your BS-11 might overheat)\n"); - printf("\tdelete-trx1\t\tDelete objects for TRX1\n"); - printf("\tpll-e1-locked\t\tSet the PLL to be locked to E1 clock\n"); - printf("\tpll-standalone\t\tSet the PLL to be in standalone mode\n"); - printf("\tpll-setvalue \tSet the PLL set value\n"); - printf("\tpll-workvalue \tSet the PLL work value\n"); - printf("\toml-tei\t\t\tSet OML E1 TS and TEI\n"); - printf("\tbport0-star\t\tSet BPORT0 line config to star\n"); - printf("\tbport0-multidrop\tSet BPORT0 line config to multidrop\n"); - printf("\tbport1-multidrop\tSet BPORT1 line config to multidrop\n"); - printf("\tcreate-bport1\t\tCreate BPORT1 object\n"); - printf("\tdelete-bport1\t\tDelete BPORT1 object\n"); -} - -static void handle_options(int argc, char **argv) -{ - int option_index = 0; - print_banner(); - - while (1) { - int c; - static struct option long_options[] = { - { "help", 0, 0, 'h' }, - { "port", 1, 0, 'p' }, - { "software", 1, 0, 's' }, - { "safety", 1, 0, 'S' }, - { "delay", 1, 0, 'd' }, - { "disconnect", 0, 0, 'D' }, - { "win-size", 1, 0, 'w' }, - { "forced", 0, 0, 'f' }, - { "restart", 0, 0, 'r' }, - { "debug", 1, 0, 'b'}, - }; - - c = getopt_long(argc, argv, "hp:s:S:td:Dw:fra:", - long_options, &option_index); - - if (c == -1) - break; - - switch (c) { - case 'h': - print_help(); - exit(0); - case 'p': - serial_port = optarg; - break; - case 'b': - log_parse_category_mask(osmo_stderr_target, optarg); - break; - case 's': - fname_software = optarg; - break; - case 'S': - fname_safety = optarg; - break; - case 'd': - delay_ms = atoi(optarg); - break; - case 'w': - win_size = atoi(optarg); - break; - case 'D': - param_disconnect = 1; - break; - case 'f': - param_forced = 1; - break; - case 'r': - param_disconnect = 1; - param_restart = 1; - break; - default: - break; - } - } - if (optind < argc) { - command = argv[optind]; - if (optind+1 < argc) - value = argv[optind+1]; - } - -} - -static int num_sigint; - -static void signal_handler(int signal) -{ - fprintf(stdout, "\nsignal %u received\n", signal); - - switch (signal) { - case SIGINT: - num_sigint++; - abis_nm_bs11_factory_logon(g_bts, 0); - if (num_sigint >= 3) - exit(0); - break; - } -} - -static int bs11cfg_sign_link(struct msgb *msg) -{ - msg->dst = oml_link; - return abis_nm_bs11cfg_rcvmsg(msg); -} - -struct e1inp_line_ops bs11cfg_e1inp_line_ops = { - .sign_link = bs11cfg_sign_link, -}; - -extern int bts_model_bs11_init(void); -int main(int argc, char **argv) -{ - struct gsm_network *gsmnet; - struct e1inp_line *line; - - tall_bs11cfg_ctx = talloc_named_const(NULL, 0, "bs11-config"); - msgb_talloc_ctx_init(tall_bs11cfg_ctx, 0); - - osmo_init_logging(&log_info); - handle_options(argc, argv); - bts_model_bs11_init(); - - gsmnet = bsc_network_init(tall_bs11cfg_ctx, 1, 1, NULL); - if (!gsmnet) { - fprintf(stderr, "Unable to allocate gsm network\n"); - exit(1); - } - g_bts = gsm_bts_alloc_register(gsmnet, GSM_BTS_TYPE_BS11, - HARDCODED_BSIC); - - /* Override existing OML callback handler to set our own. */ - g_bts->model->oml_rcvmsg = abis_nm_bs11cfg_rcvmsg; - - libosmo_abis_init(tall_bs11cfg_ctx); - - /* Initialize virtual E1 line over rs232. */ - line = talloc_zero(tall_bs11cfg_ctx, struct e1inp_line); - if (!line) { - fprintf(stderr, "Unable to allocate memory for virtual E1 line\n"); - exit(1); - } - /* set the serial port. */ - bs11cfg_e1inp_line_ops.cfg.rs232.port = serial_port; - bs11cfg_e1inp_line_ops.cfg.rs232.delay = delay_ms; - - line->driver = e1inp_driver_find("rs232"); - if (!line->driver) { - fprintf(stderr, "cannot find `rs232' driver, giving up.\n"); - exit(1); - } - e1inp_line_bind_ops(line, &bs11cfg_e1inp_line_ops); - - /* configure and create signalling link for OML. */ - e1inp_ts_config_sign(&line->ts[0], line); - g_bts->oml_link = oml_link = - e1inp_sign_link_create(&line->ts[0], E1INP_SIGN_OML, - g_bts->c0, TEI_OML, 0); - - e1inp_line_update(line); - - signal(SIGINT, &signal_handler); - - abis_nm_bs11_factory_logon(g_bts, 1); - //abis_nm_bs11_get_serno(g_bts); - - osmo_timer_setup(&status_timer, status_timer_cb, NULL); - - while (1) { - if (osmo_select_main(0) < 0) - break; - } - - abis_nm_bs11_factory_logon(g_bts, 0); - - exit(0); -} diff --git a/openbsc/src/utils/isdnsync.c b/openbsc/src/utils/isdnsync.c deleted file mode 100644 index cc8ff672..00000000 --- a/openbsc/src/utils/isdnsync.c +++ /dev/null @@ -1,189 +0,0 @@ -/* isdnsync.c - * - * Author Andreas Eversberg - * - * All rights reserved - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation; either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - * - */ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "mISDNif.h" -#define MISDN_OLD_AF_COMPATIBILITY -#define AF_COMPATIBILITY_FUNC -#include "compat_af_isdn.h" - -int card = 0; -int sock = -1; - -int mISDN_open(void) -{ - int fd, ret; - struct mISDN_devinfo devinfo; - struct sockaddr_mISDN l2addr; - - fd = socket(PF_ISDN, SOCK_RAW, ISDN_P_BASE); - if (fd < 0) { - fprintf(stderr, "could not open socket (%s)\n", strerror(errno)); - return fd; - } - devinfo.id = card; - ret = ioctl(fd, IMGETDEVINFO, &devinfo); - if (ret < 0) { - fprintf(stderr,"could not send IOCTL IMGETCOUNT (%s)\n", strerror(errno)); - close(fd); - return ret; - } - close(fd); - if (!(devinfo.Dprotocols & (1 << ISDN_P_TE_S0)) - && !(devinfo.Dprotocols & (1 << ISDN_P_TE_E1))) { - fprintf(stderr,"Interface does not support TE mode (%s)\n", strerror(errno)); - return ret; - } - fd = socket(PF_ISDN, SOCK_DGRAM, ISDN_P_LAPD_TE); - if (fd < 0) { - fprintf(stderr,"could not open ISDN_P_LAPD_TE socket (%s)\n", strerror(errno)); - return fd; - } - l2addr.family = AF_ISDN; - l2addr.dev = card; - l2addr.channel = 0; - l2addr.sapi = 0; - l2addr.tei = 0; - ret = bind(fd, (struct sockaddr *)&l2addr, sizeof(l2addr)); - if (ret < 0) { - fprintf(stderr,"could not bind socket for card %d (%s)\n", card, strerror(errno)); - close(fd); - return ret; - } - sock = fd; - - return sock; -} - - -void mISDN_handle(void) -{ - int ret; - fd_set rfd; - struct timeval tv; - struct sockaddr_mISDN addr; - socklen_t alen; - unsigned char buffer[2048]; - struct mISDNhead *hh = (struct mISDNhead *)buffer; - int l1 = 0, l2 = 0, tei = 0; - - while(1) { -again: - FD_ZERO(&rfd); - FD_SET(sock, &rfd); - tv.tv_sec = 2; - tv.tv_usec = 0; - ret = select(sock+1, &rfd, NULL, NULL, &tv); - if (ret < 0) { - if (errno == EINTR) - continue; - fprintf(stderr, "%s aborted: %s\n", __FUNCTION__, strerror(errno)); - break; - } - if (FD_ISSET(sock, &rfd)) { - alen = sizeof(addr); - ret = recvfrom(sock, buffer, sizeof(buffer), 0, (struct sockaddr *) &addr, &alen); - if (ret < 0) { - fprintf(stderr, "%s read socket error %s\n", __FUNCTION__, strerror(errno)); - } else if (ret < MISDN_HEADER_LEN) { - fprintf(stderr, "%s read socket shor frame\n", __FUNCTION__); - } else { - switch(hh->prim) { - case MPH_ACTIVATE_IND: - case PH_ACTIVATE_IND: - if (!l1) { - printf("PH_ACTIVATE\n"); - printf("*** Sync available from interface :-)\n"); - l1 = 1; - } - goto again; - break; - case MPH_DEACTIVATE_IND: - case PH_DEACTIVATE_IND: - if (l1) { - printf("PH_DEACTIVATE\n"); - printf("*** Lost sync on interface :-(\n"); - l1 = 0; - } - goto again; - break; - case DL_ESTABLISH_IND: - case DL_ESTABLISH_CNF: - printf("DL_ESTABLISH\n"); - l2 = 1; - goto again; - break; - case DL_RELEASE_IND: - case DL_RELEASE_CNF: - printf("DL_RELEASE\n"); - l2 = 0; - goto again; - break; - case DL_INFORMATION_IND: - printf("DL_INFORMATION (tei %d sapi %d)\n", addr.tei, addr.sapi); - tei = 1; - break; - default: -// printf("prim %x\n", hh->prim); - goto again; - } - } - } - if (tei && !l2) { - hh->prim = DL_ESTABLISH_REQ; - printf("-> activating layer 2\n"); - sendto(sock, buffer, MISDN_HEADER_LEN, 0, (struct sockaddr *) &addr, alen); - } - } -} - -int main(int argc, char *argv[]) -{ - int ret; - - if (argc <= 1) - { - printf("Usage: %s \n\n", argv[0]); - printf("Opens given card number in TE-mode PTP and tries to keep layer 2 established.\n"); - printf("This keeps layer 1 activated to retrieve a steady sync signal from network.\n"); - return(0); - } - - card = atoi(argv[1]); - - init_af_isdn(); - - if ((ret = mISDN_open() < 0)) - return(ret); - - mISDN_handle(); - - close(sock); - - return 0; -} diff --git a/openbsc/src/utils/meas_db.c b/openbsc/src/utils/meas_db.c deleted file mode 100644 index d81efcad..00000000 --- a/openbsc/src/utils/meas_db.c +++ /dev/null @@ -1,330 +0,0 @@ -/* Routines for storing measurement reports in SQLite3 database */ - -/* (C) 2012 by Harald Welte - * - * All Rights Reserved - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation; either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public 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 "meas_db.h" - -#define INS_MR "INSERT INTO meas_rep (time, imsi, name, scenario, nr, bs_power, ms_timing_offset, fpc, ms_l1_pwr, ms_l1_ta) VALUES (?,?,?,?,?,?,?,?,?,?)" -#define INS_UD "INSERT INTO meas_rep_unidir (meas_id, rx_lev_full, rx_lev_sub, rx_qual_full, rx_qual_sub, dtx, uplink) VALUES (?,?,?,?,?,?,?)" -#define UPD_MR "UPDATE meas_rep SET ul_unidir=?, dl_unidir=? WHERE id=?" - -struct meas_db_state { - sqlite3 *db; - sqlite3_stmt *stmt_ins_ud; - sqlite3_stmt *stmt_ins_mr; - sqlite3_stmt *stmt_upd_mr; -}; - -/* macros to check for SQLite3 result codes */ -#define _SCK_OK(db, call, exp) \ - do { \ - int rc = call; \ - if (rc != exp) { \ - fprintf(stderr,"SQL Error in line %u: %s\n", \ - __LINE__, sqlite3_errmsg(db)); \ - goto err_io; \ - } \ - } while (0) -#define SCK_OK(db, call) _SCK_OK(db, call, SQLITE_OK) -#define SCK_DONE(db, call) _SCK_OK(db, call, SQLITE_DONE) - -static int _insert_ud(struct meas_db_state *st, unsigned long meas_id, int dtx, - int uplink, const struct gsm_meas_rep_unidir *ud) -{ - unsigned long rowid; - - SCK_OK(st->db, sqlite3_bind_int(st->stmt_ins_ud, 1, meas_id)); - SCK_OK(st->db, sqlite3_bind_int(st->stmt_ins_ud, 2, - rxlev2dbm(ud->full.rx_lev))); - SCK_OK(st->db, sqlite3_bind_int(st->stmt_ins_ud, 3, - rxlev2dbm(ud->sub.rx_lev))); - SCK_OK(st->db, sqlite3_bind_int(st->stmt_ins_ud, 4, ud->full.rx_qual)); - SCK_OK(st->db, sqlite3_bind_int(st->stmt_ins_ud, 5, ud->sub.rx_qual)); - SCK_OK(st->db, sqlite3_bind_int(st->stmt_ins_ud, 6, dtx)); - SCK_OK(st->db, sqlite3_bind_int(st->stmt_ins_ud, 7, uplink)); - - SCK_DONE(st->db, sqlite3_step(st->stmt_ins_ud)); - - SCK_OK(st->db, sqlite3_reset(st->stmt_ins_ud)); - - return sqlite3_last_insert_rowid(st->db); -err_io: - exit(1); -} - -/* insert a measurement report into the database */ -int meas_db_insert(struct meas_db_state *st, const char *imsi, - const char *name, unsigned long timestamp, - const char *scenario, - const struct gsm_meas_rep *mr) -{ - int rc; - sqlite3_int64 rowid, ul_rowid, dl_rowid; - - SCK_OK(st->db, sqlite3_bind_int(st->stmt_ins_mr, 1, timestamp)); - - if (imsi) - SCK_OK(st->db, sqlite3_bind_text(st->stmt_ins_mr, 2, - imsi, -1, SQLITE_STATIC)); - else - SCK_OK(st->db, sqlite3_bind_null(st->stmt_ins_mr, 2)); - - if (name) - SCK_OK(st->db, sqlite3_bind_text(st->stmt_ins_mr, 3, - name, -1, SQLITE_STATIC)); - else - SCK_OK(st->db, sqlite3_bind_null(st->stmt_ins_mr, 3)); - - if (scenario) - SCK_OK(st->db, sqlite3_bind_text(st->stmt_ins_mr, 4, - scenario, -1, SQLITE_STATIC)); - else - SCK_OK(st->db, sqlite3_bind_null(st->stmt_ins_mr, 4)); - - - SCK_OK(st->db, sqlite3_bind_int(st->stmt_ins_mr, 5, mr->nr)); - SCK_OK(st->db, sqlite3_bind_int(st->stmt_ins_mr, 6, mr->bs_power)); - - if (mr->flags & MEAS_REP_F_MS_TO) - SCK_OK(st->db, sqlite3_bind_int(st->stmt_ins_mr, 7, mr->ms_timing_offset)); - else - SCK_OK(st->db, sqlite3_bind_null(st->stmt_ins_mr, 7)); - - if (mr->flags & MEAS_REP_F_FPC) - SCK_OK(st->db, sqlite3_bind_int(st->stmt_ins_mr, 8, 1)); - else - SCK_OK(st->db, sqlite3_bind_int(st->stmt_ins_mr, 8, 0)); - - if (mr->flags & MEAS_REP_F_MS_L1) { - SCK_OK(st->db, sqlite3_bind_int(st->stmt_ins_mr, 9, - mr->ms_l1.pwr)); - SCK_OK(st->db, sqlite3_bind_int(st->stmt_ins_mr, 10, - mr->ms_l1.ta)); - } - - SCK_DONE(st->db, sqlite3_step(st->stmt_ins_mr)); - SCK_OK(st->db, sqlite3_reset(st->stmt_ins_mr)); - - rowid = sqlite3_last_insert_rowid(st->db); - - /* insert uplink measurement */ - ul_rowid = _insert_ud(st, rowid, mr->flags & MEAS_REP_F_UL_DTX, - 1, &mr->ul); - SCK_OK(st->db, sqlite3_bind_int(st->stmt_upd_mr, 1, ul_rowid)); - - /* insert downlink measurement, if present */ - if (mr->flags & MEAS_REP_F_DL_VALID) { - dl_rowid = _insert_ud(st, rowid, mr->flags & MEAS_REP_F_DL_DTX, - 0, &mr->dl); - SCK_OK(st->db, sqlite3_bind_int(st->stmt_upd_mr, 2, dl_rowid)); - } else - SCK_OK(st->db, sqlite3_bind_null(st->stmt_upd_mr, 2)); - - /* update meas_rep with the id's of the unidirectional - * measurements */ - SCK_OK(st->db, sqlite3_bind_int(st->stmt_upd_mr, 3, rowid)); - SCK_DONE(st->db, sqlite3_step(st->stmt_upd_mr)); - SCK_OK(st->db, sqlite3_reset(st->stmt_upd_mr)); - - return 0; - -err_io: - return -EIO; -} - -int meas_db_begin(struct meas_db_state *st) -{ - SCK_OK(st->db, sqlite3_exec(st->db, "BEGIN", NULL, NULL, NULL)); - - return 0; - -err_io: - return -EIO; -} - -int meas_db_commit(struct meas_db_state *st) -{ - SCK_OK(st->db, sqlite3_exec(st->db, "COMMIT", NULL, NULL, NULL)); - - return 0; - -err_io: - return -EIO; -} - -static const char *create_stmts[] = { - "CREATE TABLE IF NOT EXISTS meas_rep (" - "id INTEGER PRIMARY KEY AUTOINCREMENT," - "time TIMESTAMP," - "imsi TEXT," - "name TEXT," - "scenario TEXT," - "nr INTEGER," - "bs_power INTEGER NOT NULL," - "ms_timing_offset INTEGER," - "fpc INTEGER NOT NULL DEFAULT 0," - "ul_unidir INTEGER REFERENCES meas_rep_unidir(id)," - "dl_unidir INTEGER REFERENCES meas_rep_unidir(id)," - "ms_l1_pwr INTEGER," - "ms_l1_ta INTEGER" - ")", - "CREATE TABLE IF NOT EXISTS meas_rep_unidir (" - "id INTEGER PRIMARY KEY AUTOINCREMENT," - "meas_id INTEGER NOT NULL REFERENCES meas_rep(id)," - "rx_lev_full INTEGER NOT NULL," - "rx_lev_sub INTEGER NOT NULL," - "rx_qual_full INTEGER NOT NULL," - "rx_qual_sub INTEGER NOT NULL," - "dtx BOOLEAN NOT NULL DEFAULT 0," - "uplink BOOLEAN NOT NULL" - ")", - "CREATE VIEW IF NOT EXISTS path_loss AS " - "SELECT " - "meas_rep.id, " - "datetime(time,'unixepoch') AS timestamp, " - "imsi, " - "name, " - "scenario, " - "ms_timing_offset, " - "ms_l1_ta, " - "fpc, " - "ms_l1_pwr, " - "ud_ul.rx_lev_full AS ul_rx_lev_full, " - "ms_l1_pwr-ud_ul.rx_lev_full AS ul_path_loss_full, " - "ud_ul.rx_lev_sub ul_rx_lev_sub, " - "ms_l1_pwr-ud_ul.rx_lev_sub AS ul_path_loss_sub, " - "ud_ul.rx_qual_full AS ul_rx_qual_full, " - "ud_ul.rx_qual_sub AS ul_rx_qual_sub, " - "bs_power, " - "ud_dl.rx_lev_full AS dl_rx_lev_full, " - "bs_power-ud_dl.rx_lev_full AS dl_path_loss_full, " - "ud_dl.rx_lev_sub AS dl_rx_lev_sub, " - "bs_power-ud_dl.rx_lev_sub AS dl_path_loss_sub, " - "ud_dl.rx_qual_full AS dl_rx_qual_full, " - "ud_dl.rx_qual_sub AS dl_rx_qual_sub " - "FROM " - "meas_rep, " - "meas_rep_unidir AS ud_dl, " - "meas_rep_unidir AS ud_ul " - "WHERE " - "ud_ul.id = meas_rep.ul_unidir AND " - "ud_dl.id = meas_rep.dl_unidir", - "CREATE VIEW IF NOT EXISTS overview AS " - "SELECT " - "id," - "timestamp," - "imsi," - "name," - "scenario," - "ms_l1_pwr," - "ul_rx_lev_full," - "ul_path_loss_full," - "ul_rx_qual_full," - "bs_power," - "dl_rx_lev_full," - "dl_path_loss_full," - "dl_rx_qual_full " - "FROM path_loss", -}; - -static int check_create_tbl(struct meas_db_state *st) -{ - int i, rc; - - for (i = 0; i < ARRAY_SIZE(create_stmts); i++) { - SCK_OK(st->db, sqlite3_exec(st->db, create_stmts[i], - NULL, NULL, NULL)); - } - - return 0; -err_io: - return -EIO; -} - - -#define PREP_CHK(db, stmt, ptr) \ - do { \ - int rc; \ - rc = sqlite3_prepare_v2(db, stmt, strlen(stmt)+1, \ - ptr, NULL); \ - if (rc != SQLITE_OK) { \ - fprintf(stderr, "Error during prepare of '%s': %s\n", \ - stmt, sqlite3_errmsg(db)); \ - goto err_io; \ - } \ - } while (0) - -struct meas_db_state *meas_db_open(void *ctx, const char *fname) -{ - int rc; - struct meas_db_state *st = talloc_zero(ctx, struct meas_db_state); - - if (!st) - return NULL; - - rc = sqlite3_open_v2(fname, &st->db, - SQLITE_OPEN_READWRITE|SQLITE_OPEN_CREATE, - NULL); - if (rc != SQLITE_OK) { - fprintf(stderr, "Unable to open DB: %s\n", - sqlite3_errmsg(st->db)); - goto err_io; - } - - rc = check_create_tbl(st); - - PREP_CHK(st->db, INS_MR, &st->stmt_ins_mr); - PREP_CHK(st->db, INS_UD, &st->stmt_ins_ud); - PREP_CHK(st->db, UPD_MR, &st->stmt_upd_mr); - - return st; -err_io: - talloc_free(st); - return NULL; -} - -void meas_db_close(struct meas_db_state *st) -{ - if (sqlite3_finalize(st->stmt_ins_mr) != SQLITE_OK) - fprintf(stderr, "DB insert measurement report finalize error: %s\n", - sqlite3_errmsg(st->db)); - if (sqlite3_finalize(st->stmt_ins_ud) != SQLITE_OK) - fprintf(stderr, "DB insert unidir finalize error: %s\n", - sqlite3_errmsg(st->db)); - if (sqlite3_finalize(st->stmt_upd_mr) != SQLITE_OK) - fprintf(stderr, "DB update measurement report finalize error: %s\n", - sqlite3_errmsg(st->db)); - if (sqlite3_close(st->db) != SQLITE_OK) - fprintf(stderr, "Unable to close DB, abandoning.\n"); - - talloc_free(st); - -} diff --git a/openbsc/src/utils/meas_db.h b/openbsc/src/utils/meas_db.h deleted file mode 100644 index 889e9022..00000000 --- a/openbsc/src/utils/meas_db.h +++ /dev/null @@ -1,17 +0,0 @@ -#ifndef OPENBSC_MEAS_DB_H -#define OPENBSC_MEAS_DB_H - -struct meas_db_state; - -struct meas_db_state *meas_db_open(void *ctx, const char *fname); -void meas_db_close(struct meas_db_state *st); - -int meas_db_begin(struct meas_db_state *st); -int meas_db_commit(struct meas_db_state *st); - -int meas_db_insert(struct meas_db_state *st, const char *imsi, - const char *name, unsigned long timestamp, - const char *scenario, - const struct gsm_meas_rep *mr); - -#endif diff --git a/openbsc/src/utils/meas_json.c b/openbsc/src/utils/meas_json.c deleted file mode 100644 index 51eb6c74..00000000 --- a/openbsc/src/utils/meas_json.c +++ /dev/null @@ -1,190 +0,0 @@ -/* Convert measurement report feed into JSON feed printed to stdout. - * Each measurement report is printed as a separae JSON root entry. - * All measurement reports are separated by a new line. - */ - -/* (C) 2015 by Alexander Chemeris - * With parts of code adopted from different places in OpenBSC. - * - * All Rights Reserved - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation; either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - * - */ -#include -#include -#include -#include -#include -#include - -#include - -#include -#include -#include - -#include - -#include -#include -#include - -static void print_meas_rep_uni_json(struct gsm_meas_rep_unidir *mru) -{ - printf("\"RXL-FULL\":%d, \"RXL-SUB\":%d, ", - rxlev2dbm(mru->full.rx_lev), - rxlev2dbm(mru->sub.rx_lev)); - printf("\"RXQ-FULL\":%d, \"RXQ-SUB\":%d", - mru->full.rx_qual, mru->sub.rx_qual); -} - -static void print_meas_rep_json(struct gsm_meas_rep *mr) -{ - int i; - - printf("\"NR\":%d", mr->nr); - - if (mr->flags & MEAS_REP_F_DL_DTX) - printf(", \"DTXd\":true"); - - printf(", \"UL_MEAS\":{"); - print_meas_rep_uni_json(&mr->ul); - printf("}"); - printf(", \"BS_POWER\":%d", mr->bs_power); - if (mr->flags & MEAS_REP_F_MS_TO) - printf(", \"MS_TO\":%d", mr->ms_timing_offset); - - if (mr->flags & MEAS_REP_F_MS_L1) { - printf(", \"L1_MS_PWR\":%d", mr->ms_l1.pwr); - printf(", \"L1_FPC\":%s", - mr->flags & MEAS_REP_F_FPC ? "true" : "false"); - printf(", \"L1_TA\":%u", mr->ms_l1.ta); - } - - if (mr->flags & MEAS_REP_F_UL_DTX) - printf(", \"DTXu\":true"); - if (mr->flags & MEAS_REP_F_BA1) - printf(", \"BA1\":true"); - if (mr->flags & MEAS_REP_F_DL_VALID) { - printf(", \"DL_MEAS\":{"); - print_meas_rep_uni_json(&mr->dl); - printf("}"); - } - - if (mr->num_cell == 7) - return; - printf(", \"NUM_NEIGH\":%u, \"NEIGH\":[", mr->num_cell); - for (i = 0; i < mr->num_cell; i++) { - struct gsm_meas_rep_cell *mrc = &mr->cell[i]; - if (i!=0) printf(", "); - printf("{\"IDX\":%u, \"ARFCN\":%u, \"BSIC\":%u, \"POWER\":%d}", - mrc->neigh_idx, mrc->arfcn, mrc->bsic, rxlev2dbm(mrc->rxlev)); - } - printf("]"); -} - -static void print_chan_info_json(struct meas_feed_meas *mfm) -{ - printf("\"lchan_type\":\"%s\", \"pchan_type\":\"%s\", " - "\"bts_nr\":%d, \"trx_nr\":%d, \"ts_nr\":%d, \"ss_nr\":%d", - gsm_lchant_name(mfm->lchan_type), gsm_pchan_name(mfm->pchan_type), - mfm->bts_nr, mfm->trx_nr, mfm->ts_nr, mfm->ss_nr); -} - -static void print_meas_feed_json(struct meas_feed_meas *mfm) -{ - time_t now = time(NULL); - - printf("{"); - printf("\"time\":%ld, \"imsi\":\"%s\", \"name\":\"%s\", \"scenario\":\"%s\", ", - now, mfm->imsi, mfm->name, mfm->scenario); - - switch (mfm->hdr.version) { - case 1: - printf("\"chan_info\":{"); - print_chan_info_json(mfm); - printf("}, "); - /* no break, fall to version 0 */ - case 0: - printf("\"meas_rep\":{"); - print_meas_rep_json(&mfm->mr); - printf("}"); - break; - } - - printf("}\n"); - -} - -static int handle_meas(struct msgb *msg) -{ - struct meas_feed_meas *mfm = (struct meas_feed_meas *) msgb_data(msg); - - print_meas_feed_json(mfm); - - return 0; -} - -static int handle_msg(struct msgb *msg) -{ - struct meas_feed_hdr *mfh = (struct meas_feed_hdr *) msgb_data(msg); - - if (mfh->version != MEAS_FEED_VERSION) - return -EINVAL; - - switch (mfh->msg_type) { - case MEAS_FEED_MEAS: - handle_meas(msg); - break; - default: - break; - } - return 0; -} - -static int udp_fd_cb(struct osmo_fd *ofd, unsigned int what) -{ - int rc; - - if (what & BSC_FD_READ) { - struct msgb *msg = msgb_alloc(1024, "UDP Rx"); - - rc = read(ofd->fd, msgb_data(msg), msgb_tailroom(msg)); - if (rc < 0) - return rc; - msgb_put(msg, rc); - handle_msg(msg); - msgb_free(msg); - } - - return 0; -} - -int main(int argc, char **argv) -{ - int rc; - struct osmo_fd udp_ofd; - - udp_ofd.cb = udp_fd_cb; - rc = osmo_sock_init_ofd(&udp_ofd, AF_INET, SOCK_DGRAM, IPPROTO_UDP, NULL, 8888, OSMO_SOCK_F_BIND); - if (rc < 0) - exit(1); - - while (1) { - osmo_select_main(0); - }; - - exit(0); -} diff --git a/openbsc/src/utils/meas_pcap2db.c b/openbsc/src/utils/meas_pcap2db.c deleted file mode 100644 index b874ac40..00000000 --- a/openbsc/src/utils/meas_pcap2db.c +++ /dev/null @@ -1,138 +0,0 @@ -/* read PCAP file with meas_feed data and write it to sqlite3 database */ - -/* (C) 2012 by Harald Welte - * - * All Rights Reserved - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation; either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public 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 "meas_db.h" - -static struct meas_db_state *db; - -static void handle_mfm(const struct pcap_pkthdr *h, - const struct meas_feed_meas *mfm) -{ - const char *scenario; - - if (strlen(mfm->scenario)) - scenario = mfm->scenario; - else - scenario = NULL; - - meas_db_insert(db, mfm->imsi, mfm->name, h->ts.tv_sec, - scenario, &mfm->mr); -} - -static void pcap_cb(u_char *user, const struct pcap_pkthdr *h, - const u_char *bytes) -{ - const char *cur = bytes; - const struct iphdr *ip; - const struct udphdr *udp; - const struct meas_feed_meas *mfm; - uint16_t udplen; - - if (h->caplen < 14+20+8) - return; - - /* Check if there is IPv4 in the Ethernet */ - if (cur[12] != 0x08 || cur[13] != 0x00) - return; - - cur += 14; /* ethernet header */ - ip = (struct iphdr *) cur; - - if (ip->version != 4) - return; - cur += ip->ihl * 4; - - if (ip->protocol != IPPROTO_UDP) - return; - - udp = (struct udphdr *) cur; - - if (udp->dest != htons(8888)) - return; - - udplen = ntohs(udp->len); - if (udplen != sizeof(*udp) + sizeof(*mfm)) - return; - cur += sizeof(*udp); - - mfm = (const struct meas_feed_meas *) cur; - - handle_mfm(h, mfm); -} - -int main(int argc, char **argv) -{ - char errbuf[PCAP_ERRBUF_SIZE+1]; - char *pcap_fname, *db_fname; - pcap_t *pc; - int rc; - - if (argc < 3) { - fprintf(stderr, "You need to specify PCAP and database file\n"); - exit(2); - } - - pcap_fname = argv[1]; - db_fname = argv[2]; - - pc = pcap_open_offline(pcap_fname, errbuf); - if (!pc) { - fprintf(stderr, "Cannot open %s: %s\n", pcap_fname, errbuf); - exit(1); - } - - db = meas_db_open(NULL, db_fname); - if (!db) - exit(0); - - rc = meas_db_begin(db); - if (rc < 0) { - fprintf(stderr, "Error during BEGIN\n"); - exit(1); - } - - pcap_loop(pc, 0 , pcap_cb, NULL); - - meas_db_commit(db); - - exit(0); -} diff --git a/openbsc/src/utils/meas_udp2db.c b/openbsc/src/utils/meas_udp2db.c deleted file mode 100644 index 5032d0c3..00000000 --- a/openbsc/src/utils/meas_udp2db.c +++ /dev/null @@ -1,126 +0,0 @@ -/* liesten to meas_feed on UDP and write it to sqlite3 database */ - -/* (C) 2012 by Harald Welte - * - * All Rights Reserved - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation; either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public 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 "meas_db.h" - -static struct osmo_fd udp_ofd; -static struct meas_db_state *db; - -static int handle_msg(struct msgb *msg) -{ - struct meas_feed_hdr *mfh = (struct meas_feed_hdr *) msgb_data(msg); - struct meas_feed_meas *mfm = (struct meas_feed_meas *) msgb_data(msg); - const char *scenario; - time_t now = time(NULL); - - if (mfh->version != MEAS_FEED_VERSION) - return -EINVAL; - - if (mfh->msg_type != MEAS_FEED_MEAS) - return -EINVAL; - - if (strlen(mfm->scenario)) - scenario = mfm->scenario; - else - scenario = NULL; - - meas_db_insert(db, mfm->imsi, mfm->name, now, - scenario, &mfm->mr); - - return 0; -} - -static int udp_fd_cb(struct osmo_fd *ofd, unsigned int what) -{ - int rc; - - if (what & BSC_FD_READ) { - struct msgb *msg = msgb_alloc(1024, "UDP Rx"); - - rc = read(ofd->fd, msgb_data(msg), msgb_tailroom(msg)); - if (rc < 0) - return rc; - msgb_put(msg, rc); - handle_msg(msg); - msgb_free(msg); - } - - return 0; -} - -int main(int argc, char **argv) -{ - char *db_fname; - int rc; - - msgb_talloc_ctx_init(NULL, 0); - - if (argc < 2) { - fprintf(stderr, "You have to specify the database file name\n"); - exit(2); - } - - db_fname = argv[1]; - - udp_ofd.cb = udp_fd_cb; - rc = osmo_sock_init_ofd(&udp_ofd, AF_INET, SOCK_DGRAM, - IPPROTO_UDP, NULL, 8888, OSMO_SOCK_F_BIND); - if (rc < 0) { - fprintf(stderr, "Unable to create UDP listen socket\n"); - exit(1); - } - - db = meas_db_open(NULL, db_fname); - if (!db) { - fprintf(stderr, "Unable to open database\n"); - exit(1); - } - - /* FIXME: timer-based BEGIN/COMMIT */ - - while (1) { - osmo_select_main(0); - }; - - meas_db_close(db); - - exit(0); -} - diff --git a/openbsc/src/utils/meas_vis.c b/openbsc/src/utils/meas_vis.c deleted file mode 100644 index 77194ded..00000000 --- a/openbsc/src/utils/meas_vis.c +++ /dev/null @@ -1,310 +0,0 @@ -#include -#include -#include -#include -#include - -#include - -#include - -#include -#include -#include -#include -#include - -#include - -#include - -struct ms_state_uni { - CDKSLIDER *cdk; - CDKLABEL *cdk_label; - - time_t last_update; - char label[32]; - char *_lbl[1]; -}; - - -struct ms_state { - struct llist_head list; - - char name[31+1]; - char imsi[15+1]; - struct gsm_meas_rep mr; - - struct ms_state_uni ul; - struct ms_state_uni dl; -}; - -struct state { - struct osmo_fd udp_ofd; - struct llist_head ms_list; - - CDKSCREEN *cdkscreen; - WINDOW *curses_win; - - CDKLABEL *cdk_title; - char *title; - - CDKLABEL *cdk_header; - char header[256]; -}; - -static struct state g_st; - -struct ms_state *find_ms(const char *imsi) -{ - struct ms_state *ms; - - llist_for_each_entry(ms, &g_st.ms_list, list) { - if (!strcmp(ms->imsi, imsi)) - return ms; - } - return NULL; -} - -static struct ms_state *find_alloc_ms(const char *imsi) -{ - struct ms_state *ms; - - ms = find_ms(imsi); - if (!ms) { - ms = talloc_zero(NULL, struct ms_state); - osmo_strlcpy(ms->imsi, imsi, sizeof(ms->imsi)); - ms->ul._lbl[0] = ms->ul.label; - ms->dl._lbl[0] = ms->dl.label; - llist_add_tail(&ms->list, &g_st.ms_list); - } - - return ms; -} - -static int handle_meas(struct msgb *msg) -{ - struct meas_feed_meas *mfm = (struct meas_feed_meas *) msgb_data(msg); - struct ms_state *ms = find_alloc_ms(mfm->imsi); - time_t now = time(NULL); - - osmo_strlcpy(ms->name, mfm->name, sizeof(ms->name)); - memcpy(&ms->mr, &mfm->mr, sizeof(ms->mr)); - ms->ul.last_update = now; - if (ms->mr.flags & MEAS_REP_F_DL_VALID) - ms->dl.last_update = now; - - /* move to head of list */ - llist_del(&ms->list); - llist_add(&ms->list, &g_st.ms_list); - - return 0; -} - -static int handle_msg(struct msgb *msg) -{ - struct meas_feed_hdr *mfh = (struct meas_feed_hdr *) msgb_data(msg); - - if (mfh->version != MEAS_FEED_VERSION) - return -EINVAL; - - switch (mfh->msg_type) { - case MEAS_FEED_MEAS: - handle_meas(msg); - break; - default: - break; - } - - return 0; -} - -static int udp_fd_cb(struct osmo_fd *ofd, unsigned int what) -{ - int rc; - - if (what & BSC_FD_READ) { - struct msgb *msg = msgb_alloc(1024, "UDP Rx"); - - rc = read(ofd->fd, msgb_data(msg), msgb_tailroom(msg)); - if (rc < 0) - return rc; - msgb_put(msg, rc); - handle_msg(msg); - msgb_free(msg); - } - - return 0; -} - - -static void destroy_dir(struct ms_state_uni *uni) -{ - if (uni->cdk) { - destroyCDKSlider(uni->cdk); - uni->cdk = NULL; - } - if (uni->cdk_label) { - destroyCDKLabel(uni->cdk_label); - uni->cdk_label = NULL; - } -} - -#define DIR_UL 0 -#define DIR_DL 1 -static const char *dir_str[2] = { - [DIR_UL] = "UL", - [DIR_DL] = "DL", -}; - -static int colpair_by_qual(uint8_t rx_qual) -{ - if (rx_qual == 0) - return 24; - else if (rx_qual <= 4) - return 32; - else - return 16; -} - -static int colpair_by_lev(int rx_lev) -{ - if (rx_lev < -95) - return 16; - else if (rx_lev < -80) - return 32; - else - return 24; -} - - -void write_uni(struct ms_state *ms, struct ms_state_uni *msu, - struct gsm_rx_lev_qual *lq, int dir, int row) -{ - - char label[128]; - time_t now = time(NULL); - int qual_col = colpair_by_qual(lq->rx_qual); - int lev_col = colpair_by_lev(rxlev2dbm(lq->rx_lev)); - int color, pwr; - - if (dir == DIR_UL) { - pwr = ms->mr.ms_l1.pwr; - } else { - pwr = ms->mr.bs_power; - } - - color = A_REVERSE | COLOR_PAIR(lev_col) | ' '; - snprintf(label, sizeof(label), "%s %s ", ms->imsi, dir_str[dir]); - msu->cdk = newCDKSlider(g_st.cdkscreen, 0, row, NULL, label, color, - COLS-40, rxlev2dbm(lq->rx_lev), -110, -47, - 1, 2, FALSE, FALSE); - //IsVisibleObj(ms->ul.cdk) = FALSE; - snprintf(msu->label, sizeof(msu->label), "%1d %3d %2u %2d %4u", - qual_col, lq->rx_qual, qual_col, pwr, - ms->mr.ms_l1.ta, ms->mr.ms_timing_offset, - now - msu->last_update); - msu->cdk_label = newCDKLabel(g_st.cdkscreen, RIGHT, row, - msu->_lbl, 1, FALSE, FALSE); -} - -static void update_sliders(void) -{ - int num_vis_sliders = 0; - struct ms_state *ms; -#define HEADER_LINES 2 - - /* remove all sliders */ - llist_for_each_entry(ms, &g_st.ms_list, list) { - destroy_dir(&ms->ul); - destroy_dir(&ms->dl); - - } - - llist_for_each_entry(ms, &g_st.ms_list, list) { - struct gsm_rx_lev_qual *lq; - unsigned int row = HEADER_LINES + num_vis_sliders*3; - - if (ms->mr.flags & MEAS_REP_F_UL_DTX) - lq = &ms->mr.ul.sub; - else - lq = &ms->mr.ul.full; - write_uni(ms, &ms->ul, lq, DIR_UL, row); - - if (ms->mr.flags & MEAS_REP_F_DL_DTX) - lq = &ms->mr.dl.sub; - else - lq = &ms->mr.dl.full; - write_uni(ms, &ms->dl, lq, DIR_DL, row+1); - - num_vis_sliders++; - if (num_vis_sliders >= LINES/3) - break; - } - - refreshCDKScreen(g_st.cdkscreen); - -} - -const struct value_string col_strs[] = { - { COLOR_WHITE, "white" }, - { COLOR_RED, "red" }, - { COLOR_GREEN, "green" }, - { COLOR_YELLOW, "yellow" }, - { COLOR_BLUE, "blue" }, - { COLOR_MAGENTA,"magenta" }, - { COLOR_CYAN, "cyan" }, - { COLOR_BLACK, "black" }, - { 0, NULL } -}; - -int main(int argc, char **argv) -{ - int rc; - char *header[1]; - char *title[1]; - - msgb_talloc_ctx_init(NULL, 0); - - printf("sizeof(gsm_meas_rep)=%u\n", sizeof(struct gsm_meas_rep)); - printf("sizeof(meas_feed_meas)=%u\n", sizeof(struct meas_feed_meas)); - - INIT_LLIST_HEAD(&g_st.ms_list); - g_st.curses_win = initscr(); - g_st.cdkscreen = initCDKScreen(g_st.curses_win); - initCDKColor(); - - g_st.title = "OpenBSC link quality monitor"; - title[0] = g_st.title; - g_st.cdk_title = newCDKLabel(g_st.cdkscreen, CENTER, 0, title, 1, FALSE, FALSE); - - snprintf(g_st.header, sizeof(g_st.header), "Q Pwr TA TO Time"); - header[0] = g_st.header; - g_st.cdk_header = newCDKLabel(g_st.cdkscreen, RIGHT, 1, header, 1, FALSE, FALSE); - -#if 0 - int i; - for (i = 0; i < 64; i++) { - short f, b; - pair_content(i, &f, &b); - attron(COLOR_PAIR(i)); - printw("%u: %u (%s) ", i, f, get_value_string(col_strs, f)); - printw("%u (%s)\n\r", b, get_value_string(col_strs, b)); - } - refresh(); - getch(); - exit(0); -#endif - - g_st.udp_ofd.cb = udp_fd_cb; - rc = osmo_sock_init_ofd(&g_st.udp_ofd, AF_INET, SOCK_DGRAM, IPPROTO_UDP, NULL, 8888, OSMO_SOCK_F_BIND); - if (rc < 0) - exit(1); - - while (1) { - osmo_select_main(0); - update_sliders(); - }; - - exit(0); -} diff --git a/openbsc/src/utils/smpp_mirror.c b/openbsc/src/utils/smpp_mirror.c deleted file mode 100644 index 95df5f2a..00000000 --- a/openbsc/src/utils/smpp_mirror.c +++ /dev/null @@ -1,329 +0,0 @@ -#include -#include -#include -#include -#include - -#include - -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include - -#include - -/* FIXME: merge with smpp_smsc.c */ -#define SMPP_SYS_ID_LEN 16 -enum esme_read_state { - READ_ST_IN_LEN = 0, - READ_ST_IN_MSG = 1, -}; -/* FIXME: merge with smpp_smsc.c */ - -struct esme { - struct osmo_fd ofd; - - uint32_t own_seq_nr; - - struct osmo_wqueue wqueue; - enum esme_read_state read_state; - uint32_t read_len; - uint32_t read_idx; - struct msgb *read_msg; - - uint8_t smpp_version; - char system_id[SMPP_SYS_ID_LEN+1]; - char password[SMPP_SYS_ID_LEN+1]; -}; - -/* FIXME: merge with smpp_smsc.c */ -#define SMPP34_UNPACK(rc, type, str, data, len) \ - memset(str, 0, sizeof(*str)); \ - rc = smpp34_unpack(type, str, data, len) -#define INIT_RESP(type, resp, req) { \ - memset((resp), 0, sizeof(*(resp))); \ - (resp)->command_length = 0; \ - (resp)->command_id = type; \ - (resp)->command_status = ESME_ROK; \ - (resp)->sequence_number = (req)->sequence_number; \ -} -#define PACK_AND_SEND(esme, ptr) pack_and_send(esme, (ptr)->command_id, ptr) -static inline uint32_t smpp_msgb_cmdid(struct msgb *msg) -{ - uint8_t *tmp = msgb_data(msg) + 4; - return ntohl(*(uint32_t *)tmp); -} -static uint32_t esme_inc_seq_nr(struct esme *esme) -{ - esme->own_seq_nr++; - if (esme->own_seq_nr > 0x7fffffff) - esme->own_seq_nr = 1; - - return esme->own_seq_nr; -} -static int pack_and_send(struct esme *esme, uint32_t type, void *ptr) -{ - struct msgb *msg = msgb_alloc(4096, "SMPP_Tx"); - int rc, rlen; - if (!msg) - return -ENOMEM; - - rc = smpp34_pack(type, msg->tail, msgb_tailroom(msg), &rlen, ptr); - if (rc != 0) { - LOGP(DSMPP, LOGL_ERROR, "[%s] Error during smpp34_pack(): %s\n", - esme->system_id, smpp34_strerror); - msgb_free(msg); - return -EINVAL; - } - msgb_put(msg, rlen); - - if (osmo_wqueue_enqueue(&esme->wqueue, msg) != 0) { - LOGP(DSMPP, LOGL_ERROR, "[%s] Write queue full. Dropping message\n", - esme->system_id); - msgb_free(msg); - return -EAGAIN; - } - return 0; -} -/* FIXME: merge with smpp_smsc.c */ - - -static int smpp_handle_deliver(struct esme *esme, struct msgb *msg) -{ - struct deliver_sm_t deliver; - struct deliver_sm_resp_t deliver_r; - struct submit_sm_t submit; - int rc; - - memset(&deliver, 0, sizeof(deliver)); - SMPP34_UNPACK(rc, DELIVER_SM, &deliver, msgb_data(msg), msgb_length(msg)); - if (rc < 0) - return rc; - - INIT_RESP(DELIVER_SM_RESP, &deliver_r, &deliver); - - PACK_AND_SEND(esme, &deliver_r); - - memset(&submit, 0, sizeof(submit)); - submit.command_id = SUBMIT_SM; - submit.command_status = ESME_ROK; - submit.sequence_number = esme_inc_seq_nr(esme); - - submit.dest_addr_ton = deliver.source_addr_ton; - submit.dest_addr_npi = deliver.source_addr_npi; - memcpy(submit.destination_addr, deliver.source_addr, - OSMO_MIN(sizeof(submit.destination_addr), - sizeof(deliver.source_addr))); - - submit.source_addr_ton = deliver.dest_addr_ton; - submit.source_addr_npi = deliver.dest_addr_npi; - memcpy(submit.source_addr, deliver.destination_addr, - OSMO_MIN(sizeof(submit.source_addr), - sizeof(deliver.destination_addr))); - - submit.esm_class = deliver.esm_class; - submit.protocol_id = deliver.protocol_id; - submit.priority_flag = deliver.priority_flag; - memcpy(submit.schedule_delivery_time, deliver.schedule_delivery_time, - OSMO_MIN(sizeof(submit.schedule_delivery_time), - sizeof(deliver.schedule_delivery_time))); - memcpy(submit.validity_period, deliver.validity_period, - OSMO_MIN(sizeof(submit.validity_period), - sizeof(deliver.validity_period))); - submit.registered_delivery = deliver.registered_delivery; - submit.replace_if_present_flag = deliver.replace_if_present_flag; - submit.data_coding = deliver.data_coding; - submit.sm_default_msg_id = deliver.sm_default_msg_id; - submit.sm_length = deliver.sm_length; - memcpy(submit.short_message, deliver.short_message, - OSMO_MIN(sizeof(submit.short_message), - sizeof(deliver.short_message))); - /* FIXME: TLV? */ - - return PACK_AND_SEND(esme, &submit); -} - -static int bind_transceiver(struct esme *esme) -{ - struct bind_transceiver_t bind; - - memset(&bind, 0, sizeof(bind)); - bind.command_id = BIND_TRANSCEIVER; - bind.sequence_number = esme_inc_seq_nr(esme); - snprintf((char *)bind.system_id, sizeof(bind.system_id), "%s", esme->system_id); - snprintf((char *)bind.password, sizeof(bind.password), "%s", esme->password); - snprintf((char *)bind.system_type, sizeof(bind.system_type), "mirror"); - bind.interface_version = esme->smpp_version; - - return PACK_AND_SEND(esme, &bind); -} - -static int smpp_pdu_rx(struct esme *esme, struct msgb *msg) -{ - uint32_t cmd_id = smpp_msgb_cmdid(msg); - int rc; - - switch (cmd_id) { - case DELIVER_SM: - rc = smpp_handle_deliver(esme, msg); - break; - default: - LOGP(DSMPP, LOGL_NOTICE, "unhandled case %d\n", cmd_id); - rc = 0; - break; - } - - return rc; -} - -/* FIXME: merge with smpp_smsc.c */ -static int esme_read_cb(struct osmo_fd *ofd) -{ - struct esme *esme = ofd->data; - uint32_t len; - uint8_t *lenptr = (uint8_t *) &len; - uint8_t *cur; - struct msgb *msg; - int rdlen; - int rc; - - switch (esme->read_state) { - case READ_ST_IN_LEN: - rdlen = sizeof(uint32_t) - esme->read_idx; - rc = read(ofd->fd, lenptr + esme->read_idx, rdlen); - if (rc < 0) { - LOGP(DSMPP, LOGL_ERROR, "[%s] read returned %d\n", - esme->system_id, rc); - } else if (rc == 0) { - goto dead_socket; - } else - esme->read_idx += rc; - if (esme->read_idx >= sizeof(uint32_t)) { - esme->read_len = ntohl(len); - msg = msgb_alloc(esme->read_len, "SMPP Rx"); - if (!msg) - return -ENOMEM; - esme->read_msg = msg; - cur = msgb_put(msg, sizeof(uint32_t)); - memcpy(cur, lenptr, sizeof(uint32_t)); - esme->read_state = READ_ST_IN_MSG; - esme->read_idx = sizeof(uint32_t); - } - break; - case READ_ST_IN_MSG: - msg = esme->read_msg; - rdlen = esme->read_len - esme->read_idx; - rc = read(ofd->fd, msg->tail, OSMO_MIN(rdlen, msgb_tailroom(msg))); - if (rc < 0) { - LOGP(DSMPP, LOGL_ERROR, "[%s] read returned %d\n", - esme->system_id, rc); - } else if (rc == 0) { - goto dead_socket; - } else { - esme->read_idx += rc; - msgb_put(msg, rc); - } - - if (esme->read_idx >= esme->read_len) { - rc = smpp_pdu_rx(esme, esme->read_msg); - esme->read_msg = NULL; - esme->read_idx = 0; - esme->read_len = 0; - esme->read_state = READ_ST_IN_LEN; - } - break; - } - - return 0; -dead_socket: - msgb_free(esme->read_msg); - osmo_fd_unregister(&esme->wqueue.bfd); - close(esme->wqueue.bfd.fd); - esme->wqueue.bfd.fd = -1; - exit(2342); - - return 0; -} - -static int esme_write_cb(struct osmo_fd *ofd, struct msgb *msg) -{ - struct esme *esme = ofd->data; - int rc; - - rc = write(ofd->fd, msgb_data(msg), msgb_length(msg)); - if (rc == 0) { - osmo_fd_unregister(&esme->wqueue.bfd); - close(esme->wqueue.bfd.fd); - esme->wqueue.bfd.fd = -1; - exit(99); - } else if (rc < msgb_length(msg)) { - LOGP(DSMPP, LOGL_ERROR, "[%s] Short write\n", esme->system_id); - return 0; - } - - return 0; -} - -static int smpp_esme_init(struct esme *esme, const char *host, uint16_t port) -{ - int rc; - - if (port == 0) - port = 2775; - - esme->own_seq_nr = rand(); - esme_inc_seq_nr(esme); - osmo_wqueue_init(&esme->wqueue, 10); - esme->wqueue.bfd.data = esme; - esme->wqueue.read_cb = esme_read_cb; - esme->wqueue.write_cb = esme_write_cb; - - rc = osmo_sock_init_ofd(&esme->wqueue.bfd, AF_UNSPEC, SOCK_STREAM, - IPPROTO_TCP, host, port, OSMO_SOCK_F_CONNECT); - if (rc < 0) - return rc; - - return bind_transceiver(esme); -} - - -int main(int argc, char **argv) -{ - struct esme esme; - char *host = "localhost"; - int port = 0; - int rc; - - msgb_talloc_ctx_init(NULL, 0); - - memset(&esme, 0, sizeof(esme)); - - osmo_init_logging(&log_info); - - snprintf((char *) esme.system_id, sizeof(esme.system_id), "mirror"); - snprintf((char *) esme.password, sizeof(esme.password), "mirror"); - esme.smpp_version = 0x34; - - if (argc >= 2) - host = argv[1]; - if (argc >= 3) - port = atoi(argv[2]); - - rc = smpp_esme_init(&esme, host, port); - if (rc < 0) - exit(1); - - while (1) { - osmo_select_main(0); - } - - exit(0); -} diff --git a/openbsc/osmoappdesc.py b/osmoappdesc.py similarity index 100% rename from openbsc/osmoappdesc.py rename to osmoappdesc.py diff --git a/openbsc/tests/Makefile.am b/tests/Makefile.am similarity index 100% rename from openbsc/tests/Makefile.am rename to tests/Makefile.am diff --git a/openbsc/tests/abis/Makefile.am b/tests/abis/Makefile.am similarity index 100% rename from openbsc/tests/abis/Makefile.am rename to tests/abis/Makefile.am diff --git a/openbsc/tests/abis/abis_test.c b/tests/abis/abis_test.c similarity index 100% rename from openbsc/tests/abis/abis_test.c rename to tests/abis/abis_test.c diff --git a/openbsc/tests/abis/abis_test.ok b/tests/abis/abis_test.ok similarity index 100% rename from openbsc/tests/abis/abis_test.ok rename to tests/abis/abis_test.ok diff --git a/openbsc/tests/atlocal.in b/tests/atlocal.in similarity index 100% rename from openbsc/tests/atlocal.in rename to tests/atlocal.in diff --git a/openbsc/tests/bsc-nat-trie/Makefile.am b/tests/bsc-nat-trie/Makefile.am similarity index 100% rename from openbsc/tests/bsc-nat-trie/Makefile.am rename to tests/bsc-nat-trie/Makefile.am diff --git a/openbsc/tests/bsc-nat-trie/bsc_nat_trie_test.c b/tests/bsc-nat-trie/bsc_nat_trie_test.c similarity index 100% rename from openbsc/tests/bsc-nat-trie/bsc_nat_trie_test.c rename to tests/bsc-nat-trie/bsc_nat_trie_test.c diff --git a/openbsc/tests/bsc-nat-trie/bsc_nat_trie_test.ok b/tests/bsc-nat-trie/bsc_nat_trie_test.ok similarity index 100% rename from openbsc/tests/bsc-nat-trie/bsc_nat_trie_test.ok rename to tests/bsc-nat-trie/bsc_nat_trie_test.ok diff --git a/openbsc/tests/bsc-nat-trie/prefixes.csv b/tests/bsc-nat-trie/prefixes.csv similarity index 100% rename from openbsc/tests/bsc-nat-trie/prefixes.csv rename to tests/bsc-nat-trie/prefixes.csv diff --git a/openbsc/tests/bsc-nat/Makefile.am b/tests/bsc-nat/Makefile.am similarity index 100% rename from openbsc/tests/bsc-nat/Makefile.am rename to tests/bsc-nat/Makefile.am diff --git a/openbsc/tests/bsc-nat/barr.cfg b/tests/bsc-nat/barr.cfg similarity index 100% rename from openbsc/tests/bsc-nat/barr.cfg rename to tests/bsc-nat/barr.cfg diff --git a/openbsc/tests/bsc-nat/barr_dup.cfg b/tests/bsc-nat/barr_dup.cfg similarity index 100% rename from openbsc/tests/bsc-nat/barr_dup.cfg rename to tests/bsc-nat/barr_dup.cfg diff --git a/openbsc/tests/bsc-nat/bsc_data.c b/tests/bsc-nat/bsc_data.c similarity index 100% rename from openbsc/tests/bsc-nat/bsc_data.c rename to tests/bsc-nat/bsc_data.c diff --git a/openbsc/tests/bsc-nat/bsc_nat_test.c b/tests/bsc-nat/bsc_nat_test.c similarity index 100% rename from openbsc/tests/bsc-nat/bsc_nat_test.c rename to tests/bsc-nat/bsc_nat_test.c diff --git a/openbsc/tests/bsc-nat/bsc_nat_test.ok b/tests/bsc-nat/bsc_nat_test.ok similarity index 100% rename from openbsc/tests/bsc-nat/bsc_nat_test.ok rename to tests/bsc-nat/bsc_nat_test.ok diff --git a/openbsc/tests/bsc-nat/prefixes.csv b/tests/bsc-nat/prefixes.csv similarity index 100% rename from openbsc/tests/bsc-nat/prefixes.csv rename to tests/bsc-nat/prefixes.csv diff --git a/openbsc/tests/bsc/Makefile.am b/tests/bsc/Makefile.am similarity index 100% rename from openbsc/tests/bsc/Makefile.am rename to tests/bsc/Makefile.am diff --git a/openbsc/tests/bsc/bsc_test.c b/tests/bsc/bsc_test.c similarity index 100% rename from openbsc/tests/bsc/bsc_test.c rename to tests/bsc/bsc_test.c diff --git a/openbsc/tests/bsc/bsc_test.ok b/tests/bsc/bsc_test.ok similarity index 100% rename from openbsc/tests/bsc/bsc_test.ok rename to tests/bsc/bsc_test.ok diff --git a/openbsc/tests/channel/Makefile.am b/tests/channel/Makefile.am similarity index 100% rename from openbsc/tests/channel/Makefile.am rename to tests/channel/Makefile.am diff --git a/openbsc/tests/channel/channel_test.c b/tests/channel/channel_test.c similarity index 100% rename from openbsc/tests/channel/channel_test.c rename to tests/channel/channel_test.c diff --git a/openbsc/tests/channel/channel_test.ok b/tests/channel/channel_test.ok similarity index 100% rename from openbsc/tests/channel/channel_test.ok rename to tests/channel/channel_test.ok diff --git a/openbsc/tests/ctrl_test_runner.py b/tests/ctrl_test_runner.py similarity index 100% rename from openbsc/tests/ctrl_test_runner.py rename to tests/ctrl_test_runner.py diff --git a/openbsc/tests/db/Makefile.am b/tests/db/Makefile.am similarity index 100% rename from openbsc/tests/db/Makefile.am rename to tests/db/Makefile.am diff --git a/openbsc/tests/db/db_test.c b/tests/db/db_test.c similarity index 100% rename from openbsc/tests/db/db_test.c rename to tests/db/db_test.c diff --git a/openbsc/tests/db/db_test.err b/tests/db/db_test.err similarity index 100% rename from openbsc/tests/db/db_test.err rename to tests/db/db_test.err diff --git a/openbsc/tests/db/db_test.ok b/tests/db/db_test.ok similarity index 100% rename from openbsc/tests/db/db_test.ok rename to tests/db/db_test.ok diff --git a/openbsc/tests/db/hlr.sqlite3 b/tests/db/hlr.sqlite3 similarity index 100% rename from openbsc/tests/db/hlr.sqlite3 rename to tests/db/hlr.sqlite3 diff --git a/openbsc/tests/gbproxy/Makefile.am b/tests/gbproxy/Makefile.am similarity index 100% rename from openbsc/tests/gbproxy/Makefile.am rename to tests/gbproxy/Makefile.am diff --git a/openbsc/tests/gbproxy/gbproxy_test.c b/tests/gbproxy/gbproxy_test.c similarity index 100% rename from openbsc/tests/gbproxy/gbproxy_test.c rename to tests/gbproxy/gbproxy_test.c diff --git a/openbsc/tests/gbproxy/gbproxy_test.ok b/tests/gbproxy/gbproxy_test.ok similarity index 100% rename from openbsc/tests/gbproxy/gbproxy_test.ok rename to tests/gbproxy/gbproxy_test.ok diff --git a/openbsc/tests/gprs/Makefile.am b/tests/gprs/Makefile.am similarity index 100% rename from openbsc/tests/gprs/Makefile.am rename to tests/gprs/Makefile.am diff --git a/openbsc/tests/gprs/gprs_test.c b/tests/gprs/gprs_test.c similarity index 100% rename from openbsc/tests/gprs/gprs_test.c rename to tests/gprs/gprs_test.c diff --git a/openbsc/tests/gprs/gprs_test.ok b/tests/gprs/gprs_test.ok similarity index 100% rename from openbsc/tests/gprs/gprs_test.ok rename to tests/gprs/gprs_test.ok diff --git a/openbsc/tests/gsm0408/Makefile.am b/tests/gsm0408/Makefile.am similarity index 100% rename from openbsc/tests/gsm0408/Makefile.am rename to tests/gsm0408/Makefile.am diff --git a/openbsc/tests/gsm0408/gsm0408_test.c b/tests/gsm0408/gsm0408_test.c similarity index 100% rename from openbsc/tests/gsm0408/gsm0408_test.c rename to tests/gsm0408/gsm0408_test.c diff --git a/openbsc/tests/gsm0408/gsm0408_test.ok b/tests/gsm0408/gsm0408_test.ok similarity index 100% rename from openbsc/tests/gsm0408/gsm0408_test.ok rename to tests/gsm0408/gsm0408_test.ok diff --git a/openbsc/tests/gtphub/Makefile.am b/tests/gtphub/Makefile.am similarity index 100% rename from openbsc/tests/gtphub/Makefile.am rename to tests/gtphub/Makefile.am diff --git a/openbsc/tests/gtphub/gtphub_test.c b/tests/gtphub/gtphub_test.c similarity index 100% rename from openbsc/tests/gtphub/gtphub_test.c rename to tests/gtphub/gtphub_test.c diff --git a/openbsc/tests/gtphub/gtphub_test.ok b/tests/gtphub/gtphub_test.ok similarity index 100% rename from openbsc/tests/gtphub/gtphub_test.ok rename to tests/gtphub/gtphub_test.ok diff --git a/openbsc/tests/mgcp/Makefile.am b/tests/mgcp/Makefile.am similarity index 100% rename from openbsc/tests/mgcp/Makefile.am rename to tests/mgcp/Makefile.am diff --git a/openbsc/tests/mgcp/mgcp_test.c b/tests/mgcp/mgcp_test.c similarity index 100% rename from openbsc/tests/mgcp/mgcp_test.c rename to tests/mgcp/mgcp_test.c diff --git a/openbsc/tests/mgcp/mgcp_test.ok b/tests/mgcp/mgcp_test.ok similarity index 100% rename from openbsc/tests/mgcp/mgcp_test.ok rename to tests/mgcp/mgcp_test.ok diff --git a/openbsc/tests/mgcp/mgcp_transcoding_test.c b/tests/mgcp/mgcp_transcoding_test.c similarity index 100% rename from openbsc/tests/mgcp/mgcp_transcoding_test.c rename to tests/mgcp/mgcp_transcoding_test.c diff --git a/openbsc/tests/mgcp/mgcp_transcoding_test.ok b/tests/mgcp/mgcp_transcoding_test.ok similarity index 100% rename from openbsc/tests/mgcp/mgcp_transcoding_test.ok rename to tests/mgcp/mgcp_transcoding_test.ok diff --git a/openbsc/tests/mm_auth/Makefile.am b/tests/mm_auth/Makefile.am similarity index 100% rename from openbsc/tests/mm_auth/Makefile.am rename to tests/mm_auth/Makefile.am diff --git a/openbsc/tests/mm_auth/mm_auth_test.c b/tests/mm_auth/mm_auth_test.c similarity index 100% rename from openbsc/tests/mm_auth/mm_auth_test.c rename to tests/mm_auth/mm_auth_test.c diff --git a/openbsc/tests/mm_auth/mm_auth_test.ok b/tests/mm_auth/mm_auth_test.ok similarity index 100% rename from openbsc/tests/mm_auth/mm_auth_test.ok rename to tests/mm_auth/mm_auth_test.ok diff --git a/openbsc/tests/nanobts_omlattr/Makefile.am b/tests/nanobts_omlattr/Makefile.am similarity index 100% rename from openbsc/tests/nanobts_omlattr/Makefile.am rename to tests/nanobts_omlattr/Makefile.am diff --git a/openbsc/tests/nanobts_omlattr/nanobts_omlattr_test.c b/tests/nanobts_omlattr/nanobts_omlattr_test.c similarity index 100% rename from openbsc/tests/nanobts_omlattr/nanobts_omlattr_test.c rename to tests/nanobts_omlattr/nanobts_omlattr_test.c diff --git a/openbsc/tests/nanobts_omlattr/nanobts_omlattr_test.ok b/tests/nanobts_omlattr/nanobts_omlattr_test.ok similarity index 100% rename from openbsc/tests/nanobts_omlattr/nanobts_omlattr_test.ok rename to tests/nanobts_omlattr/nanobts_omlattr_test.ok diff --git a/openbsc/tests/oap/Makefile.am b/tests/oap/Makefile.am similarity index 100% rename from openbsc/tests/oap/Makefile.am rename to tests/oap/Makefile.am diff --git a/openbsc/tests/oap/oap_client_test.c b/tests/oap/oap_client_test.c similarity index 100% rename from openbsc/tests/oap/oap_client_test.c rename to tests/oap/oap_client_test.c diff --git a/openbsc/tests/oap/oap_client_test.err b/tests/oap/oap_client_test.err similarity index 100% rename from openbsc/tests/oap/oap_client_test.err rename to tests/oap/oap_client_test.err diff --git a/openbsc/tests/oap/oap_client_test.ok b/tests/oap/oap_client_test.ok similarity index 100% rename from openbsc/tests/oap/oap_client_test.ok rename to tests/oap/oap_client_test.ok diff --git a/openbsc/tests/sgsn/Makefile.am b/tests/sgsn/Makefile.am similarity index 100% rename from openbsc/tests/sgsn/Makefile.am rename to tests/sgsn/Makefile.am diff --git a/openbsc/tests/sgsn/sgsn_test.c b/tests/sgsn/sgsn_test.c similarity index 100% rename from openbsc/tests/sgsn/sgsn_test.c rename to tests/sgsn/sgsn_test.c diff --git a/openbsc/tests/sgsn/sgsn_test.ok b/tests/sgsn/sgsn_test.ok similarity index 100% rename from openbsc/tests/sgsn/sgsn_test.ok rename to tests/sgsn/sgsn_test.ok diff --git a/openbsc/tests/slhc/Makefile.am b/tests/slhc/Makefile.am similarity index 100% rename from openbsc/tests/slhc/Makefile.am rename to tests/slhc/Makefile.am diff --git a/openbsc/tests/slhc/slhc_test.c b/tests/slhc/slhc_test.c similarity index 100% rename from openbsc/tests/slhc/slhc_test.c rename to tests/slhc/slhc_test.c diff --git a/openbsc/tests/slhc/slhc_test.ok b/tests/slhc/slhc_test.ok similarity index 100% rename from openbsc/tests/slhc/slhc_test.ok rename to tests/slhc/slhc_test.ok diff --git a/openbsc/tests/smpp/Makefile.am b/tests/smpp/Makefile.am similarity index 100% rename from openbsc/tests/smpp/Makefile.am rename to tests/smpp/Makefile.am diff --git a/openbsc/tests/smpp/smpp_test.c b/tests/smpp/smpp_test.c similarity index 100% rename from openbsc/tests/smpp/smpp_test.c rename to tests/smpp/smpp_test.c diff --git a/openbsc/tests/smpp/smpp_test.err b/tests/smpp/smpp_test.err similarity index 100% rename from openbsc/tests/smpp/smpp_test.err rename to tests/smpp/smpp_test.err diff --git a/openbsc/tests/smpp/smpp_test.ok b/tests/smpp/smpp_test.ok similarity index 100% rename from openbsc/tests/smpp/smpp_test.ok rename to tests/smpp/smpp_test.ok diff --git a/openbsc/tests/smpp_test_runner.py b/tests/smpp_test_runner.py similarity index 100% rename from openbsc/tests/smpp_test_runner.py rename to tests/smpp_test_runner.py diff --git a/openbsc/tests/sndcp_xid/Makefile.am b/tests/sndcp_xid/Makefile.am similarity index 100% rename from openbsc/tests/sndcp_xid/Makefile.am rename to tests/sndcp_xid/Makefile.am diff --git a/openbsc/tests/sndcp_xid/sndcp_xid_test.c b/tests/sndcp_xid/sndcp_xid_test.c similarity index 100% rename from openbsc/tests/sndcp_xid/sndcp_xid_test.c rename to tests/sndcp_xid/sndcp_xid_test.c diff --git a/openbsc/tests/sndcp_xid/sndcp_xid_test.ok b/tests/sndcp_xid/sndcp_xid_test.ok similarity index 100% rename from openbsc/tests/sndcp_xid/sndcp_xid_test.ok rename to tests/sndcp_xid/sndcp_xid_test.ok diff --git a/openbsc/tests/subscr/Makefile.am b/tests/subscr/Makefile.am similarity index 100% rename from openbsc/tests/subscr/Makefile.am rename to tests/subscr/Makefile.am diff --git a/openbsc/tests/subscr/bsc_subscr_test.c b/tests/subscr/bsc_subscr_test.c similarity index 100% rename from openbsc/tests/subscr/bsc_subscr_test.c rename to tests/subscr/bsc_subscr_test.c diff --git a/openbsc/tests/subscr/bsc_subscr_test.err b/tests/subscr/bsc_subscr_test.err similarity index 100% rename from openbsc/tests/subscr/bsc_subscr_test.err rename to tests/subscr/bsc_subscr_test.err diff --git a/openbsc/tests/subscr/bsc_subscr_test.ok b/tests/subscr/bsc_subscr_test.ok similarity index 100% rename from openbsc/tests/subscr/bsc_subscr_test.ok rename to tests/subscr/bsc_subscr_test.ok diff --git a/openbsc/tests/subscr/subscr_test.c b/tests/subscr/subscr_test.c similarity index 100% rename from openbsc/tests/subscr/subscr_test.c rename to tests/subscr/subscr_test.c diff --git a/openbsc/tests/subscr/subscr_test.ok b/tests/subscr/subscr_test.ok similarity index 100% rename from openbsc/tests/subscr/subscr_test.ok rename to tests/subscr/subscr_test.ok diff --git a/openbsc/tests/testsuite.at b/tests/testsuite.at similarity index 100% rename from openbsc/tests/testsuite.at rename to tests/testsuite.at diff --git a/openbsc/tests/trau/Makefile.am b/tests/trau/Makefile.am similarity index 100% rename from openbsc/tests/trau/Makefile.am rename to tests/trau/Makefile.am diff --git a/openbsc/tests/trau/trau_test.c b/tests/trau/trau_test.c similarity index 100% rename from openbsc/tests/trau/trau_test.c rename to tests/trau/trau_test.c diff --git a/openbsc/tests/trau/trau_test.ok b/tests/trau/trau_test.ok similarity index 100% rename from openbsc/tests/trau/trau_test.ok rename to tests/trau/trau_test.ok diff --git a/openbsc/tests/v42bis/Makefile.am b/tests/v42bis/Makefile.am similarity index 100% rename from openbsc/tests/v42bis/Makefile.am rename to tests/v42bis/Makefile.am diff --git a/openbsc/tests/v42bis/v42bis_test.c b/tests/v42bis/v42bis_test.c similarity index 100% rename from openbsc/tests/v42bis/v42bis_test.c rename to tests/v42bis/v42bis_test.c diff --git a/openbsc/tests/v42bis/v42bis_test.ok b/tests/v42bis/v42bis_test.ok similarity index 100% rename from openbsc/tests/v42bis/v42bis_test.ok rename to tests/v42bis/v42bis_test.ok diff --git a/openbsc/tests/vty_test_runner.py b/tests/vty_test_runner.py similarity index 100% rename from openbsc/tests/vty_test_runner.py rename to tests/vty_test_runner.py diff --git a/openbsc/tests/xid/Makefile.am b/tests/xid/Makefile.am similarity index 100% rename from openbsc/tests/xid/Makefile.am rename to tests/xid/Makefile.am diff --git a/openbsc/tests/xid/xid_test.c b/tests/xid/xid_test.c similarity index 100% rename from openbsc/tests/xid/xid_test.c rename to tests/xid/xid_test.c diff --git a/openbsc/tests/xid/xid_test.ok b/tests/xid/xid_test.ok similarity index 100% rename from openbsc/tests/xid/xid_test.ok rename to tests/xid/xid_test.ok diff --git a/openbsc/tools/hlrstat.pl b/tools/hlrstat.pl similarity index 100% rename from openbsc/tools/hlrstat.pl rename to tools/hlrstat.pl